@auto-engineer/narrative 0.12.1 → 0.13.1

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 (95) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/dist/src/commands/export-schema-runner.js +1 -1
  4. package/dist/src/commands/export-schema-runner.js.map +1 -1
  5. package/dist/src/fluent-builder.js +3 -3
  6. package/dist/src/fluent-builder.js.map +1 -1
  7. package/dist/src/getNarratives.specs.js +157 -161
  8. package/dist/src/getNarratives.specs.js.map +1 -1
  9. package/dist/src/id/addAutoIds.d.ts.map +1 -1
  10. package/dist/src/id/addAutoIds.js +23 -10
  11. package/dist/src/id/addAutoIds.js.map +1 -1
  12. package/dist/src/id/addAutoIds.specs.js +55 -46
  13. package/dist/src/id/addAutoIds.specs.js.map +1 -1
  14. package/dist/src/id/generators.js +1 -1
  15. package/dist/src/id/generators.js.map +1 -1
  16. package/dist/src/id/hasAllIds.d.ts.map +1 -1
  17. package/dist/src/id/hasAllIds.js +8 -3
  18. package/dist/src/id/hasAllIds.js.map +1 -1
  19. package/dist/src/id/hasAllIds.specs.js +142 -215
  20. package/dist/src/id/hasAllIds.specs.js.map +1 -1
  21. package/dist/src/index.d.ts +7 -8
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/index.js +3 -3
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/loader/graph.d.ts.map +1 -1
  26. package/dist/src/loader/graph.js +13 -6
  27. package/dist/src/loader/graph.js.map +1 -1
  28. package/dist/src/loader/ts-utils.d.ts +1 -0
  29. package/dist/src/loader/ts-utils.d.ts.map +1 -1
  30. package/dist/src/loader/ts-utils.js +95 -16
  31. package/dist/src/loader/ts-utils.js.map +1 -1
  32. package/dist/src/model-to-narrative.specs.js +548 -466
  33. package/dist/src/model-to-narrative.specs.js.map +1 -1
  34. package/dist/src/narrative-context.d.ts +8 -8
  35. package/dist/src/narrative-context.d.ts.map +1 -1
  36. package/dist/src/narrative-context.js +111 -301
  37. package/dist/src/narrative-context.js.map +1 -1
  38. package/dist/src/narrative-context.specs.js +15 -55
  39. package/dist/src/narrative-context.specs.js.map +1 -1
  40. package/dist/src/narrative.d.ts +19 -22
  41. package/dist/src/narrative.d.ts.map +1 -1
  42. package/dist/src/narrative.js +42 -71
  43. package/dist/src/narrative.js.map +1 -1
  44. package/dist/src/samples/questionnaires.narrative.js +10 -10
  45. package/dist/src/samples/questionnaires.narrative.js.map +1 -1
  46. package/dist/src/samples/test-with-ids.narrative.js +13 -29
  47. package/dist/src/samples/test-with-ids.narrative.js.map +1 -1
  48. package/dist/src/schema.d.ts +2704 -8293
  49. package/dist/src/schema.d.ts.map +1 -1
  50. package/dist/src/schema.js +26 -47
  51. package/dist/src/schema.js.map +1 -1
  52. package/dist/src/slice-builder.js +3 -3
  53. package/dist/src/slice-builder.js.map +1 -1
  54. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
  55. package/dist/src/transformers/model-to-narrative/generators/flow.js +118 -74
  56. package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
  57. package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts +9 -1
  58. package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts.map +1 -1
  59. package/dist/src/transformers/model-to-narrative/generators/gwt.js +112 -112
  60. package/dist/src/transformers/model-to-narrative/generators/gwt.js.map +1 -1
  61. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts +1 -1
  62. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts.map +1 -1
  63. package/dist/src/transformers/model-to-narrative/generators/imports.js +13 -9
  64. package/dist/src/transformers/model-to-narrative/generators/imports.js.map +1 -1
  65. package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
  66. package/dist/src/transformers/narrative-to-model/index.js +50 -23
  67. package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
  68. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +100 -90
  69. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +1 -1
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +5 -5
  72. package/src/commands/export-schema-runner.ts +3 -1
  73. package/src/fluent-builder.ts +3 -3
  74. package/src/getNarratives.specs.ts +176 -184
  75. package/src/id/addAutoIds.specs.ts +55 -48
  76. package/src/id/addAutoIds.ts +28 -11
  77. package/src/id/generators.ts +1 -1
  78. package/src/id/hasAllIds.specs.ts +147 -245
  79. package/src/id/hasAllIds.ts +11 -4
  80. package/src/index.ts +11 -12
  81. package/src/loader/graph.ts +23 -6
  82. package/src/loader/ts-utils.ts +169 -26
  83. package/src/model-to-narrative.specs.ts +548 -466
  84. package/src/narrative-context.specs.ts +73 -116
  85. package/src/narrative-context.ts +127 -374
  86. package/src/narrative.ts +70 -120
  87. package/src/samples/questionnaires.narrative.ts +10 -10
  88. package/src/samples/test-with-ids.narrative.ts +23 -31
  89. package/src/schema.ts +33 -52
  90. package/src/slice-builder.ts +3 -3
  91. package/src/transformers/model-to-narrative/generators/flow.ts +191 -85
  92. package/src/transformers/model-to-narrative/generators/gwt.ts +195 -178
  93. package/src/transformers/model-to-narrative/generators/imports.ts +13 -9
  94. package/src/transformers/narrative-to-model/index.ts +87 -26
  95. package/src/transformers/narrative-to-model/type-inference.specs.ts +100 -90
