@auto-engineer/narrative 0.13.0 → 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.
Files changed (89) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +22 -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 +149 -153
  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 +50 -12
  11. package/dist/src/id/addAutoIds.js.map +1 -1
  12. package/dist/src/id/addAutoIds.specs.js +396 -45
  13. package/dist/src/id/addAutoIds.specs.js.map +1 -1
  14. package/dist/src/id/hasAllIds.d.ts.map +1 -1
  15. package/dist/src/id/hasAllIds.js +28 -5
  16. package/dist/src/id/hasAllIds.js.map +1 -1
  17. package/dist/src/id/hasAllIds.specs.js +407 -214
  18. package/dist/src/id/hasAllIds.specs.js.map +1 -1
  19. package/dist/src/index.d.ts +6 -8
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +3 -3
  22. package/dist/src/index.js.map +1 -1
  23. package/dist/src/loader/graph.d.ts.map +1 -1
  24. package/dist/src/loader/graph.js +13 -6
  25. package/dist/src/loader/graph.js.map +1 -1
  26. package/dist/src/loader/ts-utils.d.ts +1 -0
  27. package/dist/src/loader/ts-utils.d.ts.map +1 -1
  28. package/dist/src/loader/ts-utils.js +95 -16
  29. package/dist/src/loader/ts-utils.js.map +1 -1
  30. package/dist/src/model-to-narrative.specs.js +531 -449
  31. package/dist/src/model-to-narrative.specs.js.map +1 -1
  32. package/dist/src/narrative-context.d.ts +8 -8
  33. package/dist/src/narrative-context.d.ts.map +1 -1
  34. package/dist/src/narrative-context.js +111 -301
  35. package/dist/src/narrative-context.js.map +1 -1
  36. package/dist/src/narrative-context.specs.js +15 -55
  37. package/dist/src/narrative-context.specs.js.map +1 -1
  38. package/dist/src/narrative.d.ts +19 -22
  39. package/dist/src/narrative.d.ts.map +1 -1
  40. package/dist/src/narrative.js +42 -71
  41. package/dist/src/narrative.js.map +1 -1
  42. package/dist/src/samples/test-with-ids.narrative.js +13 -29
  43. package/dist/src/samples/test-with-ids.narrative.js.map +1 -1
  44. package/dist/src/schema.d.ts +3205 -8287
  45. package/dist/src/schema.d.ts.map +1 -1
  46. package/dist/src/schema.js +29 -47
  47. package/dist/src/schema.js.map +1 -1
  48. package/dist/src/slice-builder.js +3 -3
  49. package/dist/src/slice-builder.js.map +1 -1
  50. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
  51. package/dist/src/transformers/model-to-narrative/generators/flow.js +118 -74
  52. package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
  53. package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts +9 -1
  54. package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts.map +1 -1
  55. package/dist/src/transformers/model-to-narrative/generators/gwt.js +112 -112
  56. package/dist/src/transformers/model-to-narrative/generators/gwt.js.map +1 -1
  57. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts +1 -1
  58. package/dist/src/transformers/model-to-narrative/generators/imports.d.ts.map +1 -1
  59. package/dist/src/transformers/model-to-narrative/generators/imports.js +13 -9
  60. package/dist/src/transformers/model-to-narrative/generators/imports.js.map +1 -1
  61. package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
  62. package/dist/src/transformers/narrative-to-model/index.js +50 -23
  63. package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
  64. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +100 -90
  65. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/package.json +5 -5
  68. package/src/commands/export-schema-runner.ts +3 -1
  69. package/src/fluent-builder.ts +3 -3
  70. package/src/getNarratives.specs.ts +168 -176
  71. package/src/id/addAutoIds.specs.ts +424 -47
  72. package/src/id/addAutoIds.ts +57 -13
  73. package/src/id/hasAllIds.specs.ts +400 -223
  74. package/src/id/hasAllIds.ts +32 -6
  75. package/src/index.ts +9 -12
  76. package/src/loader/graph.ts +23 -6
  77. package/src/loader/ts-utils.ts +169 -26
  78. package/src/model-to-narrative.specs.ts +531 -449
  79. package/src/narrative-context.specs.ts +73 -116
  80. package/src/narrative-context.ts +127 -374
  81. package/src/narrative.ts +70 -120
  82. package/src/samples/test-with-ids.narrative.ts +23 -31
  83. package/src/schema.ts +36 -52
  84. package/src/slice-builder.ts +3 -3
  85. package/src/transformers/model-to-narrative/generators/flow.ts +191 -85
  86. package/src/transformers/model-to-narrative/generators/gwt.ts +195 -178
  87. package/src/transformers/model-to-narrative/generators/imports.ts +13 -9
  88. package/src/transformers/narrative-to-model/index.ts +87 -26
  89. package/src/transformers/narrative-to-model/type-inference.specs.ts +100 -90
