@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
@@ -9,6 +9,7 @@ describe('modelToNarrative', () => {
9
9
  data,
10
10
  describe,
11
11
  example,
12
+ given,
12
13
  gql,
13
14
  it,
14
15
  narrative,
@@ -18,6 +19,8 @@ describe('modelToNarrative', () => {
18
19
  sink,
19
20
  source,
20
21
  specs,
22
+ then,
23
+ when,
21
24
  } from '@auto-engineer/narrative';
22
25
  import type { Command, Event, State } from '@auto-engineer/narrative';
23
26
  import { AI, ProductCatalog } from '../server/src/integrations.js';
@@ -114,34 +117,36 @@ narrative('Seasonal Assistant', () => {
114
117
  data([sink().event('ShoppingCriteriaEntered').toStream('shopping-session-\${sessionId}')]);
115
118
  specs('When shopper submits criteria, a shopping session is started', () => {
116
119
  rule('Valid criteria should start a shopping session', () => {
117
- example('User submits shopping criteria for children')
118
- .when<EnterShoppingCriteria>({
120
+ example('User submits shopping criteria for children', () => {
121
+ when<EnterShoppingCriteria>({
119
122
  sessionId: 'shopper-123',
120
123
  criteria:
121
124
  '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.',
122
- })
123
- .then<ShoppingCriteriaEntered>({
125
+ });
126
+ then<ShoppingCriteriaEntered>({
124
127
  sessionId: 'shopper-123',
125
128
  criteria:
126
129
  '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.',
127
130
  });
131
+ });
128
132
  });
129
133
  });
130
134
  });
131
135
  react('creates a chat session').server(() => {
132
136
  specs('When shopping criteria are entered, request wishlist creation', () => {
133
137
  rule('Shopping criteria should trigger item suggestion', () => {
134
- example('Criteria entered triggers wishlist creation')
135
- .when<ShoppingCriteriaEntered>({
138
+ example('Criteria entered triggers wishlist creation', () => {
139
+ when<ShoppingCriteriaEntered>({
136
140
  sessionId: 'session-abc',
137
141
  criteria:
138
142
  '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.',
139
- })
140
- .then<SuggestShoppingItems>({
143
+ });
144
+ then<SuggestShoppingItems>({
141
145
  sessionId: 'session-abc',
142
146
  prompt:
143
147
  '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.',
144
148
  });
149
+ });
145
150
  });
146
151
  });
147
152
  });
@@ -158,8 +163,8 @@ narrative('Seasonal Assistant', () => {
158
163
  ]);
159
164
  specs('When chat is triggered, AI suggests items based on product catalog', () => {
160
165
  rule('AI should suggest relevant items from available products', () => {
161
- example('Product catalog with matching items generates suggestions')
162
- .given<Products>({
166
+ example('Product catalog with matching items generates suggestions', () => {
167
+ given<Products>({
163
168
  products: [
164
169
  {
165
170
  productId: 'prod-soccer-ball',
@@ -194,13 +199,13 @@ narrative('Seasonal Assistant', () => {
194
199
  imageUrl: 'https://example.com/mtg-starter.jpg',
195
200
  },
196
201
  ],
197
- })
198
- .when<SuggestShoppingItems>({
202
+ });
203
+ when<SuggestShoppingItems>({
199
204
  sessionId: 'session-abc',
200
205
  prompt:
201
206
  '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.',
202
- })
203
- .then<ShoppingItemsSuggested>({
207
+ });
208
+ then<ShoppingItemsSuggested>({
204
209
  sessionId: 'session-abc',
205
210
  suggestedItems: [
206
211
  {
@@ -229,6 +234,7 @@ narrative('Seasonal Assistant', () => {
229
234
  },
230
235
  ],
231
236
  });
237
+ });
232
238
  });
233
239
  });
234
240
  });
@@ -257,8 +263,8 @@ narrative('Seasonal Assistant', () => {
257
263
  data([source().state('SuggestedItems').fromProjection('SuggestedItemsProjection', 'sessionId')]);
258
264
  specs('Suggested items are available for viewing', () => {
259
265
  rule('Items should be available for viewing after suggestion', () => {
260
- example('Item becomes available after AI suggestion event')
261
- .when<ShoppingItemsSuggested>({
266
+ example('Item becomes available after AI suggestion event', () => {
267
+ when<ShoppingItemsSuggested>({
262
268
  sessionId: 'session-abc',
263
269
  suggestedItems: [
264
270
  {
@@ -286,8 +292,8 @@ narrative('Seasonal Assistant', () => {
286
292
  reason: 'Ideal starter set for Magic the Gathering enthusiasts',
287
293
  },
288
294
  ],
289
- })
290
- .then<SuggestedItems>({
295
+ });
296
+ then<SuggestedItems>({
291
297
  sessionId: 'session-abc',
292
298
  items: [
293
299
  {
@@ -316,6 +322,7 @@ narrative('Seasonal Assistant', () => {
316
322
  },
317
323
  ],
318
324
  });
325
+ });
319
326
  });
320
327
  });
321
328
  });
@@ -331,8 +338,8 @@ narrative('Seasonal Assistant', () => {
331
338
  data([sink().event('ItemsAddedToCart').toStream('shopping-session-\${sessionId}')]);
332
339
  specs('When shopper accepts items, they are added to cart', () => {
333
340
  rule('Accepted items should be added to the shopping cart', () => {
334
- example('User selects all suggested items for cart')
335
- .when<AddItemsToCart>({
341
+ example('User selects all suggested items for cart', () => {
342
+ when<AddItemsToCart>({
336
343
  sessionId: 'session-abc',
337
344
  items: [
338
345
  { productId: 'prod-soccer-ball', quantity: 1 },
@@ -340,8 +347,8 @@ narrative('Seasonal Assistant', () => {
340
347
  { productId: 'prod-laptop-bag', quantity: 1 },
341
348
  { productId: 'prod-mtg-starter', quantity: 1 },
342
349
  ],
343
- })
344
- .then<ItemsAddedToCart>({
350
+ });
351
+ then<ItemsAddedToCart>({
345
352
  sessionId: 'session-abc',
346
353
  items: [
347
354
  { productId: 'prod-soccer-ball', quantity: 1 },
@@ -350,6 +357,7 @@ narrative('Seasonal Assistant', () => {
350
357
  { productId: 'prod-mtg-starter', quantity: 1 },
351
358
  ],
352
359
  });
360
+ });
353
361
  });
354
362
  });
355
363
  });
@@ -477,10 +485,13 @@ narrative('Test Flow without IDs', () => {
477
485
  },
478
486
  server: {
479
487
  description: 'Product query server',
480
- specs: {
481
- name: 'Product data specs',
482
- rules: [],
483
- },
488
+ specs: [
489
+ {
490
+ type: 'gherkin',
491
+ feature: 'Product data specs',
492
+ rules: [],
493
+ },
494
+ ],
484
495
  },
485
496
  },
486
497
  ],
@@ -528,30 +539,36 @@ narrative('Test Flow with IDs', 'FLOW-123', () => {
528
539
  },
529
540
  server: {
530
541
  description: 'Command processing server',
531
- specs: {
532
- name: 'Command Processing',
533
- rules: [
534
- {
535
- id: 'RULE-ABC',
536
- description: 'Valid commands should be processed',
537
- examples: [
538
- {
539
- description: 'User submits valid command',
540
- when: {
541
- commandRef: 'ProcessCommand',
542
- exampleData: { id: 'cmd-123', action: 'create' },
542
+ specs: [
543
+ {
544
+ type: 'gherkin',
545
+ feature: 'Command Processing',
546
+ rules: [
547
+ {
548
+ id: 'RULE-ABC',
549
+ name: 'Valid commands should be processed',
550
+ examples: [
551
+ {
552
+ id: 'EX-001',
553
+ name: 'User submits valid command',
554
+ steps: [
555
+ {
556
+ keyword: 'When',
557
+ text: 'ProcessCommand',
558
+ docString: { id: 'cmd-123', action: 'create' },
559
+ },
560
+ {
561
+ keyword: 'Then',
562
+ text: 'CommandProcessed',
563
+ docString: { id: 'cmd-123', status: 'success' },
564
+ },
565
+ ],
543
566
  },
544
- then: [
545
- {
546
- eventRef: 'CommandProcessed',
547
- exampleData: { id: 'cmd-123', status: 'success' },
548
- },
549
- ],
550
- },
551
- ],
552
- },
553
- ],
554
- },
567
+ ],
568
+ },
569
+ ],
570
+ },
571
+ ],
555
572
  },
556
573
  },
557
574
  ],
@@ -581,7 +598,8 @@ narrative('Test Flow with IDs', 'FLOW-123', () => {
581
598
  integrations: [],
582
599
  };
583
600
  const code = await modelToNarrative(modelWithRuleIds);
584
- expect(code).toEqual(`import { command, example, narrative, rule, specs } from '@auto-engineer/narrative';
601
+ expect(code)
602
+ .toEqual(`import { command, example, narrative, rule, specs, then, when } from '@auto-engineer/narrative';
585
603
  import type { Command, Event } from '@auto-engineer/narrative';
586
604
  type ProcessCommand = Command<
587
605
  'ProcessCommand',
@@ -601,9 +619,10 @@ narrative('Test Flow with Rule IDs', 'FLOW-456', () => {
601
619
  command('process command', 'SLICE-789').server(() => {
602
620
  specs('Command Processing', () => {
603
621
  rule('Valid commands should be processed', 'RULE-ABC', () => {
604
- example('User submits valid command')
605
- .when<ProcessCommand>({ id: 'cmd-123', action: 'create' })
606
- .then<CommandProcessed>({ id: 'cmd-123', status: 'success' });
622
+ example('User submits valid command', () => {
623
+ when<ProcessCommand>({ id: 'cmd-123', action: 'create' });
624
+ then<CommandProcessed>({ id: 'cmd-123', status: 'success' });
625
+ });
607
626
  });
608
627
  });
609
628
  });
@@ -680,11 +699,11 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
680
699
  narratives: [
681
700
  {
682
701
  name: 'Questionnaires',
683
- id: 'AUTO-Q9m2Kp4Lx',
702
+ id: 'Q9m2Kp4Lx',
684
703
  slices: [
685
704
  {
686
705
  name: 'Homepage',
687
- id: 'AUTO-H1a4Bn6Cy',
706
+ id: 'H1a4Bn6Cy',
688
707
  type: 'experience',
689
708
  client: {
690
709
  specs: [
@@ -695,7 +714,7 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
695
714
  },
696
715
  {
697
716
  name: 'views the questionnaire',
698
- id: 'AUTO-V7n8Rq5M',
717
+ id: 'V7n8Rq5M',
699
718
  type: 'query',
700
719
  client: {
701
720
  specs: [
@@ -727,59 +746,64 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
727
746
  },
728
747
  },
729
748
  ],
730
- specs: {
731
- name: '',
732
- rules: [
733
- {
734
- id: 'AUTO-r1A3Bp9W',
735
- description: 'questionnaires show current progress',
736
- examples: [
737
- {
738
- description: 'a question has already been answered',
739
- given: [
740
- {
741
- eventRef: 'QuestionnaireLinkSent',
742
- exampleData: {
743
- questionnaireId: 'q-001',
744
- participantId: 'participant-abc',
745
- link: 'https://app.example.com/q/q-001?participant=participant-abc',
746
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
749
+ specs: [
750
+ {
751
+ type: 'gherkin',
752
+ feature: '',
753
+ rules: [
754
+ {
755
+ id: 'r1A3Bp9W',
756
+ name: 'questionnaires show current progress',
757
+ examples: [
758
+ {
759
+ id: 'EX-001',
760
+ name: 'a question has already been answered',
761
+ steps: [
762
+ {
763
+ keyword: 'Given',
764
+ text: 'QuestionnaireLinkSent',
765
+ docString: {
766
+ questionnaireId: 'q-001',
767
+ participantId: 'participant-abc',
768
+ link: 'https://app.example.com/q/q-001?participant=participant-abc',
769
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
770
+ },
747
771
  },
748
- },
749
- ],
750
- when: {
751
- exampleData: {
752
- questionnaireId: 'q-001',
753
- participantId: 'participant-abc',
754
- questionId: 'q1',
755
- answer: 'Yes',
756
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
757
- },
758
- eventRef: 'QuestionAnswered',
759
- },
760
- then: [
761
- {
762
- stateRef: 'QuestionnaireProgress',
763
- exampleData: {
764
- questionnaireId: 'q-001',
765
- participantId: 'participant-abc',
766
- status: 'in_progress',
767
- currentQuestionId: 'q2',
768
- remainingQuestions: ['q2', 'q3'],
769
- answers: [
770
- {
771
- questionId: 'q1',
772
- value: 'Yes',
773
- },
774
- ],
772
+ {
773
+ keyword: 'When',
774
+ text: 'QuestionAnswered',
775
+ docString: {
776
+ questionnaireId: 'q-001',
777
+ participantId: 'participant-abc',
778
+ questionId: 'q1',
779
+ answer: 'Yes',
780
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
781
+ },
775
782
  },
776
- },
777
- ],
778
- },
779
- ],
780
- },
781
- ],
782
- },
783
+ {
784
+ keyword: 'Then',
785
+ text: 'QuestionnaireProgress',
786
+ docString: {
787
+ questionnaireId: 'q-001',
788
+ participantId: 'participant-abc',
789
+ status: 'in_progress',
790
+ currentQuestionId: 'q2',
791
+ remainingQuestions: ['q2', 'q3'],
792
+ answers: [
793
+ {
794
+ questionId: 'q1',
795
+ value: 'Yes',
796
+ },
797
+ ],
798
+ },
799
+ },
800
+ ],
801
+ },
802
+ ],
803
+ },
804
+ ],
805
+ },
806
+ ],
783
807
  },
784
808
  },
785
809
  ],
@@ -899,6 +923,7 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
899
923
  describe,
900
924
  example,
901
925
  experience,
926
+ given,
902
927
  gql,
903
928
  it,
904
929
  narrative,
@@ -906,6 +931,8 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
906
931
  rule,
907
932
  source,
908
933
  specs,
934
+ then,
935
+ when,
909
936
  } from '@auto-engineer/narrative';
910
937
  import type { Event, State } from '@auto-engineer/narrative';
911
938
  type QuestionnaireLinkSent = Event<
@@ -941,12 +968,12 @@ type QuestionnaireProgress = State<
941
968
  }[];
942
969
  }
943
970
  >;
944
- narrative('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
945
- experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {
971
+ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
972
+ experience('Homepage', 'H1a4Bn6Cy').client(() => {
946
973
  it('show a hero section with a welcome message');
947
974
  it('allow user to start the questionnaire');
948
975
  });
949
- query('views the questionnaire', 'AUTO-V7n8Rq5M')
976
+ query('views the questionnaire', 'V7n8Rq5M')
950
977
  .client(() => {
951
978
  describe('Questionnaire Progress', () => {
952
979
  it('focus on the current question based on the progress state');
@@ -973,22 +1000,22 @@ narrative('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
973
1000
  .server(() => {
974
1001
  data([source().state('QuestionnaireProgress').fromProjection('Questionnaires', 'questionnaire-participantId')]);
975
1002
  specs(() => {
976
- rule('questionnaires show current progress', 'AUTO-r1A3Bp9W', () => {
977
- example('a question has already been answered')
978
- .given<QuestionnaireLinkSent>({
1003
+ rule('questionnaires show current progress', 'r1A3Bp9W', () => {
1004
+ example('a question has already been answered', () => {
1005
+ given<QuestionnaireLinkSent>({
979
1006
  questionnaireId: 'q-001',
980
1007
  participantId: 'participant-abc',
981
1008
  link: 'https://app.example.com/q/q-001?participant=participant-abc',
982
1009
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
983
- })
984
- .when<QuestionAnswered>({
1010
+ });
1011
+ when<QuestionAnswered>({
985
1012
  questionnaireId: 'q-001',
986
1013
  participantId: 'participant-abc',
987
1014
  questionId: 'q1',
988
1015
  answer: 'Yes',
989
1016
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
990
- })
991
- .then<QuestionnaireProgress>({
1017
+ });
1018
+ then<QuestionnaireProgress>({
992
1019
  questionnaireId: 'q-001',
993
1020
  participantId: 'participant-abc',
994
1021
  status: 'in_progress',
@@ -996,6 +1023,7 @@ narrative('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
996
1023
  remainingQuestions: ['q2', 'q3'],
997
1024
  answers: [{ questionId: 'q1', value: 'Yes' }],
998
1025
  });
1026
+ });
999
1027
  });
1000
1028
  });
1001
1029
  });
@@ -1019,71 +1047,78 @@ narrative('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
1019
1047
  },
1020
1048
  server: {
1021
1049
  description: 'Test server for duplicate rules',
1022
- specs: {
1023
- name: 'Test Rules',
1024
- rules: [
1025
- {
1026
- id: 'AUTO-r1A3Bp9W',
1027
- description: 'questionnaires show current progress',
1028
- examples: [
1029
- {
1030
- description: 'a question has already been answered',
1031
- given: [
1032
- {
1033
- eventRef: 'QuestionnaireLinkSent',
1034
- exampleData: {
1035
- questionnaireId: 'q-001',
1036
- participantId: 'participant-abc',
1050
+ specs: [
1051
+ {
1052
+ type: 'gherkin',
1053
+ feature: 'Test Rules',
1054
+ rules: [
1055
+ {
1056
+ id: 'r1A3Bp9W',
1057
+ name: 'questionnaires show current progress',
1058
+ examples: [
1059
+ {
1060
+ id: 'EX-001',
1061
+ name: 'a question has already been answered',
1062
+ steps: [
1063
+ {
1064
+ keyword: 'Given',
1065
+ text: 'QuestionnaireLinkSent',
1066
+ docString: {
1067
+ questionnaireId: 'q-001',
1068
+ participantId: 'participant-abc',
1069
+ },
1037
1070
  },
1038
- },
1039
- ],
1040
- when: {
1041
- eventRef: 'QuestionAnswered',
1042
- exampleData: {
1043
- questionnaireId: 'q-001',
1044
- questionId: 'q1',
1045
- answer: 'Yes',
1046
- },
1047
- },
1048
- then: [
1049
- {
1050
- stateRef: 'QuestionnaireProgress',
1051
- exampleData: {
1052
- questionnaireId: 'q-001',
1053
- status: 'in_progress',
1071
+ {
1072
+ keyword: 'When',
1073
+ text: 'QuestionAnswered',
1074
+ docString: {
1075
+ questionnaireId: 'q-001',
1076
+ questionId: 'q1',
1077
+ answer: 'Yes',
1078
+ },
1054
1079
  },
1055
- },
1056
- ],
1057
- },
1058
- {
1059
- description: 'no questions have been answered yet',
1060
- given: [
1061
- {
1062
- eventRef: 'QuestionnaireLinkSent',
1063
- exampleData: {
1064
- questionnaireId: 'q-001',
1065
- participantId: 'participant-abc',
1080
+ {
1081
+ keyword: 'Then',
1082
+ text: 'QuestionnaireProgress',
1083
+ docString: {
1084
+ questionnaireId: 'q-001',
1085
+ status: 'in_progress',
1086
+ },
1066
1087
  },
1067
- },
1068
- ],
1069
- when: {
1070
- eventRef: 'QuestionnaireLinkSent',
1071
- exampleData: {},
1088
+ ],
1072
1089
  },
1073
- then: [
1074
- {
1075
- stateRef: 'QuestionnaireProgress',
1076
- exampleData: {
1077
- questionnaireId: 'q-001',
1078
- status: 'in_progress',
1090
+ {
1091
+ id: 'EX-002',
1092
+ name: 'no questions have been answered yet',
1093
+ steps: [
1094
+ {
1095
+ keyword: 'Given',
1096
+ text: 'QuestionnaireLinkSent',
1097
+ docString: {
1098
+ questionnaireId: 'q-001',
1099
+ participantId: 'participant-abc',
1100
+ },
1079
1101
  },
1080
- },
1081
- ],
1082
- },
1083
- ],
1084
- },
1085
- ],
1086
- },
1102
+ {
1103
+ keyword: 'When',
1104
+ text: 'QuestionnaireLinkSent',
1105
+ docString: {},
1106
+ },
1107
+ {
1108
+ keyword: 'Then',
1109
+ text: 'QuestionnaireProgress',
1110
+ docString: {
1111
+ questionnaireId: 'q-001',
1112
+ status: 'in_progress',
1113
+ },
1114
+ },
1115
+ ],
1116
+ },
1117
+ ],
1118
+ },
1119
+ ],
1120
+ },
1121
+ ],
1087
1122
  },
1088
1123
  },
1089
1124
  ],
@@ -1124,7 +1159,8 @@ narrative('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
1124
1159
  integrations: [],
1125
1160
  };
1126
1161
  const code = await modelToNarrative(modelWithDuplicateRules);
1127
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1162
+ expect(code)
1163
+ .toEqual(`import { example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1128
1164
  import type { Event, State } from '@auto-engineer/narrative';
1129
1165
  type QuestionnaireLinkSent = Event<
1130
1166
  'QuestionnaireLinkSent',
@@ -1151,22 +1187,23 @@ type QuestionnaireProgress = State<
1151
1187
  narrative('Test Flow', 'TEST-FLOW', () => {
1152
1188
  query('test slice', 'TEST-SLICE').server(() => {
1153
1189
  specs('Test Rules', () => {
1154
- rule('questionnaires show current progress', 'AUTO-r1A3Bp9W', () => {
1155
- example('a question has already been answered')
1156
- .given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' })
1157
- .when<QuestionAnswered>({ questionnaireId: 'q-001', questionId: 'q1', answer: 'Yes' })
1158
- .then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1159
- example('no questions have been answered yet')
1160
- .given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' })
1161
- .when<QuestionnaireLinkSent>({})
1162
- .then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1190
+ rule('questionnaires show current progress', 'r1A3Bp9W', () => {
1191
+ example('a question has already been answered', () => {
1192
+ given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' });
1193
+ when<QuestionAnswered>({ questionnaireId: 'q-001', questionId: 'q1', answer: 'Yes' });
1194
+ then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1195
+ });
1196
+ example('no questions have been answered yet', () => {
1197
+ given<QuestionnaireLinkSent>({ questionnaireId: 'q-001', participantId: 'participant-abc' });
1198
+ then<QuestionnaireProgress>({ questionnaireId: 'q-001', status: 'in_progress' });
1199
+ });
1163
1200
  });
1164
1201
  });
1165
1202
  });
1166
1203
  });
1167
1204
  `);
1168
1205
  });
1169
- it('should chain multiple given examples with .and() syntax', async () => {
1206
+ it('should chain multiple given examples with and() syntax', async () => {
1170
1207
  const modelWithMultiGiven = {
1171
1208
  variant: 'specs',
1172
1209
  narratives: [
@@ -1183,84 +1220,92 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1183
1220
  },
1184
1221
  server: {
1185
1222
  description: 'Multi given server rules',
1186
- specs: {
1187
- name: 'Multi Given Rules',
1188
- rules: [
1189
- {
1190
- id: 'AUTO-MultiGiven',
1191
- description: 'all questions have been answered',
1192
- examples: [
1193
- {
1194
- description: 'questionnaire with multiple events',
1195
- given: [
1196
- {
1197
- stateRef: 'QuestionnaireConfig',
1198
- exampleData: {
1199
- questionnaireId: 'q-001',
1200
- numberOfQuestions: 3,
1223
+ specs: [
1224
+ {
1225
+ type: 'gherkin',
1226
+ feature: 'Multi Given Rules',
1227
+ rules: [
1228
+ {
1229
+ id: 'MultiGiven',
1230
+ name: 'all questions have been answered',
1231
+ examples: [
1232
+ {
1233
+ id: 'EX-001',
1234
+ name: 'questionnaire with multiple events',
1235
+ steps: [
1236
+ {
1237
+ keyword: 'Given',
1238
+ text: 'QuestionnaireConfig',
1239
+ docString: {
1240
+ questionnaireId: 'q-001',
1241
+ numberOfQuestions: 3,
1242
+ },
1201
1243
  },
1202
- },
1203
- {
1204
- eventRef: 'QuestionnaireLinkSent',
1205
- exampleData: {
1206
- questionnaireId: 'q-001',
1207
- participantId: 'participant-abc',
1208
- link: 'https://example.com/q/q-001',
1209
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
1244
+ {
1245
+ keyword: 'And',
1246
+ text: 'QuestionnaireLinkSent',
1247
+ docString: {
1248
+ questionnaireId: 'q-001',
1249
+ participantId: 'participant-abc',
1250
+ link: 'https://example.com/q/q-001',
1251
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
1252
+ },
1210
1253
  },
1211
- },
1212
- {
1213
- eventRef: 'QuestionAnswered',
1214
- exampleData: {
1215
- questionnaireId: 'q-001',
1216
- participantId: 'participant-abc',
1217
- questionId: 'q1',
1218
- answer: 'Yes',
1219
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
1254
+ {
1255
+ keyword: 'And',
1256
+ text: 'QuestionAnswered',
1257
+ docString: {
1258
+ questionnaireId: 'q-001',
1259
+ participantId: 'participant-abc',
1260
+ questionId: 'q1',
1261
+ answer: 'Yes',
1262
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
1263
+ },
1220
1264
  },
1221
- },
1222
- {
1223
- eventRef: 'QuestionAnswered',
1224
- exampleData: {
1225
- questionnaireId: 'q-001',
1226
- participantId: 'participant-abc',
1227
- questionId: 'q2',
1228
- answer: 'No',
1229
- savedAt: new Date('2030-01-01T09:10:00.000Z'),
1265
+ {
1266
+ keyword: 'And',
1267
+ text: 'QuestionAnswered',
1268
+ docString: {
1269
+ questionnaireId: 'q-001',
1270
+ participantId: 'participant-abc',
1271
+ questionId: 'q2',
1272
+ answer: 'No',
1273
+ savedAt: new Date('2030-01-01T09:10:00.000Z'),
1274
+ },
1230
1275
  },
1231
- },
1232
- ],
1233
- when: {
1234
- eventRef: 'QuestionAnswered',
1235
- exampleData: {
1236
- questionnaireId: 'q-001',
1237
- participantId: 'participant-abc',
1238
- questionId: 'q3',
1239
- answer: 'Maybe',
1240
- savedAt: new Date('2030-01-01T09:15:00.000Z'),
1241
- },
1242
- },
1243
- then: [
1244
- {
1245
- stateRef: 'QuestionnaireProgress',
1246
- exampleData: {
1247
- questionnaireId: 'q-001',
1248
- participantId: 'participant-abc',
1249
- status: 'ready_to_submit',
1250
- currentQuestionId: null,
1251
- remainingQuestions: [],
1252
- answers: [
1253
- { questionId: 'q1', value: 'Yes' },
1254
- { questionId: 'q2', value: 'No' },
1255
- ],
1276
+ {
1277
+ keyword: 'When',
1278
+ text: 'QuestionAnswered',
1279
+ docString: {
1280
+ questionnaireId: 'q-001',
1281
+ participantId: 'participant-abc',
1282
+ questionId: 'q3',
1283
+ answer: 'Maybe',
1284
+ savedAt: new Date('2030-01-01T09:15:00.000Z'),
1285
+ },
1256
1286
  },
1257
- },
1258
- ],
1259
- },
1260
- ],
1261
- },
1262
- ],
1263
- },
1287
+ {
1288
+ keyword: 'Then',
1289
+ text: 'QuestionnaireProgress',
1290
+ docString: {
1291
+ questionnaireId: 'q-001',
1292
+ participantId: 'participant-abc',
1293
+ status: 'ready_to_submit',
1294
+ currentQuestionId: null,
1295
+ remainingQuestions: [],
1296
+ answers: [
1297
+ { questionId: 'q1', value: 'Yes' },
1298
+ { questionId: 'q2', value: 'No' },
1299
+ ],
1300
+ },
1301
+ },
1302
+ ],
1303
+ },
1304
+ ],
1305
+ },
1306
+ ],
1307
+ },
1308
+ ],
1264
1309
  },
1265
1310
  },
1266
1311
  ],
@@ -1318,7 +1363,8 @@ narrative('Test Flow', 'TEST-FLOW', () => {
1318
1363
  integrations: [],
1319
1364
  };
1320
1365
  const code = await modelToNarrative(modelWithMultiGiven);
1321
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1366
+ expect(code)
1367
+ .toEqual(`import { and, example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1322
1368
  import type { Event, State } from '@auto-engineer/narrative';
1323
1369
  type QuestionnaireConfig = State<
1324
1370
  'QuestionnaireConfig',
@@ -1363,37 +1409,37 @@ type QuestionnaireProgress = State<
1363
1409
  narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1364
1410
  query('multi given slice', 'MULTI-SLICE').server(() => {
1365
1411
  specs('Multi Given Rules', () => {
1366
- rule('all questions have been answered', 'AUTO-MultiGiven', () => {
1367
- example('questionnaire with multiple events')
1368
- .given<QuestionnaireConfig>({ questionnaireId: 'q-001', numberOfQuestions: 3 })
1369
- .and<QuestionnaireLinkSent>({
1412
+ rule('all questions have been answered', 'MultiGiven', () => {
1413
+ example('questionnaire with multiple events', () => {
1414
+ given<QuestionnaireConfig>({ questionnaireId: 'q-001', numberOfQuestions: 3 });
1415
+ and<QuestionnaireLinkSent>({
1370
1416
  questionnaireId: 'q-001',
1371
1417
  participantId: 'participant-abc',
1372
1418
  link: 'https://example.com/q/q-001',
1373
1419
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
1374
- })
1375
- .and<QuestionAnswered>({
1420
+ });
1421
+ and<QuestionAnswered>({
1376
1422
  questionnaireId: 'q-001',
1377
1423
  participantId: 'participant-abc',
1378
1424
  questionId: 'q1',
1379
1425
  answer: 'Yes',
1380
1426
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
1381
- })
1382
- .and<QuestionAnswered>({
1427
+ });
1428
+ and<QuestionAnswered>({
1383
1429
  questionnaireId: 'q-001',
1384
1430
  participantId: 'participant-abc',
1385
1431
  questionId: 'q2',
1386
1432
  answer: 'No',
1387
1433
  savedAt: new Date('2030-01-01T09:10:00.000Z'),
1388
- })
1389
- .when<QuestionAnswered>({
1434
+ });
1435
+ when<QuestionAnswered>({
1390
1436
  questionnaireId: 'q-001',
1391
1437
  participantId: 'participant-abc',
1392
1438
  questionId: 'q3',
1393
1439
  answer: 'Maybe',
1394
1440
  savedAt: new Date('2030-01-01T09:15:00.000Z'),
1395
- })
1396
- .then<QuestionnaireProgress>({
1441
+ });
1442
+ then<QuestionnaireProgress>({
1397
1443
  questionnaireId: 'q-001',
1398
1444
  participantId: 'participant-abc',
1399
1445
  status: 'ready_to_submit',
@@ -1404,6 +1450,7 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1404
1450
  { questionId: 'q2', value: 'No' },
1405
1451
  ],
1406
1452
  });
1453
+ });
1407
1454
  });
1408
1455
  });
1409
1456
  });
@@ -1451,45 +1498,50 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1451
1498
  },
1452
1499
  },
1453
1500
  ],
1454
- specs: {
1455
- name: 'Database State Rules',
1456
- rules: [
1457
- {
1458
- id: 'AUTO-RefState',
1459
- description: 'questionnaire config is available when referenced',
1460
- examples: [
1461
- {
1462
- description: 'config from database is accessible',
1463
- given: [
1464
- {
1465
- stateRef: 'QuestionnaireConfig',
1466
- exampleData: {
1467
- questionnaireId: 'q-001',
1468
- numberOfQuestions: 5,
1469
- title: 'Customer Satisfaction Survey',
1501
+ specs: [
1502
+ {
1503
+ type: 'gherkin',
1504
+ feature: 'Database State Rules',
1505
+ rules: [
1506
+ {
1507
+ id: 'RefState',
1508
+ name: 'questionnaire config is available when referenced',
1509
+ examples: [
1510
+ {
1511
+ id: 'EX-001',
1512
+ name: 'config from database is accessible',
1513
+ steps: [
1514
+ {
1515
+ keyword: 'Given',
1516
+ text: 'QuestionnaireConfig',
1517
+ docString: {
1518
+ questionnaireId: 'q-001',
1519
+ numberOfQuestions: 5,
1520
+ title: 'Customer Satisfaction Survey',
1521
+ },
1470
1522
  },
1471
- },
1472
- ],
1473
- when: {
1474
- eventRef: 'QuestionnaireProgress',
1475
- exampleData: {},
1476
- },
1477
- then: [
1478
- {
1479
- stateRef: 'QuestionnaireProgress',
1480
- exampleData: {
1481
- questionnaireId: 'q-001',
1482
- participantId: 'participant-abc',
1483
- status: 'in_progress',
1484
- totalQuestions: 5,
1523
+ {
1524
+ keyword: 'When',
1525
+ text: 'QuestionnaireProgress',
1526
+ docString: {},
1485
1527
  },
1486
- },
1487
- ],
1488
- },
1489
- ],
1490
- },
1491
- ],
1492
- },
1528
+ {
1529
+ keyword: 'Then',
1530
+ text: 'QuestionnaireProgress',
1531
+ docString: {
1532
+ questionnaireId: 'q-001',
1533
+ participantId: 'participant-abc',
1534
+ status: 'in_progress',
1535
+ totalQuestions: 5,
1536
+ },
1537
+ },
1538
+ ],
1539
+ },
1540
+ ],
1541
+ },
1542
+ ],
1543
+ },
1544
+ ],
1493
1545
  },
1494
1546
  },
1495
1547
  ],
@@ -1522,7 +1574,7 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1522
1574
  };
1523
1575
  const code = await modelToNarrative(modelWithReferencedStates);
1524
1576
  expect(code)
1525
- .toEqual(`import { data, example, narrative, query, rule, source, specs } from '@auto-engineer/narrative';
1577
+ .toEqual(`import { data, example, given, narrative, query, rule, source, specs, then } from '@auto-engineer/narrative';
1526
1578
  import type { State } from '@auto-engineer/narrative';
1527
1579
  type QuestionnaireProgress = State<
1528
1580
  'QuestionnaireProgress',
@@ -1548,20 +1600,20 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1548
1600
  source().state('QuestionnaireConfig').fromDatabase('ConfigStore', { questionnaireId: '$questionnaireId' }),
1549
1601
  ]);
1550
1602
  specs('Database State Rules', () => {
1551
- rule('questionnaire config is available when referenced', 'AUTO-RefState', () => {
1552
- example('config from database is accessible')
1553
- .given<QuestionnaireConfig>({
1603
+ rule('questionnaire config is available when referenced', 'RefState', () => {
1604
+ example('config from database is accessible', () => {
1605
+ given<QuestionnaireConfig>({
1554
1606
  questionnaireId: 'q-001',
1555
1607
  numberOfQuestions: 5,
1556
1608
  title: 'Customer Satisfaction Survey',
1557
- })
1558
- .when<QuestionnaireProgress>({})
1559
- .then<QuestionnaireProgress>({
1609
+ });
1610
+ then<QuestionnaireProgress>({
1560
1611
  questionnaireId: 'q-001',
1561
1612
  participantId: 'participant-abc',
1562
1613
  status: 'in_progress',
1563
1614
  totalQuestions: 5,
1564
1615
  });
1616
+ });
1565
1617
  });
1566
1618
  });
1567
1619
  });
@@ -1585,48 +1637,53 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1585
1637
  },
1586
1638
  server: {
1587
1639
  description: 'Date server with Date fields',
1588
- specs: {
1589
- name: 'Date Field Rules',
1590
- rules: [
1591
- {
1592
- id: 'AUTO-DateRule',
1593
- description: 'handles Date fields correctly',
1594
- examples: [
1595
- {
1596
- description: 'event with Date fields',
1597
- given: [
1598
- {
1599
- eventRef: 'TimestampedEvent',
1600
- exampleData: {
1601
- id: 'event-123',
1602
- sentAt: new Date('2030-01-01T09:00:00.000Z'),
1603
- savedAt: new Date('2030-01-01T09:05:00.000Z'),
1604
- attemptedAt: '2030-01-01T09:10:00.000Z',
1605
- submittedAt: '2030-01-01T09:15:00.000Z',
1640
+ specs: [
1641
+ {
1642
+ type: 'gherkin',
1643
+ feature: 'Date Field Rules',
1644
+ rules: [
1645
+ {
1646
+ id: 'DateRule',
1647
+ name: 'handles Date fields correctly',
1648
+ examples: [
1649
+ {
1650
+ id: 'EX-001',
1651
+ name: 'event with Date fields',
1652
+ steps: [
1653
+ {
1654
+ keyword: 'Given',
1655
+ text: 'TimestampedEvent',
1656
+ docString: {
1657
+ id: 'event-123',
1658
+ sentAt: new Date('2030-01-01T09:00:00.000Z'),
1659
+ savedAt: new Date('2030-01-01T09:05:00.000Z'),
1660
+ attemptedAt: '2030-01-01T09:10:00.000Z',
1661
+ submittedAt: '2030-01-01T09:15:00.000Z',
1662
+ },
1606
1663
  },
1607
- },
1608
- ],
1609
- when: {
1610
- eventRef: 'ProcessEvent',
1611
- exampleData: {
1612
- processedAt: '2030-01-01T10:00:00.000Z',
1613
- },
1614
- },
1615
- then: [
1616
- {
1617
- stateRef: 'ProcessState',
1618
- exampleData: {
1619
- id: 'state-123',
1620
- completedAt: '2030-01-01T11:00:00.000Z',
1621
- status: 'completed',
1664
+ {
1665
+ keyword: 'When',
1666
+ text: 'ProcessEvent',
1667
+ docString: {
1668
+ processedAt: '2030-01-01T10:00:00.000Z',
1669
+ },
1622
1670
  },
1623
- },
1624
- ],
1625
- },
1626
- ],
1627
- },
1628
- ],
1629
- },
1671
+ {
1672
+ keyword: 'Then',
1673
+ text: 'ProcessState',
1674
+ docString: {
1675
+ id: 'state-123',
1676
+ completedAt: '2030-01-01T11:00:00.000Z',
1677
+ status: 'completed',
1678
+ },
1679
+ },
1680
+ ],
1681
+ },
1682
+ ],
1683
+ },
1684
+ ],
1685
+ },
1686
+ ],
1630
1687
  },
1631
1688
  },
1632
1689
  ],
@@ -1667,7 +1724,8 @@ narrative('Referenced States Flow', 'REF-STATES', () => {
1667
1724
  integrations: [],
1668
1725
  };
1669
1726
  const code = await modelToNarrative(modelWithDateFields);
1670
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1727
+ expect(code)
1728
+ .toEqual(`import { example, given, narrative, query, rule, specs, then, when } from '@auto-engineer/narrative';
1671
1729
  import type { Event, State } from '@auto-engineer/narrative';
1672
1730
  type TimestampedEvent = Event<
1673
1731
  'TimestampedEvent',
@@ -1696,21 +1754,22 @@ type ProcessState = State<
1696
1754
  narrative('Date Handling Flow', 'DATE-FLOW', () => {
1697
1755
  query('date handling slice', 'DATE-SLICE').server(() => {
1698
1756
  specs('Date Field Rules', () => {
1699
- rule('handles Date fields correctly', 'AUTO-DateRule', () => {
1700
- example('event with Date fields')
1701
- .given<TimestampedEvent>({
1757
+ rule('handles Date fields correctly', 'DateRule', () => {
1758
+ example('event with Date fields', () => {
1759
+ given<TimestampedEvent>({
1702
1760
  id: 'event-123',
1703
1761
  sentAt: new Date('2030-01-01T09:00:00.000Z'),
1704
1762
  savedAt: new Date('2030-01-01T09:05:00.000Z'),
1705
1763
  attemptedAt: new Date('2030-01-01T09:10:00.000Z'),
1706
1764
  submittedAt: new Date('2030-01-01T09:15:00.000Z'),
1707
- })
1708
- .when<ProcessEvent>({ processedAt: new Date('2030-01-01T10:00:00.000Z') })
1709
- .then<ProcessState>({
1765
+ });
1766
+ when<ProcessEvent>({ processedAt: new Date('2030-01-01T10:00:00.000Z') });
1767
+ then<ProcessState>({
1710
1768
  id: 'state-123',
1711
1769
  completedAt: new Date('2030-01-01T11:00:00.000Z'),
1712
1770
  status: 'completed',
1713
1771
  });
1772
+ });
1714
1773
  });
1715
1774
  });
1716
1775
  });
@@ -1727,7 +1786,7 @@ narrative('Date Handling Flow', 'DATE-FLOW', () => {
1727
1786
  slices: [
1728
1787
  {
1729
1788
  name: 'Active Surveys Summary',
1730
- id: 'AUTO-aifPcU3hw',
1789
+ id: 'aifPcU3hw',
1731
1790
  type: 'experience',
1732
1791
  client: {
1733
1792
  specs: [{ type: 'it', title: 'show active surveys summary' }],
@@ -1741,7 +1800,7 @@ narrative('Date Handling Flow', 'DATE-FLOW', () => {
1741
1800
  slices: [
1742
1801
  {
1743
1802
  name: 'Create Survey Form',
1744
- id: 'AUTO-MPviTMrQC',
1803
+ id: 'MPviTMrQC',
1745
1804
  type: 'experience',
1746
1805
  client: {
1747
1806
  specs: [{ type: 'it', title: 'allow entering survey title' }],
@@ -1755,7 +1814,7 @@ narrative('Date Handling Flow', 'DATE-FLOW', () => {
1755
1814
  slices: [
1756
1815
  {
1757
1816
  name: 'Response Rate Charts',
1758
- id: 'AUTO-eME978Euk',
1817
+ id: 'eME978Euk',
1759
1818
  type: 'experience',
1760
1819
  client: {
1761
1820
  specs: [{ type: 'it', title: 'show daily response rate charts' }],
@@ -1770,17 +1829,17 @@ narrative('Date Handling Flow', 'DATE-FLOW', () => {
1770
1829
  const code = await modelToNarrative(modelWithMultipleFlowsSameSource);
1771
1830
  expect(code).toEqual(`import { experience, it, narrative } from '@auto-engineer/narrative';
1772
1831
  narrative('Home Screen', () => {
1773
- experience('Active Surveys Summary', 'AUTO-aifPcU3hw').client(() => {
1832
+ experience('Active Surveys Summary', 'aifPcU3hw').client(() => {
1774
1833
  it('show active surveys summary');
1775
1834
  });
1776
1835
  });
1777
1836
  narrative('Create Survey', () => {
1778
- experience('Create Survey Form', 'AUTO-MPviTMrQC').client(() => {
1837
+ experience('Create Survey Form', 'MPviTMrQC').client(() => {
1779
1838
  it('allow entering survey title');
1780
1839
  });
1781
1840
  });
1782
1841
  narrative('Response Analytics', () => {
1783
- experience('Response Rate Charts', 'AUTO-eME978Euk').client(() => {
1842
+ experience('Response Rate Charts', 'eME978Euk').client(() => {
1784
1843
  it('show daily response rate charts');
1785
1844
  });
1786
1845
  });
@@ -1803,71 +1862,74 @@ narrative('Response Analytics', () => {
1803
1862
  },
1804
1863
  server: {
1805
1864
  description: 'Summary calculation server',
1806
- specs: {
1807
- name: 'Summary Statistics',
1808
- rules: [
1809
- {
1810
- id: 'RULE-SUMMARY',
1811
- description: 'summary shows overall todo list statistics',
1812
- examples: [
1813
- {
1814
- description: 'calculates summary from multiple todos',
1815
- given: [
1816
- {
1817
- eventRef: 'TodoAdded',
1818
- exampleData: {
1819
- todoId: 'todo-001',
1820
- description: 'Buy groceries',
1821
- status: 'pending',
1822
- addedAt: new Date('2030-01-01T09:00:00.000Z'),
1865
+ specs: [
1866
+ {
1867
+ type: 'gherkin',
1868
+ feature: 'Summary Statistics',
1869
+ rules: [
1870
+ {
1871
+ id: 'RULE-SUMMARY',
1872
+ name: 'summary shows overall todo list statistics',
1873
+ examples: [
1874
+ {
1875
+ id: 'EX-001',
1876
+ name: 'calculates summary from multiple todos',
1877
+ steps: [
1878
+ {
1879
+ keyword: 'Given',
1880
+ text: 'TodoAdded',
1881
+ docString: {
1882
+ todoId: 'todo-001',
1883
+ description: 'Buy groceries',
1884
+ status: 'pending',
1885
+ addedAt: new Date('2030-01-01T09:00:00.000Z'),
1886
+ },
1823
1887
  },
1824
- },
1825
- {
1826
- eventRef: 'TodoAdded',
1827
- exampleData: {
1828
- todoId: 'todo-002',
1829
- description: 'Write report',
1830
- status: 'pending',
1831
- addedAt: new Date('2030-01-01T09:10:00.000Z'),
1888
+ {
1889
+ keyword: 'And',
1890
+ text: 'TodoAdded',
1891
+ docString: {
1892
+ todoId: 'todo-002',
1893
+ description: 'Write report',
1894
+ status: 'pending',
1895
+ addedAt: new Date('2030-01-01T09:10:00.000Z'),
1896
+ },
1832
1897
  },
1833
- },
1834
- {
1835
- eventRef: 'TodoMarkedInProgress',
1836
- exampleData: {
1837
- todoId: 'todo-001',
1838
- markedAt: new Date('2030-01-01T10:00:00.000Z'),
1898
+ {
1899
+ keyword: 'And',
1900
+ text: 'TodoMarkedInProgress',
1901
+ docString: {
1902
+ todoId: 'todo-001',
1903
+ markedAt: new Date('2030-01-01T10:00:00.000Z'),
1904
+ },
1839
1905
  },
1840
- },
1841
- {
1842
- eventRef: 'TodoMarkedComplete',
1843
- exampleData: {
1844
- todoId: 'todo-002',
1845
- completedAt: new Date('2030-01-01T11:00:00.000Z'),
1906
+ {
1907
+ keyword: 'And',
1908
+ text: 'TodoMarkedComplete',
1909
+ docString: {
1910
+ todoId: 'todo-002',
1911
+ completedAt: new Date('2030-01-01T11:00:00.000Z'),
1912
+ },
1846
1913
  },
1847
- },
1848
- ],
1849
- when: {
1850
- eventRef: '',
1851
- exampleData: {},
1852
- },
1853
- then: [
1854
- {
1855
- stateRef: 'TodoListSummary',
1856
- exampleData: {
1857
- summaryId: 'main-summary',
1858
- totalTodos: 2,
1859
- pendingCount: 0,
1860
- inProgressCount: 1,
1861
- completedCount: 1,
1862
- completionPercentage: 50,
1914
+ {
1915
+ keyword: 'Then',
1916
+ text: 'TodoListSummary',
1917
+ docString: {
1918
+ summaryId: 'main-summary',
1919
+ totalTodos: 2,
1920
+ pendingCount: 0,
1921
+ inProgressCount: 1,
1922
+ completedCount: 1,
1923
+ completionPercentage: 50,
1924
+ },
1863
1925
  },
1864
- },
1865
- ],
1866
- },
1867
- ],
1868
- },
1869
- ],
1870
- },
1926
+ ],
1927
+ },
1928
+ ],
1929
+ },
1930
+ ],
1931
+ },
1932
+ ],
1871
1933
  },
1872
1934
  },
1873
1935
  ],
@@ -1923,7 +1985,8 @@ narrative('Response Analytics', () => {
1923
1985
  integrations: [],
1924
1986
  };
1925
1987
  const code = await modelToNarrative(modelWithEmptyWhen);
1926
- expect(code).toEqual(`import { example, narrative, query, rule, specs } from '@auto-engineer/narrative';
1988
+ expect(code)
1989
+ .toEqual(`import { and, example, given, narrative, query, rule, specs, then } from '@auto-engineer/narrative';
1927
1990
  import type { Event, State } from '@auto-engineer/narrative';
1928
1991
  type TodoAdded = Event<
1929
1992
  'TodoAdded',
@@ -1963,22 +2026,22 @@ narrative('Todo List Summary', 'TODO-001', () => {
1963
2026
  query('views completion summary', 'SUMMARY-001').server(() => {
1964
2027
  specs('Summary Statistics', () => {
1965
2028
  rule('summary shows overall todo list statistics', 'RULE-SUMMARY', () => {
1966
- example('calculates summary from multiple todos')
1967
- .given<TodoAdded>({
2029
+ example('calculates summary from multiple todos', () => {
2030
+ given<TodoAdded>({
1968
2031
  todoId: 'todo-001',
1969
2032
  description: 'Buy groceries',
1970
2033
  status: 'pending',
1971
2034
  addedAt: new Date('2030-01-01T09:00:00.000Z'),
1972
- })
1973
- .and<TodoAdded>({
2035
+ });
2036
+ and<TodoAdded>({
1974
2037
  todoId: 'todo-002',
1975
2038
  description: 'Write report',
1976
2039
  status: 'pending',
1977
2040
  addedAt: new Date('2030-01-01T09:10:00.000Z'),
1978
- })
1979
- .and<TodoMarkedInProgress>({ todoId: 'todo-001', markedAt: new Date('2030-01-01T10:00:00.000Z') })
1980
- .and<TodoMarkedComplete>({ todoId: 'todo-002', completedAt: new Date('2030-01-01T11:00:00.000Z') })
1981
- .then<TodoListSummary>({
2041
+ });
2042
+ and<TodoMarkedInProgress>({ todoId: 'todo-001', markedAt: new Date('2030-01-01T10:00:00.000Z') });
2043
+ and<TodoMarkedComplete>({ todoId: 'todo-002', completedAt: new Date('2030-01-01T11:00:00.000Z') });
2044
+ then<TodoListSummary>({
1982
2045
  summaryId: 'main-summary',
1983
2046
  totalTodos: 2,
1984
2047
  pendingCount: 0,
@@ -1986,13 +2049,14 @@ narrative('Todo List Summary', 'TODO-001', () => {
1986
2049
  completedCount: 1,
1987
2050
  completionPercentage: 50,
1988
2051
  });
2052
+ });
1989
2053
  });
1990
2054
  });
1991
2055
  });
1992
2056
  });
1993
2057
  `);
1994
- expect(code).not.toContain('.when({})');
1995
- expect(code).not.toContain('.when<');
2058
+ expect(code).not.toContain('when({})');
2059
+ expect(code).not.toContain('when<');
1996
2060
  });
1997
2061
  describe('projection DSL generation', () => {
1998
2062
  it('should generate fromSingletonProjection for singleton projections', async () => {
@@ -2025,10 +2089,13 @@ narrative('Todo List Summary', 'TODO-001', () => {
2025
2089
  },
2026
2090
  },
2027
2091
  ],
2028
- specs: {
2029
- name: 'Summary Rules',
2030
- rules: [],
2031
- },
2092
+ specs: [
2093
+ {
2094
+ type: 'gherkin',
2095
+ feature: 'Summary Rules',
2096
+ rules: [],
2097
+ },
2098
+ ],
2032
2099
  },
2033
2100
  },
2034
2101
  ],
@@ -2095,10 +2162,13 @@ narrative('Todo Summary Flow', 'TODO-SUMMARY', () => {
2095
2162
  },
2096
2163
  },
2097
2164
  ],
2098
- specs: {
2099
- name: 'Todo Rules',
2100
- rules: [],
2101
- },
2165
+ specs: [
2166
+ {
2167
+ type: 'gherkin',
2168
+ feature: 'Todo Rules',
2169
+ rules: [],
2170
+ },
2171
+ ],
2102
2172
  },
2103
2173
  },
2104
2174
  ],
@@ -2165,10 +2235,13 @@ narrative('Todo Flow', 'TODO-FLOW', () => {
2165
2235
  },
2166
2236
  },
2167
2237
  ],
2168
- specs: {
2169
- name: 'User Project Rules',
2170
- rules: [],
2171
- },
2238
+ specs: [
2239
+ {
2240
+ type: 'gherkin',
2241
+ feature: 'User Project Rules',
2242
+ rules: [],
2243
+ },
2244
+ ],
2172
2245
  },
2173
2246
  },
2174
2247
  ],
@@ -2237,10 +2310,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2237
2310
  },
2238
2311
  },
2239
2312
  ],
2240
- specs: {
2241
- name: 'Summary Rules',
2242
- rules: [],
2243
- },
2313
+ specs: [
2314
+ {
2315
+ type: 'gherkin',
2316
+ feature: 'Summary Rules',
2317
+ rules: [],
2318
+ },
2319
+ ],
2244
2320
  },
2245
2321
  },
2246
2322
  {
@@ -2265,10 +2341,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2265
2341
  },
2266
2342
  },
2267
2343
  ],
2268
- specs: {
2269
- name: 'Todo Rules',
2270
- rules: [],
2271
- },
2344
+ specs: [
2345
+ {
2346
+ type: 'gherkin',
2347
+ feature: 'Todo Rules',
2348
+ rules: [],
2349
+ },
2350
+ ],
2272
2351
  },
2273
2352
  },
2274
2353
  {
@@ -2293,10 +2372,13 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2293
2372
  },
2294
2373
  },
2295
2374
  ],
2296
- specs: {
2297
- name: 'User Project Rules',
2298
- rules: [],
2299
- },
2375
+ specs: [
2376
+ {
2377
+ type: 'gherkin',
2378
+ feature: 'User Project Rules',
2379
+ rules: [],
2380
+ },
2381
+ ],
2300
2382
  },
2301
2383
  },
2302
2384
  ],