@@ -55,32 +55,36 @@ describe('getNarratives', (_mode) => {
55
55
  expect(createItemSlice.client.specs[0].children).toHaveLength(1);
56
56
  }
57
57
  expect(createItemSlice.server.specs).toBeDefined();
58
- const spec = createItemSlice.server.specs;
59
- expect(spec.name).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();
60
62
  expect(spec.rules).toHaveLength(1);
61
63
  const rule = spec.rules[0];
62
- expect(rule.description).toBeDefined();
64
+ expect(rule.name).toBeDefined();
63
65
  expect(rule.examples).toHaveLength(1);
64
66
  const example = rule.examples[0];
65
- expect(typeof example.when === 'object' && !Array.isArray(example.when)).toBe(true);
66
- if (typeof example.when === 'object' && !Array.isArray(example.when)) {
67
- if ('commandRef' in example.when) {
68
- expect(example.when.commandRef).toBe('CreateItem');
69
- }
70
- expect(example.when.exampleData).toMatchObject({
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({
71
76
  itemId: 'item_123',
72
77
  description: 'A new item',
73
78
  });
74
79
  }
75
- expect(example.then).toHaveLength(1);
76
- expect(example.then[0]).toMatchObject({
77
- eventRef: 'ItemCreated',
78
- exampleData: {
80
+ if (thenStep && 'text' in thenStep) {
81
+ expect(thenStep.text).toBe('ItemCreated');
82
+ expect(thenStep.docString).toMatchObject({
79
83
  id: 'item_123',
80
84
  description: 'A new item',
81
85
  addedAt: new Date('2024-01-15T10:00:00.000Z'),
82
- },
83
- });
86
+ });
87
+ }
84
88
  }
85
89
  const viewItemSlice = items.slices[1];
86
90
  expect(viewItemSlice.type).toBe('query');
@@ -102,7 +106,7 @@ describe('getNarratives', (_mode) => {
102
106
  expect(data[0].target).toMatchObject({ type: 'State', name: 'items' });
103
107
  expect(data[0].origin).toMatchObject({ name: 'ItemsProjection', type: 'projection' });
104
108
  const specs = viewItemSlice?.server?.specs;
105
- if (specs == null || specs.name === '')
109
+ if (specs == null || specs.length === 0 || specs[0].feature === '')
106
110
  throw new Error('No specs found in view items slice');
107
111
  expect(specs).toBeDefined();
108
112
  }
@@ -122,28 +126,32 @@ describe('getNarratives', (_mode) => {
122
126
  expect(submitOrderSlice.client.specs[0].children).toHaveLength(2);
123
127
  }
124
128
  expect(submitOrderSlice.server.specs).toBeDefined();
125
- const spec = submitOrderSlice.server.specs;
129
+ expect(Array.isArray(submitOrderSlice.server.specs)).toBe(true);
130
+ expect(submitOrderSlice.server.specs).toHaveLength(1);
131
+ const spec = submitOrderSlice.server.specs[0];
126
132
  expect(spec.rules).toHaveLength(1);
127
133
  const rule = spec.rules[0];
128
134
  expect(rule.examples).toHaveLength(1);
129
135
  const example = rule.examples[0];
130
- expect(typeof example.when === 'object' && !Array.isArray(example.when)).toBe(true);
131
- if (typeof example.when === 'object' && !Array.isArray(example.when)) {
132
- if ('commandRef' in example.when) {
133
- expect(example.when.commandRef).toBe('PlaceOrder');
134
- }
135
- expect(example.when.exampleData).toMatchObject({ productId: 'product_789', quantity: 3 });
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 });
136
145
  }
137
- expect(example.then).toHaveLength(1);
138
- expect(example.then[0]).toMatchObject({
139
- eventRef: 'OrderPlaced',
140
- exampleData: {
146
+ if (thenStep && 'text' in thenStep) {
147
+ expect(thenStep.text).toBe('OrderPlaced');
148
+ expect(thenStep.docString).toMatchObject({
141
149
  orderId: 'order_001',
142
150
  productId: 'product_789',
143
151
  quantity: 3,
144
152
  placedAt: new Date('2024-01-20T10:00:00.000Z'),
145
- },
146
- });
153
+ });
154
+ }
147
155
  }
148
156
  }