@@ -12,6 +12,7 @@ describe('modelToNarrative', () => {
12
12
  data,
13
13
  describe,
14
14
  example,
15
+ given,
15
16
  gql,
16
17
  it,
17
18
  narrative,
@@ -21,6 +22,8 @@ describe('modelToNarrative', () => {
21
22
  sink,
22
23
  source,
23
24
  specs,
25
+ then,
26
+ when,
24
27
  } from '@auto-engineer/narrative';
25
28
  import type { Command, Event, State } from '@auto-engineer/narrative';
26
29
  import { AI, ProductCatalog } from '../server/src/integrations';
@@ -117,34 +120,36 @@ narrative('Seasonal Assistant', () => {
117
120
  data([sink().event('ShoppingCriteriaEntered').toStream('shopping-session-\${sessionId}')]);
118
121
  specs('When shopper submits criteria, a shopping session is started', () => {
119
122
  rule('Valid criteria should start a shopping session', () => {
120
- example('User submits shopping criteria for children')
121
- .when<EnterShoppingCriteria>({
123
+ example('User submits shopping criteria for children', () => {
124
+ when<EnterShoppingCriteria>({
122
125
  sessionId: 'shopper-123',
123
126
  criteria:
124
127
  'I need back-to-school items for my 7-year-old daughter who loves soccer and crafts, and my 12-year-old son who is into computers and Magic the Gathering.',
125
- })
126
- .then<ShoppingCriteriaEntered>({
128
+ });
129
+ then<ShoppingCriteriaEntered>({
127
130
  sessionId: 'shopper-123',
128
131
  criteria:
129
132
  'I need back-to-school items for my 7-year-old daughter who loves soccer and crafts, and my 12-year-old son who is into computers and Magic the Gathering.',
130
133
  });
134
+ });
131
135
  });
132
136
  });
133
137
  });
134
138
  react('creates a chat session').server(() => {
135
139
  specs('When shopping criteria are entered, request wishlist creation', () => {
136
140
  rule('Shopping criteria should trigger item suggestion', () => {
137
- example('Criteria entered triggers wishlist creation')
138
- .when<ShoppingCriteriaEntered>({
141
+ example('Criteria entered triggers wishlist creation', () => {
142
+ when<ShoppingCriteriaEntered>({
139
143
  sessionId: 'session-abc',
140
144
  criteria:
141
145
  'I need back-to-school items for my 7-year-old daughter who loves soccer and crafts, and my 12-year-old son who is into computers and Magic the Gathering.',
142
- })
143
- .then<SuggestShoppingItems>({
146
+ });
147
+ then<SuggestShoppingItems>({
144
148
  sessionId: 'session-abc',
145
149
  prompt:
146
150
  'I need back-to-school items for my 7-year-old daughter who loves soccer and crafts, and my 12-year-old son who is into computers and Magic the Gathering.',
147
151
  });
152
+ });
148
153
  });
149
154
  });
150
155
  });
@@ -161,8 +166,8 @@ narrative('Seasonal Assistant', () => {
161
166
  ]);
162
167
  specs('When chat is triggered, AI suggests items based on product catalog', () => {
163
168
  rule('AI should suggest relevant items from available products', () => {
164
- example('Product catalog with matching items generates suggestions')
165
- .given<Products>({
169
+ example('Product catalog with matching items generates suggestions', () => {
170
+ given<Products>({
166
171
  products: [
167
172
  {
168
173
  productId: 'prod-soccer-ball',
@@ -197,13 +202,13 @@ narrative('Seasonal Assistant', () => {
197
202
  imageUrl: 'https://example.com/mtg-starter.jpg',
198
203
  },
199
204
  ],
200
- })
201
- .when<SuggestShoppingItems>({
205
+ });
206
+ when<SuggestShoppingItems>({
202
207
  sessionId: 'session-abc',
203
208
  prompt:
204
209
  'I need back-to-school items for my 7-year-old daughter who loves soccer and crafts, and my 12-year-old son who is into computers and Magic the Gathering.',
205
- })
206
- .then<ShoppingItemsSuggested>({
210
+ });
211
+ then<ShoppingItemsSuggested>({
207
212
  sessionId: 'session-abc',
208
213
  suggestedItems: [
209
214
  {
@@ -232,6 +237,7 @@ narrative('Seasonal Assistant', () => {
232
237
  },
233
238
  ],
234
239
  });
240
+ });
235
241
  });
236
242
  });
237
243
  });
@@ -260,8 +266,8 @@ narrative('Seasonal Assistant', () => {
260
266
  data([source().state('SuggestedItems').fromProjection('SuggestedItemsProjection', 'sessionId')]);
261
267
  specs('Suggested items are available for viewing', () => {
262
268
  rule('Items should be available for viewing after suggestion', () => {
263
- example('Item becomes available after AI suggestion event')
264
- .when<ShoppingItemsSuggested>({
269
+ example('Item becomes available after AI suggestion event', () => {
270
+ when<ShoppingItemsSuggested>({
265
271
  sessionId: 'session-abc',
266
272
  suggestedItems: [
267
273
  {
@@ -289,8 +295,8 @@ narrative('Seasonal Assistant', () => {
289
295
  reason: 'Ideal starter set for Magic the Gathering enthusiasts',
290
296
  },
291
297
  ],
292
- })
293
- .then<SuggestedItems>({
298
+ });
299
+ then<SuggestedItems>({
294
300
  sessionId: 'session-abc',
295
301
  items: [
296
302
  {
@@ -319,6 +325,7 @@ narrative('Seasonal Assistant', () => {
319
325
  },
320
326
  ],
321
327
  });
328
+ });
322
329
  });
323
330
  });
324
331
  });
@@ -334,8 +341,8 @@ narrative('Seasonal Assistant', () => {
334
341
  data([sink().event('ItemsAddedToCart').toStream('shopping-session-\${sessionId}')]);
335
342
  specs('When shopper accepts items, they are added to cart', () => {
336
343
  rule('Accepted items should be added to the shopping cart', () => {
337
- example('User selects all suggested items for cart')
338
- .when<AddItemsToCart>({
344
+ example('User selects all suggested items for cart', () => {
345
+ when<AddItemsToCart>({
339
346
  sessionId: 'session-abc',
340
347
  items: [
341
348
  { productId: 'prod-soccer-ball', quantity: 1 },
@@ -343,8 +350,8 @@ narrative('Seasonal Assistant', () => {
343
350
  { productId: 'prod-laptop-bag', quantity: 1 },
344
351
  { productId: 'prod-mtg-starter', quantity: 1 },
345
352
  ],
346
- })
347
- .then<ItemsAddedToCart>({
353
+ });
354
+ then<ItemsAddedToCart>({
348
355
  sessionId: 'session-abc',
349
356
  items: [
350
357
  { productId: 'prod-soccer-ball', quantity: 1 },
@@ -353,6 +360,7 @@ narrative('Seasonal Assistant', () => {
353
360
  { productId: 'prod-mtg-starter', quantity: 1 },
354
361
  ],
355
362
  });
363
+ });
356
364
  });
357
365
  });
358
366
  });
@@ -487,10 +495,13 @@ narrative('Test Flow without IDs', () => {
487
495
  },
488
496
  server: {
489
497
  description: 'Product query server',
490
- specs: {
491
- name: 'Product data specs',
492
- rules: [],
493
- },
498
+ specs: [
499
+ {
500
+ type: 'gherkin',
501
+ feature: 'Product data specs',
502
+ rules: [],
503
+ },
504
+ ],
494
505
  },
495
506
  },
496
507
  ],
@@ -541,30 +552,36 @@ narrative('Test Flow with IDs', 'FLOW-123', () => {
541
552
  },
542
553
  server: {
543
554
  description: 'Command processing server',
544
- specs: {
545
- name: 'Command Processing',
546
- rules: [
547
- {
548
- id: 'RULE-ABC',
549
- description: 'Valid commands should be processed',
550
- examples: [
551
- {
552
- description: 'User submits valid command',
553
- when: {
554
- commandRef: 'ProcessCommand',
555
- exampleData: { id: 'cmd-123', action: 'create' },
555
+ specs: [
556
+ {
557
+ type: 'gherkin',
558
+ feature: 'Command Processing',
559
+ rules: [
560
+ {
561
+ id: 'RULE-ABC',
562
+ name: 'Valid commands should be processed',
563
+ examples: [
564
+ {
565
+ id: 'EX-001',
566
+ name: 'User submits valid command',
567
+ steps: [
568
+ {
569
+ keyword: 'When',
570
+ text: 'ProcessCommand',
571
+ docString: { id: 'cmd-123', action: 'create' },
572
+ },
573
+ {
574
+ keyword: 'Then',
575
+ text: 'CommandProcessed',
576
+ docString: { id: 'cmd-123', status: 'success' },
577
+ },
578
+ ],
556
579
  },
557
- then: [
558
- {
559
- eventRef: 'CommandProcessed',
560
- exampleData: { id: 'cmd-123', status: 'success' },
561
- },
562
- ],
563
- },
564
- ],
565
- },
566
- ],
567
- },
580
+ ],
581
+ },
582
+ ],
583
+ },
584
+ ],
568
585
  },
569
586
  },
570
587
  ],
@@ -596,7 +613,8 @@ narrative('Test Flow with IDs', 'FLOW-123', () => {
596
613
 
597
614
  const code = await modelToNarrative(modelWithRuleIds);
598
615
 
599
- expect(code).toEqual(`import { command, example, narrative, rule, specs } from '@auto-engineer/narrative';
616
+ expect(code)
617
+ .toEqual(`import { command, example, narrative, rule, specs, then, when } from '@auto-engineer/narrative';
600
618
  import type { Command, Event } from '@auto-engineer/narrative';
601
619
  type ProcessCommand = Command<
602
620
  'ProcessCommand',
@@ -616,9 +634,10 @@ narrative('Test Flow with Rule IDs', 'FLOW-456', () => {
616
634
  command('process command', 'SLICE-789').server(() => {
617
635
  specs('Command Processing', () => {
618
636
  rule('Valid commands should be processed', 'RULE-ABC', () => {
619
- example('User submits valid command')
620
- .when<ProcessCommand>({ id: 'cmd-123', action: 'create' })
621
- .then<CommandProcessed>({ id: 'cmd-123', status: 'success' });
637
+ example('User submits valid command', () => {
638
+ when<ProcessCommand>({ id: 'cmd-123', action: 'create' });
639
+ then<CommandProcessed>({ id: 'cmd-123', status: 'success' });
640
+ });
622
641
  });
623
642
  });
624
643
  });
@@ -747,59 +766,64 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
747
766
  },
748
767
  },
749
768
  ],
750
- specs: {
751
- name: '',
752
- rules: [
753
- {
754
- id: 'r1A3Bp9W',
755
- description: 'questionnaires show current progress',
756
- examples: [
757
- {
758
- description: 'a question has already been answered',
759
- given: [
760
- {
761
- eventRef: 'QuestionnaireLinkSent',
762
- exampleData: {
763
- questionnaireId: 'q-001',
764
- participantId: 'participant-abc',
765
- link: 'https://app.example.com/q/q-001?participant=participant-abc',
766
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
769
+ specs: [
770
+ {
771
+ type: 'gherkin',
772
+ feature: '',
773
+ rules: [
774
+ {
775
+ id: 'r1A3Bp9W',
776
+ name: 'questionnaires show current progress',
777
+ examples: [
778
+ {
779
+ id: 'EX-001',
780
+ name: 'a question has already been answered',
781
+ steps: [
782
+ {
783
+ keyword: 'Given',
784
+ text: 'QuestionnaireLinkSent',
785
+ docString: {
786
+ questionnaireId: 'q-001',
787
+ participantId: 'participant-abc',
788
+ link: 'https://app.example.com/q/q-001?participant=participant-abc',
789
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
790
+ },
767
791
  },
768
- },
769
- ],
770
- when: {
771
- exampleData: {
772
- questionnaireId: 'q-001',
773
- participantId: 'participant-abc',
774
- questionId: 'q1',
775
- answer: 'Yes',
776
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
777
- },
778
- eventRef: 'QuestionAnswered',
779
- },
780
- then: [
781
- {
782
- stateRef: 'QuestionnaireProgress',
783
- exampleData: {
784
- questionnaireId: 'q-001',
785
- participantId: 'participant-abc',
786
- status: 'in_progress',
787
- currentQuestionId: 'q2',
788
- remainingQuestions: ['q2', 'q3'],
789
- answers: [
790
- {
791
- questionId: 'q1',
792
- value: 'Yes',
793
- },
794
- ],
792
+ {
793
+ keyword: 'When',
794
+ text: 'QuestionAnswered',
795
+ docString: {
796
+ questionnaireId: 'q-001',
797
+ participantId: 'participant-abc',
798
+ questionId: 'q1',
799
+ answer: 'Yes',
800
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
801
+ },
795
802
  },
796
- },
797
- ],
798
- },
799
- ],
800
- },
801
- ],
802
- },
803
+ {
804
+ keyword: 'Then',
805
+ text: 'QuestionnaireProgress',
806
+ docString: {
807
+ questionnaireId: 'q-001',
808
+ participantId: 'participant-abc',
809
+ status: 'in_progress',
810
+ currentQuestionId: 'q2',
811
+ remainingQuestions: ['q2', 'q3'],
812
+ answers: [
813
+ {
814
+ questionId: 'q1',
815
+ value: 'Yes',
816
+ },
817
+ ],
818
+ },
819
+ },
820
+ ],
821
+ },
822
+ ],
823
+ },
824
+ ],
825
+ },
826
+ ],
803
827
  },
804
828
  },
805
829
  ],
@@ -921,6 +945,7 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
921
945
  describe,
922
946
  example,
923
947
  experience,
948
+ given,
924
949
  gql,
925
950
  it,
926
951
  narrative,
@@ -928,6 +953,8 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
928
953
  rule,
929
954
  source,
930
955
  specs,
956
+ then,
957
+ when,
931
958
  } from '@auto-engineer/narrative';
932
959
  import type { Event, State } from '@auto-engineer/narrative';
933
960
  type QuestionnaireLinkSent = Event<
@@ -996,21 +1023,21 @@ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
996
1023
  data([source().state('QuestionnaireProgress').fromProjection('Questionnaires', 'questionnaire-participantId')]);
997
1024
  specs(() => {
998
1025
  rule('questionnaires show current progress', 'r1A3Bp9W', () => {
999
- example('a question has already been answered')
1000
- .given<QuestionnaireLinkSent>({
1026
+ example('a question has already been answered', () => {
1027
+ given<QuestionnaireLinkSent>({
1001
1028
  questionnaireId: 'q-001',
1002
1029
  participantId: 'participant-abc',
1003
1030
  link: 'https://app.example.com/q/q-001?participant=participant-abc',
1004
1031
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
1005
- })
1006
- .when<QuestionAnswered>({
1032
+ });
1033
+ when<QuestionAnswered>({
1007
1034
  questionnaireId: 'q-001',
1008
1035
  participantId: 'participant-abc',
1009
1036
  questionId: 'q1',
1010
1037
  answer: 'Yes',
1011
1038
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
1012
- })
1013
- .then<QuestionnaireProgress>({
1039
+ });
1040
+ then<QuestionnaireProgress>({
1014
1041
  questionnaireId: 'q-001',
1015
1042
  participantId: 'participant-abc',
1016
1043
  status: 'in_progress',
@@ -1018,6 +1045,7 @@ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
1018
1045
  remainingQuestions: ['q2', 'q3'],
1019
1046
  answers: [{ questionId: 'q1', value: 'Yes' }],
1020
1047
  });
1048
+ });
1021
1049
  });
1022
1050
  });
1023
1051
  });
@@ -1042,71 +1070,78 @@ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
1042
1070
  },
1043
1071
  server: {
1044
1072
  description: 'Test server for duplicate rules',
1045
- specs: {
1046
- name: 'Test Rules',
1047
- rules: [
1048
- {
1049
- id: 'r1A3Bp9W',
1050
- description: 'questionnaires show current progress',
1051
- examples: [
1052
- {
1053
- description: 'a question has already been answered',
1054
- given: [
1055
- {
1056
- eventRef: 'QuestionnaireLinkSent',
1057
- exampleData: {
1058
- questionnaireId: 'q-001',
1059
- participantId: 'participant-abc',
1073
+ specs: [
1074
+ {
1075
+ type: 'gherkin',
1076
+ feature: 'Test Rules',
1077
+ rules: [
1078
+ {
1079
+ id: 'r1A3Bp9W',
1080
+ name: 'questionnaires show current progress',
1081
+ examples: [
1082
+ {
1083
+ id: 'EX-001',
1084
+ name: 'a question has already been answered',
1085
+ steps: [
1086
+ {
1087
+ keyword: 'Given',
1088
+ text: 'QuestionnaireLinkSent',
1089
+ docString: {
1090
+ questionnaireId: 'q-001',
1091
+ participantId: 'participant-abc',
1092
+ },
1060
1093
  },
1061
- },
1062
- ],
1063
- when: {
1064
- eventRef: 'QuestionAnswered',
1065
- exampleData: {
1066
- questionnaireId: 'q-001',
1067
- questionId: 'q1',
1068
- answer: 'Yes',
1069
- },
1070
- },
1071
- then: [
1072
- {
1073
- stateRef: 'QuestionnaireProgress',
1074
- exampleData: {
1075
- questionnaireId: 'q-001',
1076
- status: 'in_progress',
1094
+ {
1095
+ keyword: 'When',
1096
+ text: 'QuestionAnswered',
1097
+ docString: {
1098
+ questionnaireId: 'q-001',
1099
+ questionId: 'q1',
1100
+ answer: 'Yes',
1101
+ },
1077
1102
  },
1078
- },
1079
- ],
1080
- },
1081
- {
1082
- description: 'no questions have been answered yet',
1083
- given: [
1084
- {
1085
- eventRef: 'QuestionnaireLinkSent',
1086
- exampleData: {
1087
- questionnaireId: 'q-001',
1088
- participantId: 'participant-abc',
1103
+ {
1104
+ keyword: 'Then',
1105
+ text: 'QuestionnaireProgress',
1106
+ docString: {
1107
+ questionnaireId: 'q-001',
1108
+ status: 'in_progress',
1109
+ },
1089
1110
  },
1090
- },
1091
- ],
1092
- when: {
1093
- eventRef: 'QuestionnaireLinkSent',
1094
- exampleData: {},
1111
+ ],
1095
1112
  },
1096
- then: [
1097
- {
1098
- stateRef: 'QuestionnaireProgress',
1099
- exampleData: {
1100
- questionnaireId: 'q-001',
1101
- status: 'in_progress',
1113
+ {
1114
+ id: 'EX-002',
1115
+ name: 'no questions have been answered yet',
1116
+ steps: [
1117
+ {
1118
+ keyword: 'Given',
1119
+ text: 'QuestionnaireLinkSent',
1120
+ docString: {
1121
+ questionnaireId: 'q-001',
1122
+ participantId: 'participant-abc',
1123
+ },
1102
1124
  },
1103
- },
1104
- ],
1105
- },
1106
- ],
1107
- },
1108
- ],
1109
- },
1125
+ {
1126
+ keyword: 'When',
1127
+ text: 'QuestionnaireLinkSent',
1128
+ docString: {},
1129
+ },
1130
+ {
1131
+ keyword: 'Then',
1132
+ text: 'QuestionnaireProgress',
1133
+ docString: {
1134
+ questionnaireId: 'q-001',
1135
+ status: 'in_progress',
1136
+ },
1137
+ },
1138
+ ],
1139
+ },
1140
+ ],
1141
+ },
1142
+ ],
1143
+ },
1144
+ ],
1110
1145
  },
1111
1146
  },
1112
1147
  ],
@@ -1149,7 +1184,8 @@ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
1149
1184
 
1150
1185
  const code = await modelToNarrative(modelWithDuplicateRules);
1151
1186
 
1152
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1187
+ expect(code)
1188
+ .toEqual(`import { example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1153
1189
  import type { Event, State } from '@auto-engineer/narrative';
1154
1190
  type QuestionnaireLinkSent = Event<
1155
1191
  'QuestionnaireLinkSent',
@@ -1177,14 +1213,15 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1177
1213
  query('test slice', 'TEST-SLICE').server(() => {
1178
1214
  specs('Test Rules', () => {
1179
1215
  rule('questionnaires show current progress', 'r1A3Bp9W', () => {
1180
- example('a question has already been answered')
1181
- .given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' })
1182
- .when<QuestionAnswered>({ questionnaireId: 'q-001', questionId: 'q1', answer: 'Yes' })
1183
- .then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1184
- example('no questions have been answered yet')
1185
- .given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' })
1186
- .when<QuestionnaireLinkSent>({})
1187
- .then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1216
+ example('a question has already been answered', () => {
1217
+ given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' });
1218
+ when<QuestionAnswered>({ questionnaireId: 'q-001', questionId: 'q1', answer: 'Yes' });
1219
+ then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1220
+ });
1221
+ example('no questions have been answered yet', () => {
1222
+ given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' });
1223
+ then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1224
+ });
1188
1225
  });
1189
1226
  });
1190
1227
  });
@@ -1192,7 +1229,7 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1192
1229
  `);
1193
1230
  });
1194
1231
 
1195
- it('should chain multiple given examples with .and() syntax', async () => {
1232
+ it('should chain multiple given examples with and() syntax', async () => {
1196
1233
  const modelWithMultiGiven: Model = {
1197
1234
  variant: 'specs',
1198
1235
  narratives: [
@@ -1209,84 +1246,92 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1209
1246
  },
1210
1247
  server: {
1211
1248
  description: 'Multi given server rules',
1212
- specs: {
1213
- name: 'Multi Given Rules',
1214
- rules: [
1215
- {
1216
- id: 'MultiGiven',
1217
- description: 'all questions have been answered',
1218
- examples: [
1219
- {
1220
- description: 'questionnaire with multiple events',
1221
- given: [
1222
- {
1223
- stateRef: 'QuestionnaireConfig',
1224
- exampleData: {
1225
- questionnaireId: 'q-001',
1226
- numberOfQuestions: 3,
1249
+ specs: [
1250
+ {
1251
+ type: 'gherkin',
1252
+ feature: 'Multi Given Rules',
1253
+ rules: [
1254
+ {
1255
+ id: 'MultiGiven',
1256
+ name: 'all questions have been answered',
1257
+ examples: [
1258
+ {
1259
+ id: 'EX-001',
1260
+ name: 'questionnaire with multiple events',
1261
+ steps: [
1262
+ {
1263
+ keyword: 'Given',
1264
+ text: 'QuestionnaireConfig',
1265
+ docString: {
1266
+ questionnaireId: 'q-001',
1267
+ numberOfQuestions: 3,
1268
+ },
1227
1269
  },
1228
- },
1229
- {
1230
- eventRef: 'QuestionnaireLinkSent',
1231
- exampleData: {
1232
- questionnaireId: 'q-001',
1233
- participantId: 'participant-abc',
1234
- link: 'https://example.com/q/q-001',
1235
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
1270
+ {
1271
+ keyword: 'And',
1272
+ text: 'QuestionnaireLinkSent',
1273
+ docString: {
1274
+ questionnaireId: 'q-001',
1275
+ participantId: 'participant-abc',
1276
+ link: 'https://example.com/q/q-001',
1277
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
1278
+ },
1236
1279
  },
1237
- },
1238
- {
1239
- eventRef: 'QuestionAnswered',
1240
- exampleData: {
1241
- questionnaireId: 'q-001',
1242
- participantId: 'participant-abc',
1243
- questionId: 'q1',
1244
- answer: 'Yes',
1245
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
1280
+ {
1281
+ keyword: 'And',
1282
+ text: 'QuestionAnswered',
1283
+ docString: {
1284
+ questionnaireId: 'q-001',
1285
+ participantId: 'participant-abc',
1286
+ questionId: 'q1',
1287
+ answer: 'Yes',
1288
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
1289
+ },
1246
1290
  },
1247
- },
1248
- {
1249
- eventRef: 'QuestionAnswered',
1250
- exampleData: {
1251
- questionnaireId: 'q-001',
1252
- participantId: 'participant-abc',
1253
- questionId: 'q2',
1254
- answer: 'No',
1255
- savedAt: new Date('2030-01-01T09:10:00.000Z'),
1291
+ {
1292
+ keyword: 'And',
1293
+ text: 'QuestionAnswered',
1294
+ docString: {
1295
+ questionnaireId: 'q-001',
1296
+ participantId: 'participant-abc',
1297
+ questionId: 'q2',
1298
+ answer: 'No',
1299
+ savedAt: new Date('2030-01-01T09:10:00.000Z'),
1300
+ },
1256
1301
  },
1257
- },
1258
- ],
1259
- when: {
1260
- eventRef: 'QuestionAnswered',
1261
- exampleData: {
1262
- questionnaireId: 'q-001',
1263
- participantId: 'participant-abc',
1264
- questionId: 'q3',
1265
- answer: 'Maybe',
1266
- savedAt: new Date('2030-01-01T09:15:00.000Z'),
1267
- },
1268
- },
1269
- then: [
1270
- {
1271
- stateRef: 'QuestionnaireProgress',
1272
- exampleData: {
1273
- questionnaireId: 'q-001',
1274
- participantId: 'participant-abc',
1275
- status: 'ready_to_submit',
1276
- currentQuestionId: null,
1277
- remainingQuestions: [],
1278
- answers: [
1279
- { questionId: 'q1', value: 'Yes' },
1280
- { questionId: 'q2', value: 'No' },
1281
- ],
1302
+ {
1303
+ keyword: 'When',
1304
+ text: 'QuestionAnswered',
1305
+ docString: {
1306
+ questionnaireId: 'q-001',
1307
+ participantId: 'participant-abc',
1308
+ questionId: 'q3',
1309
+ answer: 'Maybe',
1310
+ savedAt: new Date('2030-01-01T09:15:00.000Z'),
1311
+ },
1282
1312
  },
1283
- },
1284
- ],
1285
- },
1286
- ],
1287
- },
1288
- ],
1289
- },
1313
+ {
1314
+ keyword: 'Then',
1315
+ text: 'QuestionnaireProgress',
1316
+ docString: {
1317
+ questionnaireId: 'q-001',
1318
+ participantId: 'participant-abc',
1319
+ status: 'ready_to_submit',
1320
+ currentQuestionId: null,
1321
+ remainingQuestions: [],
1322
+ answers: [
1323
+ { questionId: 'q1', value: 'Yes' },
1324
+ { questionId: 'q2', value: 'No' },
1325
+ ],
1326
+ },
1327
+ },
1328
+ ],
1329
+ },
1330
+ ],
1331
+ },
1332
+ ],
1333
+ },
1334
+ ],
1290
1335
  },
1291
1336
  },
1292
1337
  ],
@@ -1346,7 +1391,8 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1346
1391
 
1347
1392
  const code = await modelToNarrative(modelWithMultiGiven);
1348
1393
 
1349
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1394
+ expect(code)
1395
+ .toEqual(`import { and, example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1350
1396
  import type { Event, State } from '@auto-engineer/narrative';
1351
1397
  type QuestionnaireConfig = State<
1352
1398
  'QuestionnaireConfig',
@@ -1392,36 +1438,36 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1392
1438
  query('multi given slice', 'MULTI-SLICE').server(() => {
1393
1439
  specs('Multi Given Rules', () => {
1394
1440
  rule('all questions have been answered', 'MultiGiven', () => {
1395
- example('questionnaire with multiple events')
1396
- .given<QuestionnaireConfig>({ questionnaireId: 'q-001', numberOfQuestions: 3 })
1397
- .and<QuestionnaireLinkSent>({
1441
+ example('questionnaire with multiple events', () => {
1442
+ given<QuestionnaireConfig>({ questionnaireId: 'q-001', numberOfQuestions: 3 });
1443
+ and<QuestionnaireLinkSent>({
1398
1444
  questionnaireId: 'q-001',
1399
1445
  participantId: 'participant-abc',
1400
1446
  link: 'https://example.com/q/q-001',
1401
1447
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
1402
- })
1403
- .and<QuestionAnswered>({
1448
+ });
1449
+ and<QuestionAnswered>({
1404
1450
  questionnaireId: 'q-001',
1405
1451
  participantId: 'participant-abc',
1406
1452
  questionId: 'q1',
1407
1453
  answer: 'Yes',
1408
1454
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
1409
- })
1410
- .and<QuestionAnswered>({
1455
+ });
1456
+ and<QuestionAnswered>({
1411
1457
  questionnaireId: 'q-001',
1412
1458
  participantId: 'participant-abc',
1413
1459
  questionId: 'q2',
1414
1460
  answer: 'No',
1415
1461
  savedAt: new Date('2030-01-01T09:10:00.000Z'),
1416
- })
1417
- .when<QuestionAnswered>({
1462
+ });
1463
+ when<QuestionAnswered>({
1418
1464
  questionnaireId: 'q-001',
1419
1465
  participantId: 'participant-abc',
1420
1466
  questionId: 'q3',
1421
1467
  answer: 'Maybe',
1422
1468
  savedAt: new Date('2030-01-01T09:15:00.000Z'),
1423
- })
1424
- .then<QuestionnaireProgress>({
1469
+ });
1470
+ then<QuestionnaireProgress>({
1425
1471
  questionnaireId: 'q-001',
1426
1472
  participantId: 'participant-abc',
1427
1473
  status: 'ready_to_submit',
@@ -1432,6 +1478,7 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1432
1478
  { questionId: 'q2', value: 'No' },
1433
1479
  ],
1434
1480
  });
1481
+ });
1435
1482
  });
1436
1483
  });
1437
1484
  });
@@ -1480,45 +1527,50 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1480
1527
  },
1481
1528
  },
1482
1529
  ],
1483
- specs: {
1484
- name: 'Database State Rules',
1485
- rules: [
1486
- {
1487
- id: 'RefState',
1488
- description: 'questionnaire config is available when referenced',
1489
- examples: [
1490
- {
1491
- description: 'config from database is accessible',
1492
- given: [
1493
- {
1494
- stateRef: 'QuestionnaireConfig',
1495
- exampleData: {
1496
- questionnaireId: 'q-001',
1497
- numberOfQuestions: 5,
1498
- title: 'Customer Satisfaction Survey',
1530
+ specs: [
1531
+ {
1532
+ type: 'gherkin',
1533
+ feature: 'Database State Rules',
1534
+ rules: [
1535
+ {
1536
+ id: 'RefState',
1537
+ name: 'questionnaire config is available when referenced',
1538
+ examples: [
1539
+ {
1540
+ id: 'EX-001',
1541
+ name: 'config from database is accessible',
1542
+ steps: [
1543
+ {
1544
+ keyword: 'Given',
1545
+ text: 'QuestionnaireConfig',
1546
+ docString: {
1547
+ questionnaireId: 'q-001',
1548
+ numberOfQuestions: 5,
1549
+ title: 'Customer Satisfaction Survey',
1550
+ },
1499
1551
  },
1500
- },
1501
- ],
1502
- when: {
1503
- eventRef: 'QuestionnaireProgress',
1504
- exampleData: {},
1505
- },
1506
- then: [
1507
- {
1508
- stateRef: 'QuestionnaireProgress',
1509
- exampleData: {
1510
- questionnaireId: 'q-001',
1511
- participantId: 'participant-abc',
1512
- status: 'in_progress',
1513
- totalQuestions: 5,
1552
+ {
1553
+ keyword: 'When',
1554
+ text: 'QuestionnaireProgress',
1555
+ docString: {},
1514
1556
  },
1515
- },
1516
- ],
1517
- },
1518
- ],
1519
- },
1520
- ],
1521
- },
1557
+ {
1558
+ keyword: 'Then',
1559
+ text: 'QuestionnaireProgress',
1560
+ docString: {
1561
+ questionnaireId: 'q-001',
1562
+ participantId: 'participant-abc',
1563
+ status: 'in_progress',
1564
+ totalQuestions: 5,
1565
+ },
1566
+ },
1567
+ ],
1568
+ },
1569
+ ],
1570
+ },
1571
+ ],
1572
+ },
1573
+ ],
1522
1574
  },
1523
1575
  },
1524
1576
  ],
@@ -1553,7 +1605,7 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1553
1605
  const code = await modelToNarrative(modelWithReferencedStates);
1554
1606
 
1555
1607
  expect(code)
1556
- .toEqual(`import { data, example, narrative, query, rule, source, specs } from '@auto-engineer/narrative';
1608
+ .toEqual(`import { data, example, given, narrative, query, rule, source, specs, then } from '@auto-engineer/narrative';
1557
1609
  import type { State } from '@auto-engineer/narrative';
1558
1610
  type QuestionnaireProgress = State<
1559
1611
  'QuestionnaireProgress',
@@ -1580,19 +1632,19 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1580
1632
  ]);
1581
1633
  specs('Database State Rules', () => {
1582
1634
  rule('questionnaire config is available when referenced', 'RefState', () => {
1583
- example('config from database is accessible')
1584
- .given<QuestionnaireConfig>({
1635
+ example('config from database is accessible', () => {
1636
+ given<QuestionnaireConfig>({
1585
1637
  questionnaireId: 'q-001',
1586
1638
  numberOfQuestions: 5,
1587
1639
  title: 'Customer Satisfaction Survey',
1588
- })
1589
- .when<QuestionnaireProgress>({})
1590
- .then<QuestionnaireProgress>({
1640
+ });
1641
+ then<QuestionnaireProgress>({
1591
1642
  questionnaireId: 'q-001',
1592
1643
  participantId: 'participant-abc',
1593
1644
  status: 'in_progress',
1594
1645
  totalQuestions: 5,
1595
1646
  });
1647
+ });
1596
1648
  });
1597
1649
  });
1598
1650
  });
@@ -1617,48 +1669,53 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1617
1669
  },
1618
1670
  server: {
1619
1671
  description: 'Date server with Date fields',
1620
- specs: {
1621
- name: 'Date Field Rules',
1622
- rules: [
1623
- {
1624
- id: 'DateRule',
1625
- description: 'handles Date fields correctly',
1626
- examples: [
1627
- {
1628
- description: 'event with Date fields',
1629
- given: [
1630
- {
1631
- eventRef: 'TimestampedEvent',
1632
- exampleData: {
1633
- id: 'event-123',
1634
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
1635
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
1636
- attemptedAt: '2030-01-01T09:10:00.000Z',
1637
- submittedAt: '2030-01-01T09:15:00.000Z',
1672
+ specs: [
1673
+ {
1674
+ type: 'gherkin',
1675
+ feature: 'Date Field Rules',
1676
+ rules: [
1677
+ {
1678
+ id: 'DateRule',
1679
+ name: 'handles Date fields correctly',
1680
+ examples: [
1681
+ {
1682
+ id: 'EX-001',
1683
+ name: 'event with Date fields',
1684
+ steps: [
1685
+ {
1686
+ keyword: 'Given',
1687
+ text: 'TimestampedEvent',
1688
+ docString: {
1689
+ id: 'event-123',
1690
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
1691
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
1692
+ attemptedAt: '2030-01-01T09:10:00.000Z',
1693
+ submittedAt: '2030-01-01T09:15:00.000Z',
1694
+ },
1638
1695
  },
1639
- },
1640
- ],
1641
- when: {
1642
- eventRef: 'ProcessEvent',
1643
- exampleData: {
1644
- processedAt: '2030-01-01T10:00:00.000Z',
1645
- },
1646
- },
1647
- then: [
1648
- {
1649
- stateRef: 'ProcessState',
1650
- exampleData: {
1651
- id: 'state-123',
1652
- completedAt: '2030-01-01T11:00:00.000Z',
1653
- status: 'completed',
1696
+ {
1697
+ keyword: 'When',
1698
+ text: 'ProcessEvent',
1699
+ docString: {
1700
+ processedAt: '2030-01-01T10:00:00.000Z',
1701
+ },
1654
1702
  },
1655
- },
1656
- ],
1657
- },
1658
- ],
1659
- },
1660
- ],
1661
- },
1703
+ {
1704
+ keyword: 'Then',
1705
+ text: 'ProcessState',
1706
+ docString: {
1707
+ id: 'state-123',
1708
+ completedAt: '2030-01-01T11:00:00.000Z',
1709
+ status: 'completed',
1710
+ },
1711
+ },
1712
+ ],
1713
+ },
1714
+ ],
1715
+ },
1716
+ ],
1717
+ },
1718
+ ],
1662
1719
  },
1663
1720
  },
1664
1721
  ],
@@ -1701,7 +1758,8 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1701
1758
 
1702
1759
  const code = await modelToNarrative(modelWithDateFields);
1703
1760
 
1704
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1761
+ expect(code)
1762
+ .toEqual(`import { example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1705
1763
  import type { Event, State } from '@auto-engineer/narrative';
1706
1764
  type TimestampedEvent = Event<
1707
1765
  'TimestampedEvent',
@@ -1731,20 +1789,21 @@ narrative('Date Handling Flow', 'DATE-FLOW', () => {
1731
1789
  query('date handling slice', 'DATE-SLICE').server(() => {
1732
1790
  specs('Date Field Rules', () => {
1733
1791
  rule('handles Date fields correctly', 'DateRule', () => {
1734
- example('event with Date fields')
1735
- .given<TimestampedEvent>({
1792
+ example('event with Date fields', () => {
1793
+ given<TimestampedEvent>({
1736
1794
  id: 'event-123',
1737
1795
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
1738
1796
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
1739
1797
  attemptedAt: new Date('2030-01-01T09:10:00.000Z'),
1740
1798
  submittedAt: new Date('2030-01-01T09:15:00.000Z'),
1741
- })
1742
- .when<ProcessEvent>({ processedAt: new Date('2030-01-01T10:00:00.000Z') })
1743
- .then<ProcessState>({
1799
+ });
1800
+ when<ProcessEvent>({ processedAt: new Date('2030-01-01T10:00:00.000Z') });
1801
+ then<ProcessState>({
1744
1802
  id: 'state-123',
1745
1803
  completedAt: new Date('2030-01-01T11:00:00.000Z'),
1746
1804
  status: 'completed',
1747
1805
  });
1806
+ });
1748
1807
  });
1749
1808
  });
1750
1809
  });
@@ -1841,71 +1900,74 @@ narrative('Response Analytics', () => {
1841
1900
  },
1842
1901
  server: {
1843
1902
  description: 'Summary calculation server',
1844
- specs: {
1845
- name: 'Summary Statistics',
1846
- rules: [
1847
- {
1848
- id: 'RULE-SUMMARY',
1849
- description: 'summary shows overall todo list statistics',
1850
- examples: [
1851
- {
1852
- description: 'calculates summary from multiple todos',
1853
- given: [
1854
- {
1855
- eventRef: 'TodoAdded',
1856
- exampleData: {
1857
- todoId: 'todo-001',
1858
- description: 'Buy groceries',
1859
- status: 'pending',
1860
- addedAt: new Date('2030-01-01T09:00:00.000Z'),
1903
+ specs: [
1904
+ {
1905
+ type: 'gherkin',
1906
+ feature: 'Summary Statistics',
1907
+ rules: [
1908
+ {
1909
+ id: 'RULE-SUMMARY',
1910
+ name: 'summary shows overall todo list statistics',
1911
+ examples: [
1912
+ {
1913
+ id: 'EX-001',
1914
+ name: 'calculates summary from multiple todos',
1915
+ steps: [
1916
+ {
1917
+ keyword: 'Given',
1918
+ text: 'TodoAdded',
1919
+ docString: {
1920
+ todoId: 'todo-001',
1921
+ description: 'Buy groceries',
1922
+ status: 'pending',
1923
+ addedAt: new Date('2030-01-01T09:00:00.000Z'),
1924
+ },
1861
1925
  },
1862
- },
1863
- {
1864
- eventRef: 'TodoAdded',
1865
- exampleData: {
1866
- todoId: 'todo-002',
1867
- description: 'Write report',
1868
- status: 'pending',
1869
- addedAt: new Date('2030-01-01T09:10:00.000Z'),
1926
+ {
1927
+ keyword: 'And',
1928
+ text: 'TodoAdded',
1929
+ docString: {
1930
+ todoId: 'todo-002',
1931
+ description: 'Write report',
1932
+ status: 'pending',
1933
+ addedAt: new Date('2030-01-01T09:10:00.000Z'),
1934
+ },
1870
1935
  },
1871
- },
1872
- {
1873
- eventRef: 'TodoMarkedInProgress',
1874
- exampleData: {
1875
- todoId: 'todo-001',
1876
- markedAt: new Date('2030-01-01T10:00:00.000Z'),
1936
+ {
1937
+ keyword: 'And',
1938
+ text: 'TodoMarkedInProgress',
1939
+ docString: {
1940
+ todoId: 'todo-001',
1941
+ markedAt: new Date('2030-01-01T10:00:00.000Z'),
1942
+ },
1877
1943
  },
1878
- },
1879
- {
1880
- eventRef: 'TodoMarkedComplete',
1881
- exampleData: {
1882
- todoId: 'todo-002',
1883
- completedAt: new Date('2030-01-01T11:00:00.000Z'),
1944
+ {
1945
+ keyword: 'And',
1946
+ text: 'TodoMarkedComplete',
1947
+ docString: {
1948
+ todoId: 'todo-002',
1949
+ completedAt: new Date('2030-01-01T11:00:00.000Z'),
1950
+ },
1884
1951
  },
1885
- },
1886
- ],
1887
- when: {
1888
- eventRef: '',
1889
- exampleData: {},
1890
- },
1891
- then: [
1892
- {
1893
- stateRef: 'TodoListSummary',
1894
- exampleData: {
1895
- summaryId: 'main-summary',
1896
- totalTodos: 2,
1897
- pendingCount: 0,
1898
- inProgressCount: 1,
1899
- completedCount: 1,
1900
- completionPercentage: 50,
1952
+ {
1953
+ keyword: 'Then',
1954
+ text: 'TodoListSummary',
1955
+ docString: {
1956
+ summaryId: 'main-summary',
1957
+ totalTodos: 2,
1958
+ pendingCount: 0,
1959
+ inProgressCount: 1,
1960
+ completedCount: 1,
1961
+ completionPercentage: 50,
1962
+ },
1901
1963
  },
1902
- },
1903
- ],
1904
- },
1905
- ],
1906
- },
1907
- ],
1908
- },
1964
+ ],
1965
+ },
1966
+ ],
1967
+ },
1968
+ ],
1969
+ },
1970
+ ],
1909
1971
  },
1910
1972
  },
1911
1973
  ],
@@ -1963,7 +2025,8 @@ narrative('Response Analytics', () => {
1963
2025
 
1964
2026
  const code = await modelToNarrative(modelWithEmptyWhen);
1965
2027
 
1966
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
2028
+ expect(code)
2029
+ .toEqual(`import { and, example, given, narrative, query, rule, specs, then } from '@auto-engineer/narrative';
1967
2030
  import type { Event, State } from '@auto-engineer/narrative';
1968
2031
  type TodoAdded = Event<
1969
2032
  'TodoAdded',
@@ -2003,22 +2066,22 @@ narrative('Todo List Summary', 'TODO-001', () => {
2003
2066
  query('views completion summary', 'SUMMARY-001').server(() => {
2004
2067
  specs('Summary Statistics', () => {
2005
2068
  rule('summary shows overall todo list statistics', 'RULE-SUMMARY', () => {
2006
- example('calculates summary from multiple todos')
2007
- .given<TodoAdded>({
2069
+ example('calculates summary from multiple todos', () => {
2070
+ given<TodoAdded>({
2008
2071
  todoId: 'todo-001',
2009
2072
  description: 'Buy groceries',
2010
2073
  status: 'pending',
2011
2074
  addedAt: new Date('2030-01-01T09:00:00.000Z'),
2012
- })
2013
- .and<TodoAdded>({
2075
+ });
2076
+ and<TodoAdded>({
2014
2077
  todoId: 'todo-002',
2015
2078
  description: 'Write report',
2016
2079
  status: 'pending',
2017
2080
  addedAt: new Date('2030-01-01T09:10:00.000Z'),
2018
- })
2019
- .and<TodoMarkedInProgress>({ todoId: 'todo-001', markedAt: new Date('2030-01-01T10:00:00.000Z') })
2020
- .and<TodoMarkedComplete>({ todoId: 'todo-002', completedAt: new Date('2030-01-01T11:00:00.000Z') })
2021
- .then<TodoListSummary>({
2081
+ });
2082
+ and<TodoMarkedInProgress>({ todoId: 'todo-001', markedAt: new Date('2030-01-01T10:00:00.000Z') });
2083
+ and<TodoMarkedComplete>({ todoId: 'todo-002', completedAt: new Date('2030-01-01T11:00:00.000Z') });
2084
+ then<TodoListSummary>({
2022
2085
  summaryId: 'main-summary',
2023
2086
  totalTodos: 2,
2024
2087
  pendingCount: 0,
@@ -2026,14 +2089,15 @@ narrative('Todo List Summary', 'TODO-001', () => {
2026
2089
  completedCount: 1,
2027
2090
  completionPercentage: 50,
2028
2091
  });
2092
+ });
2029
2093
  });
2030
2094
  });
2031
2095
  });
2032
2096
  });
2033
2097
  `);
2034
2098
 
2035
- expect(code).not.toContain('.when({})');
2036
- expect(code).not.toContain('.when<');
2099
+ expect(code).not.toContain('when({})');
2100
+ expect(code).not.toContain('when<');
2037
2101
  });
2038
2102
 
2039
2103
  describe('projection DSL generation', () => {
@@ -2067,10 +2131,13 @@ narrative('Todo List Summary', 'TODO-001', () => {
2067
2131
  },
2068
2132
  },
2069
2133
  ],
2070
- specs: {
2071
- name: 'Summary Rules',
2072
- rules: [],
2073
- },
2134
+ specs: [
2135
+ {
2136
+ type: 'gherkin',
2137
+ feature: 'Summary Rules',
2138
+ rules: [],
2139
+ },
2140
+ ],
2074
2141
  },
2075
2142
  },
2076
2143
  ],
@@ -2140,10 +2207,13 @@ narrative('Todo Summary Flow', 'TODO-SUMMARY', () => {
2140
2207
  },
2141
2208
  },
2142
2209
  ],
2143
- specs: {
2144
- name: 'Todo Rules',
2145
- rules: [],
2146
- },
2210
+ specs: [
2211
+ {
2212
+ type: 'gherkin',
2213
+ feature: 'Todo Rules',
2214
+ rules: [],
2215
+ },
2216
+ ],
2147
2217
  },
2148
2218
  },
2149
2219
  ],
@@ -2213,10 +2283,13 @@ narrative('Todo Flow', 'TODO-FLOW', () => {
2213
2283
  },
2214
2284
  },
2215
2285
  ],
2216
- specs: {
2217
- name: 'User Project Rules',
2218
- rules: [],
2219
- },
2286
+ specs: [
2287
+ {
2288
+ type: 'gherkin',
2289
+ feature: 'User Project Rules',
2290
+ rules: [],
2291
+ },
2292
+ ],
2220
2293
  },
2221
2294
  },
2222
2295
  ],
@@ -2288,10 +2361,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2288
2361
  },
2289
2362
  },
2290
2363
  ],
2291
- specs: {
2292
- name: 'Summary Rules',
2293
- rules: [],
2294
- },
2364
+ specs: [
2365
+ {
2366
+ type: 'gherkin',
2367
+ feature: 'Summary Rules',
2368
+ rules: [],
2369
+ },
2370
+ ],
2295
2371
  },
2296
2372
  },
2297
2373
  {
@@ -2316,10 +2392,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2316
2392
  },
2317
2393
  },
2318
2394
  ],
2319
- specs: {
2320
- name: 'Todo Rules',
2321
- rules: [],
2322
- },
2395
+ specs: [
2396
+ {
2397
+ type: 'gherkin',
2398
+ feature: 'Todo Rules',
2399
+ rules: [],
2400
+ },
2401
+ ],
2323
2402
  },
2324
2403
  },
2325
2404
  {
@@ -2344,10 +2423,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2344
2423
  },
2345
2424
  },
2346
2425
  ],
2347
- specs: {
2348
- name: 'User Project Rules',
2349
- rules: [],
2350
- },
2426
+ specs: [
2427
+ {
2428
+ type: 'gherkin',
2429
+ feature: 'User Project Rules',
2430
+ rules: [],
2431
+ },
2432
+ ],
2351
2433
  },
2352
2434
  },
2353
2435
  ],