149
157
  const messages = schemas.messages;
@@ -197,16 +205,15 @@ describe('getNarratives', (_mode) => {
197
205
  if (slice.type === 'react') {
198
206
  expect(slice.server).toBeDefined();
199
207
  expect(slice.server.specs).toBeDefined();
200
- expect(typeof slice.server.specs === 'object' && !Array.isArray(slice.server.specs)).toBe(true);
201
- const spec = slice.server.specs;
208
+ expect(Array.isArray(slice.server.specs)).toBe(true);
209
+ expect(slice.server.specs.length).toBeGreaterThanOrEqual(1);
210
+ const spec = slice.server.specs[0];
202
211
  expect(spec.rules).toBeDefined();
203
212
  expect(Array.isArray(spec.rules)).toBe(true);
204
213
  spec.rules.forEach((rule) => {
205
214
  rule.examples.forEach((example) => {
206
- expect(example.when).toBeDefined();
207
- expect(Array.isArray(example.when)).toBe(true);
208
- expect(example.then).toBeDefined();
209
- expect(Array.isArray(example.then)).toBe(true);
215
+ expect(example.steps).toBeDefined();
216
+ expect(Array.isArray(example.steps)).toBe(true);
210
217
  });
211
218
  });
212
219
  }
@@ -259,10 +266,10 @@ describe('getNarratives', (_mode) => {
259
266
  const commandSlice = testFlowWithIds.slices.find((s) => s.name === 'Create test item');
260
267
  if (commandSlice?.type !== 'command')
261
268
  return;
262
- expect(commandSlice.server.specs.rules).toHaveLength(2);
263
- const rule1 = commandSlice.server.specs.rules.find((r) => r.description === 'Valid test items should be created successfully');
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');
264
271
  expect(rule1?.id).toBe('RULE-001');
265
- const rule2 = commandSlice.server.specs.rules.find((r) => r.description === 'Invalid test items should be rejected');
272
+ const rule2 = commandSlice.server.specs[0].rules.find((r) => r.name === 'Invalid test items should be rejected');
266
273
  expect(rule2?.id).toBe('RULE-002');
267
274
  });
268
275
  it('should have ids for query slice rules', async () => {
@@ -274,8 +281,8 @@ describe('getNarratives', (_mode) => {
274
281
  const querySlice = testFlowWithIds.slices.find((s) => s.name === 'Get test items');
275
282
  if (querySlice?.type !== 'query')
276
283
  return;
277
- expect(querySlice.server.specs.rules).toHaveLength(1);
278
- const rule3 = querySlice.server.specs.rules.find((r) => r.description === 'Items should be retrievable after creation');
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');
279
286
  expect(rule3?.id).toBe('RULE-003');
280
287
  });
281
288
  it('should have ids for react slice rules', async () => {
@@ -287,8 +294,8 @@ describe('getNarratives', (_mode) => {
287
294
  const reactSlice = testFlowWithIds.slices.find((s) => s.name === 'React to test event');
288
295
  if (reactSlice?.type !== 'react')
289
296
  return;
290
- expect(reactSlice.server.specs.rules).toHaveLength(1);
291
- const rule4 = reactSlice.server.specs.rules.find((r) => r.description === 'System should react to test item creation');
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');
292
299
  expect(rule4?.id).toBe('RULE-004');
293
300
  });
294
301
  it('should handle when examples correctly', async () => {
@@ -304,14 +311,10 @@ describe('getNarratives', (_mode) => {
304
311
  const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits the questionnaire');
305
312
  expect(submitSlice?.type).toBe('command');
306
313
  if (submitSlice?.type === 'command') {
307
- const example = submitSlice.server?.specs?.rules[0]?.examples[0];
308
- if (example !== null &&
309
- example !== undefined &&
310
- typeof example.when === 'object' &&
311
- example.when !== null &&
312
- !Array.isArray(example.when) &&
313
- 'commandRef' in example.when) {
314
- expect(example.when.commandRef).toBe('SubmitQuestionnaire');
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');
315
318
  }
316
319
  }
317
320
  }
@@ -332,7 +335,7 @@ describe('getNarratives', (_mode) => {
332
335
  import { flow, experience, it, specs } from '@auto-engineer/narrative';
333
336
 
334
337
  flow('Test Experience Flow', () => {
335
- experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {
338
+ experience('Homepage', 'H1a4Bn6Cy').client(() => {
336
339
  specs(() => {
337
340
  it('show a hero section with a welcome message');
338
341
  it('allow user to start the questionnaire');
@@ -402,8 +405,8 @@ flow('Browser Test Flow', () => {
402
405
  const flowContent = `
403
406
  import { flow, experience } from '@auto-engineer/narrative';
404
407
 
405
- flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
406
- experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {});
408
+ flow('Questionnaires', 'Q9m2Kp4Lx', () => {
409
+ experience('Homepage', 'H1a4Bn6Cy').client(() => {});
407
410
  });
408
411
  `;
409
412
  await memoryVfs.write('/browser/questionnaires.narrative.ts', new TextEncoder().encode(flowContent));
@@ -538,7 +541,7 @@ flow('questionnaires-test', () => {
538
541
  expect(querySlice?.type).toBe('query');
539
542
  if (querySlice?.type !== 'query')
540
543
  return;
541
- const example = querySlice.server.specs.rules[0]?.examples[0];
544
+ const example = querySlice.server.specs[0].rules[0]?.examples[0];
542
545
  expect(example).toBeDefined();
543
546
  if (example !== null && example !== undefined) {
544
547
  validateMixedGivenTypes(example);
@@ -677,39 +680,37 @@ flow('Todo List', () => {
677
680
  expect(summarySlice?.type).toBe('query');
678
681
  if (summarySlice?.type !== 'query')
679
682
  return;
680
- const example = summarySlice.server.specs.rules[0]?.examples[0];
683
+ const example = summarySlice.server.specs[0].rules[0]?.examples[0];
681
684
  expect(example).toBeDefined();
682
- expect(example.given).toBeDefined();
683
- expect(Array.isArray(example.given)).toBe(true);
684
- expect(example.given).toHaveLength(5);
685
- if (!example.given) {
686
- throw new Error('expected example.given to be defined');
687
- }
688
- validateGivenItemsHaveEventRef(example.given);
689
- validateTodoEventRefs(example.given);
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);
690
691
  validateTodoMessages(model);
691
692
  });
692
693
  });
693
- function validateGivenItemsHaveEventRef(given) {
694
- for (let i = 0; i < given.length; i++) {
695
- const givenItem = given[i];
696
- if (typeof givenItem === 'object' && givenItem !== null) {
697
- expect('eventRef' in givenItem).toBe(true);
698
- expect('stateRef' in givenItem).toBe(false);
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);
699
700
  }
700
701
  }
701
702
  }
702
- function expectEventRef(item, expectedType) {
703
- if (item !== null && item !== undefined && typeof item === 'object' && 'eventRef' in item) {
704
- expect(item.eventRef).toBe(expectedType);
703
+ function expectStepText(step, expectedType) {
704
+ if (step !== null && step !== undefined && typeof step === 'object' && 'text' in step) {
705
+ expect(step.text).toBe(expectedType);
705
706
  }
706
707
  }
707
- function validateTodoEventRefs(given) {
708
- expectEventRef(given[0], 'TodoAdded');
709
- expectEventRef(given[1], 'TodoAdded');
710
- expectEventRef(given[2], 'TodoAdded');
711
- expectEventRef(given[3], 'TodoMarkedInProgress');
712
- expectEventRef(given[4], 'TodoMarkedComplete');
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');
713
714
  }
714
715
  function validateTodoMessages(model) {
715
716
  const todoAddedEvent = model.messages.find((m) => m.name === 'TodoAdded');
@@ -729,14 +730,10 @@ function validateSubmitQuestionnaireCommand(questionnaireFlow) {
729
730
  const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits questionnaire');
730
731
  expect(submitSlice?.type).toBe('command');
731
732
  if (submitSlice?.type === 'command') {
732
- const example = submitSlice.server?.specs?.rules[0]?.examples[0];
733
- if (example !== null &&
734
- example !== undefined &&
735
- typeof example.when === 'object' &&
736
- example.when !== null &&
737
- !Array.isArray(example.when) &&
738
- 'commandRef' in example.when) {
739
- expect(example.when.commandRef).toBe('SubmitQuestionnaire');
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');
740
737
  }
741
738
  }
742
739
  }
@@ -747,15 +744,11 @@ function validateQuestionAnsweredEvent(model) {
747
744
  function validateGivenSectionEventRefs(questionnaireFlow) {
748
745
  const viewsSlice = questionnaireFlow.slices.find((s) => s.name === 'views progress');
749
746
  if (viewsSlice?.type === 'query') {
750
- const example = viewsSlice.server?.specs?.rules[0]?.examples[0];
751
- if (example?.given && Array.isArray(example.given) && example.given.length > 0) {
752
- const givenItem = example.given[0];
753
- if (typeof givenItem === 'object' && givenItem !== null) {
754
- expect('eventRef' in givenItem).toBe(true);
755
- expect('stateRef' in givenItem).toBe(false);
756
- if ('eventRef' in givenItem) {
757
- expect(givenItem.eventRef).toBe('QuestionAnswered');
758
- }
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');
759
752
  }
760
753
  }
761
754
  }
@@ -767,45 +760,43 @@ function validateCurrentQuestionIdType(model) {
767
760
  expect(currentQuestionIdField?.type).toBe('string | null');
768
761
  }
769
762
  function validateMixedGivenTypes(example) {
770
- expect(example.description).toBe('system with 2 items reaches max of 2');
771
- expect(example.given).toBeDefined();
772
- expect(Array.isArray(example.given)).toBe(true);
773
- if (!example.given)
774
- return;
775
- expect(example.given).toHaveLength(4);
776
- const firstGiven = example.given[0];
777
- expect('stateRef' in firstGiven).toBe(true);
778
- expect('eventRef' in firstGiven).toBe(false);
779
- if ('stateRef' in firstGiven) {
780
- expect(firstGiven.stateRef).toBe('ConfigState');
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');
781
772
  }
782
- const secondGiven = example.given[1];
783
- expect('eventRef' in secondGiven).toBe(true);
784
- if ('eventRef' in secondGiven) {
785
- expect(secondGiven.eventRef).toBe('SystemInitialized');
773
+ const secondGiven = givenSteps[1];
774
+ expect('text' in secondGiven).toBe(true);
775
+ if ('text' in secondGiven) {
776
+ expect(secondGiven.text).toBe('SystemInitialized');
786
777
  }
787
- const thirdGiven = example.given[2];
788
- expect('eventRef' in thirdGiven).toBe(true);
789
- if ('eventRef' in thirdGiven) {
790
- expect(thirdGiven.eventRef).toBe('ItemAdded');
778
+ const thirdGiven = givenSteps[2];
779
+ expect('text' in thirdGiven).toBe(true);
780
+ if ('text' in thirdGiven) {
781
+ expect(thirdGiven.text).toBe('ItemAdded');
791
782
  }
792
- const fourthGiven = example.given[3];
793
- expect('eventRef' in fourthGiven).toBe(true);
794
- if ('eventRef' in fourthGiven) {
795
- expect(fourthGiven.eventRef).toBe('ItemAdded');
783
+ const fourthGiven = givenSteps[3];
784
+ expect('text' in fourthGiven).toBe(true);
785
+ if ('text' in fourthGiven) {
786
+ expect(fourthGiven.text).toBe('ItemAdded');
796
787
  }
797
788
  }
798
789
  function validateEmptyWhenClause(example) {
799
- expect(example.when).toBeUndefined();
790
+ const whenStep = example.steps.find((s) => s.keyword === 'When');
791
+ expect(whenStep).toBeUndefined();
800
792
  }
801
793
  function validateThenClause(example) {
802
- expect(example.then).toBeDefined();
803
- expect(Array.isArray(example.then)).toBe(true);
804
- expect(example.then).toHaveLength(1);
805
- const thenOutcome = example.then[0];
806
- expect('stateRef' in thenOutcome).toBe(true);
807
- if ('stateRef' in thenOutcome) {
808
- expect(thenOutcome.stateRef).toBe('SystemStatus');
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');
809
800
  }
810
801
  }
811
802
  function validateMixedGivenTypeMessages(model) {
@@ -885,11 +876,11 @@ type SubmitQuestionnaire = Command<
885
876
  }
886
877
  >;
887
878
 
888
- flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
889
- command('sends the questionnaire link', 'AUTO-S2b5Cp7Dz')
879
+ flow('Questionnaires', 'Q9m2Kp4Lx', () => {
880
+ command('sends the questionnaire link', 'S2b5Cp7Dz')
890
881
  .server(() => {
891
882
  specs(() => {
892
- rule('questionnaire link is sent to participant', 'AUTO-r0A1Bo8X', () => {
883
+ rule('questionnaire link is sent to participant', 'r0A1Bo8X', () => {
893
884
  example('sends the questionnaire link successfully')
894
885
  .when<SendQuestionnaireLink>({
895
886
  questionnaireId: 'q-001',
@@ -919,10 +910,10 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
919
910
  });
920
911
  });
921
912
 
922
- command('submits the questionnaire', 'AUTO-T5k9Jw3V')
913
+ command('submits the questionnaire', 'T5k9Jw3V')
923
914
  .server(() => {
924
915
  specs(() => {
925
- rule('questionnaire allowed to be submitted when all questions are answered', 'AUTO-r4H0Lx4U', () => {
916
+ rule('questionnaire allowed to be submitted when all questions are answered', 'r4H0Lx4U', () => {
926
917
  example('submits the questionnaire successfully')
927
918
  .when<SubmitQuestionnaire>({
928
919
  questionnaireId: 'q-001',
@@ -976,32 +967,34 @@ function getSubmitSlice(questionnaireFlow) {
976
967
  return submitSlice;
977
968
  }
978
969
  function getSubmitExample(submitSlice) {
979
- const rule = submitSlice.server?.specs?.rules[0];
970
+ const rule = submitSlice.server?.specs?.[0]?.rules[0];
980
971
  expect(rule).toBeDefined();
981
972
  expect(rule?.examples).toHaveLength(1);
982
973
  const example = rule?.examples[0];
983
- expect(example?.description).toBe('submits the questionnaire successfully');
974
+ expect(example?.name).toBe('submits the questionnaire successfully');
984
975
  return example;
985
976
  }
986
977
  function validateSubmitCommandRef(example) {
987
978
  const ex = example;
988
- expect(ex?.when).toBeDefined();
989
- if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
990
- expect(ex.when.commandRef).toBe('SubmitQuestionnaire');
991
- expect(ex.when.commandRef).not.toBe('SendQuestionnaireLink');
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');
992
984
  }
993
985
  else {
994
- throw new Error('Expected when to have commandRef property');
986
+ throw new Error('Expected steps to have a When step with text property');
995
987
  }
996
988
  }
997
989
  function validateLinkSliceCommandRef(questionnaireFlow) {
998
990
  const linkSlice = questionnaireFlow.slices.find((s) => s.name === 'sends the questionnaire link');
999
991
  expect(linkSlice?.type).toBe('command');
1000
992
  if (linkSlice?.type === 'command') {
1001
- const linkExample = linkSlice.server?.specs?.rules[0]?.examples[0];
993
+ const linkExample = linkSlice.server?.specs?.[0]?.rules[0]?.examples[0];
1002
994
  const ex = linkExample;
1003
- if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
1004
- expect(ex.when.commandRef).toBe('SendQuestionnaireLink');
995
+ const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
996
+ if (whenStep && 'text' in whenStep) {
997
+ expect(whenStep.text).toBe('SendQuestionnaireLink');
1005
998
  }
1006
999
  }
1007
1000
  }
@@ -1027,17 +1020,19 @@ function getServerSpecsFromSlice(submitSlice) {
1027
1020
  const slice = submitSlice;
1028
1021
  const serverSpecs = slice.server?.specs;
1029
1022
  expect(serverSpecs).toBeDefined();
1030
- const specs = serverSpecs;
1031
- expect(specs?.rules).toBeDefined();
1032
- expect(specs?.rules).toHaveLength(1);
1033
- return serverSpecs;
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;
1034
1029
  }
1035
1030
  function getFirstRuleFromSpecs(serverSpecs) {
1036
1031
  const specs = serverSpecs;
1037
1032
  const rule = specs?.rules?.[0];
1038
1033
  expect(rule).toBeDefined();
1039
1034
  const r = rule;
1040
- expect(r?.description).toBe('questionnaire allowed to be submitted when all questions are answered');
1035
+ expect(r?.name).toBe('questionnaire allowed to be submitted when all questions are answered');
1041
1036
  expect(r?.examples).toBeDefined();
1042
1037
  expect(r?.examples).toHaveLength(1);
1043
1038
  return rule;
@@ -1047,34 +1042,35 @@ function getFirstExampleFromRule(rule) {
1047
1042
  const example = r?.examples?.[0];
1048
1043
  expect(example).toBeDefined();
1049
1044
  const ex = example;
1050
- expect(ex?.description).toBe('submits the questionnaire successfully');
1045
+ expect(ex?.name).toBe('submits the questionnaire successfully');
1051
1046
  return example;
1052
1047
  }
1053
1048
  function validateExampleCommandRef(example) {
1054
1049
  const ex = example;
1055
- expect(ex?.when).toBeDefined();
1056
- if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
1057
- expect(ex.when.commandRef).toBe('SubmitQuestionnaire');
1058
- expect(ex.when.commandRef).not.toBe('SendQuestionnaireLink');
1059
- expect(ex.when.exampleData).toEqual({
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({
1060
1057
  questionnaireId: 'q-001',
1061
1058
  participantId: 'participant-abc',
1062
1059
  });
1063
1060
  }
1064
1061
  else {
1065
- throw new Error('Expected when to have commandRef property');
1062
+ throw new Error('Expected when step to have text property');
1066
1063
  }
1067
1064
  }
1068
1065
  function validateThenEvents(example) {
1069
1066
  const ex = example;
1070
- expect(ex?.then).toBeDefined();
1071
- expect(Array.isArray(ex?.then)).toBe(true);
1072
- expect(ex?.then).toHaveLength(1);
1073
- const thenEvent = ex?.then?.[0];
1074
- if (thenEvent !== null && thenEvent !== undefined && 'eventRef' in thenEvent) {
1075
- const event = thenEvent;
1076
- expect(event.eventRef).toBe('QuestionnaireSubmitted');
1077
- expect(event.exampleData).toEqual({
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({
1078
1074
  questionnaireId: 'q-001',
1079
1075
  participantId: 'participant-abc',
1080
1076
  submittedAt: new Date('2030-01-01T09:00:00.000Z'),