@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/src/commands/export-schema-runner.js +1 -1
- package/dist/src/commands/export-schema-runner.js.map +1 -1
- package/dist/src/fluent-builder.js +3 -3
- package/dist/src/fluent-builder.js.map +1 -1
- package/dist/src/getNarratives.specs.js +157 -161
- package/dist/src/getNarratives.specs.js.map +1 -1
- package/dist/src/id/addAutoIds.d.ts.map +1 -1
- package/dist/src/id/addAutoIds.js +23 -10
- package/dist/src/id/addAutoIds.js.map +1 -1
- package/dist/src/id/addAutoIds.specs.js +55 -46
- package/dist/src/id/addAutoIds.specs.js.map +1 -1
- package/dist/src/id/generators.js +1 -1
- package/dist/src/id/generators.js.map +1 -1
- package/dist/src/id/hasAllIds.d.ts.map +1 -1
- package/dist/src/id/hasAllIds.js +8 -3
- package/dist/src/id/hasAllIds.js.map +1 -1
- package/dist/src/id/hasAllIds.specs.js +142 -215
- package/dist/src/id/hasAllIds.specs.js.map +1 -1
- package/dist/src/index.d.ts +7 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/loader/graph.d.ts.map +1 -1
- package/dist/src/loader/graph.js +13 -6
- package/dist/src/loader/graph.js.map +1 -1
- package/dist/src/loader/ts-utils.d.ts +1 -0
- package/dist/src/loader/ts-utils.d.ts.map +1 -1
- package/dist/src/loader/ts-utils.js +95 -16
- package/dist/src/loader/ts-utils.js.map +1 -1
- package/dist/src/model-to-narrative.specs.js +548 -466
- package/dist/src/model-to-narrative.specs.js.map +1 -1
- package/dist/src/narrative-context.d.ts +8 -8
- package/dist/src/narrative-context.d.ts.map +1 -1
- package/dist/src/narrative-context.js +111 -301
- package/dist/src/narrative-context.js.map +1 -1
- package/dist/src/narrative-context.specs.js +15 -55
- package/dist/src/narrative-context.specs.js.map +1 -1
- package/dist/src/narrative.d.ts +19 -22
- package/dist/src/narrative.d.ts.map +1 -1
- package/dist/src/narrative.js +42 -71
- package/dist/src/narrative.js.map +1 -1
- package/dist/src/samples/questionnaires.narrative.js +10 -10
- package/dist/src/samples/questionnaires.narrative.js.map +1 -1
- package/dist/src/samples/test-with-ids.narrative.js +13 -29
- package/dist/src/samples/test-with-ids.narrative.js.map +1 -1
- package/dist/src/schema.d.ts +2704 -8293
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +26 -47
- package/dist/src/schema.js.map +1 -1
- package/dist/src/slice-builder.js +3 -3
- package/dist/src/slice-builder.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.js +118 -74
- package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts +9 -1
- package/dist/src/transformers/model-to-narrative/generators/gwt.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/gwt.js +112 -112
- package/dist/src/transformers/model-to-narrative/generators/gwt.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/imports.d.ts +1 -1
- package/dist/src/transformers/model-to-narrative/generators/imports.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/imports.js +13 -9
- package/dist/src/transformers/model-to-narrative/generators/imports.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
- package/dist/src/transformers/narrative-to-model/index.js +50 -23
- package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/type-inference.specs.js +100 -90
- package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/commands/export-schema-runner.ts +3 -1
- package/src/fluent-builder.ts +3 -3
- package/src/getNarratives.specs.ts +176 -184
- package/src/id/addAutoIds.specs.ts +55 -48
- package/src/id/addAutoIds.ts +28 -11
- package/src/id/generators.ts +1 -1
- package/src/id/hasAllIds.specs.ts +147 -245
- package/src/id/hasAllIds.ts +11 -4
- package/src/index.ts +11 -12
- package/src/loader/graph.ts +23 -6
- package/src/loader/ts-utils.ts +169 -26
- package/src/model-to-narrative.specs.ts +548 -466
- package/src/narrative-context.specs.ts +73 -116
- package/src/narrative-context.ts +127 -374
- package/src/narrative.ts +70 -120
- package/src/samples/questionnaires.narrative.ts +10 -10
- package/src/samples/test-with-ids.narrative.ts +23 -31
- package/src/schema.ts +33 -52
- package/src/slice-builder.ts +3 -3
- package/src/transformers/model-to-narrative/generators/flow.ts +191 -85
- package/src/transformers/model-to-narrative/generators/gwt.ts +195 -178
- package/src/transformers/model-to-narrative/generators/imports.ts +13 -9
- package/src/transformers/narrative-to-model/index.ts +87 -26
- package/src/transformers/narrative-to-model/type-inference.specs.ts +100 -90
|
@@ -64,32 +64,36 @@ describe('getNarratives', (_mode) => {
|
|
|
64
64
|
expect(createItemSlice.client.specs[0].children).toHaveLength(1);
|
|
65
65
|
}
|
|
66
66
|
expect(createItemSlice.server.specs).toBeDefined();
|
|
67
|
-
|
|
68
|
-
expect(
|
|
67
|
+
expect(Array.isArray(createItemSlice.server.specs)).toBe(true);
|
|
68
|
+
expect(createItemSlice.server.specs).toHaveLength(1);
|
|
69
|
+
const spec = createItemSlice.server.specs[0];
|
|
70
|
+
expect(spec.feature).toBeDefined();
|
|
69
71
|
expect(spec.rules).toHaveLength(1);
|
|
70
72
|
const rule = spec.rules[0];
|
|
71
|
-
expect(rule.
|
|
73
|
+
expect(rule.name).toBeDefined();
|
|
72
74
|
expect(rule.examples).toHaveLength(1);
|
|
73
75
|
const example = rule.examples[0];
|
|
74
|
-
expect(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
expect(example.steps).toBeDefined();
|
|
77
|
+
expect(example.steps.length).toBeGreaterThanOrEqual(2);
|
|
78
|
+
const whenStep = example.steps.find((s) => s.keyword === 'When');
|
|
79
|
+
const thenStep = example.steps.find((s) => s.keyword === 'Then');
|
|
80
|
+
expect(whenStep).toBeDefined();
|
|
81
|
+
expect(thenStep).toBeDefined();
|
|
82
|
+
if (whenStep && 'text' in whenStep) {
|
|
83
|
+
expect(whenStep.text).toBe('CreateItem');
|
|
84
|
+
expect(whenStep.docString).toMatchObject({
|
|
80
85
|
itemId: 'item_123',
|
|
81
86
|
description: 'A new item',
|
|
82
87
|
});
|
|
83
88
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
exampleData: {
|
|
89
|
+
if (thenStep && 'text' in thenStep) {
|
|
90
|
+
expect(thenStep.text).toBe('ItemCreated');
|
|
91
|
+
expect(thenStep.docString).toMatchObject({
|
|
88
92
|
id: 'item_123',
|
|
89
93
|
description: 'A new item',
|
|
90
94
|
addedAt: new Date('2024-01-15T10:00:00.000Z'),
|
|
91
|
-
}
|
|
92
|
-
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
const viewItemSlice = items.slices[1] as QuerySlice;
|
|
@@ -116,7 +120,8 @@ describe('getNarratives', (_mode) => {
|
|
|
116
120
|
expect(data[0].origin).toMatchObject({ name: 'ItemsProjection', type: 'projection' });
|
|
117
121
|
|
|
118
122
|
const specs = viewItemSlice?.server?.specs;
|
|
119
|
-
if (specs == null || specs.
|
|
123
|
+
if (specs == null || specs.length === 0 || specs[0].feature === '')
|
|
124
|
+
throw new Error('No specs found in view items slice');
|
|
120
125
|
expect(specs).toBeDefined();
|
|
121
126
|
}
|
|
122
127
|
|
|
@@ -137,28 +142,32 @@ describe('getNarratives', (_mode) => {
|
|
|
137
142
|
expect(submitOrderSlice.client.specs[0].children).toHaveLength(2);
|
|
138
143
|
}
|
|
139
144
|
expect(submitOrderSlice.server.specs).toBeDefined();
|
|
140
|
-
|
|
145
|
+
expect(Array.isArray(submitOrderSlice.server.specs)).toBe(true);
|
|
146
|
+
expect(submitOrderSlice.server.specs).toHaveLength(1);
|
|
147
|
+
const spec = submitOrderSlice.server.specs[0];
|
|
141
148
|
expect(spec.rules).toHaveLength(1);
|
|
142
149
|
const rule = spec.rules[0];
|
|
143
150
|
expect(rule.examples).toHaveLength(1);
|
|
144
151
|
const example = rule.examples[0];
|
|
145
|
-
expect(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
expect(example.steps).toBeDefined();
|
|
153
|
+
expect(example.steps.length).toBeGreaterThanOrEqual(2);
|
|
154
|
+
const whenStep = example.steps.find((s) => s.keyword === 'When');
|
|
155
|
+
const thenStep = example.steps.find((s) => s.keyword === 'Then');
|
|
156
|
+
expect(whenStep).toBeDefined();
|
|
157
|
+
expect(thenStep).toBeDefined();
|
|
158
|
+
if (whenStep && 'text' in whenStep) {
|
|
159
|
+
expect(whenStep.text).toBe('PlaceOrder');
|
|
160
|
+
expect(whenStep.docString).toMatchObject({ productId: 'product_789', quantity: 3 });
|
|
151
161
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
exampleData: {
|
|
162
|
+
if (thenStep && 'text' in thenStep) {
|
|
163
|
+
expect(thenStep.text).toBe('OrderPlaced');
|
|
164
|
+
expect(thenStep.docString).toMatchObject({
|
|
156
165
|
orderId: 'order_001',
|
|
157
166
|
productId: 'product_789',
|
|
158
167
|
quantity: 3,
|
|
159
168
|
placedAt: new Date('2024-01-20T10:00:00.000Z'),
|
|
160
|
-
}
|
|
161
|
-
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
173
|
|
|
@@ -240,16 +249,15 @@ describe('getNarratives', (_mode) => {
|
|
|
240
249
|
if (slice.type === 'react') {
|
|
241
250
|
expect(slice.server).toBeDefined();
|
|
242
251
|
expect(slice.server.specs).toBeDefined();
|
|
243
|
-
expect(
|
|
244
|
-
|
|
252
|
+
expect(Array.isArray(slice.server.specs)).toBe(true);
|
|
253
|
+
expect(slice.server.specs.length).toBeGreaterThanOrEqual(1);
|
|
254
|
+
const spec = slice.server.specs[0];
|
|
245
255
|
expect(spec.rules).toBeDefined();
|
|
246
256
|
expect(Array.isArray(spec.rules)).toBe(true);
|
|
247
257
|
spec.rules.forEach((rule) => {
|
|
248
258
|
rule.examples.forEach((example) => {
|
|
249
|
-
expect(example.
|
|
250
|
-
expect(Array.isArray(example.
|
|
251
|
-
expect(example.then).toBeDefined();
|
|
252
|
-
expect(Array.isArray(example.then)).toBe(true);
|
|
259
|
+
expect(example.steps).toBeDefined();
|
|
260
|
+
expect(Array.isArray(example.steps)).toBe(true);
|
|
253
261
|
});
|
|
254
262
|
});
|
|
255
263
|
}
|
|
@@ -311,16 +319,14 @@ describe('getNarratives', (_mode) => {
|
|
|
311
319
|
const commandSlice = testFlowWithIds.slices.find((s) => s.name === 'Create test item');
|
|
312
320
|
if (commandSlice?.type !== 'command') return;
|
|
313
321
|
|
|
314
|
-
expect(commandSlice.server.specs.rules).toHaveLength(2);
|
|
322
|
+
expect(commandSlice.server.specs[0].rules).toHaveLength(2);
|
|
315
323
|
|
|
316
|
-
const rule1 = commandSlice.server.specs.rules.find(
|
|
317
|
-
(r) => r.
|
|
324
|
+
const rule1 = commandSlice.server.specs[0].rules.find(
|
|
325
|
+
(r) => r.name === 'Valid test items should be created successfully',
|
|
318
326
|
);
|
|
319
327
|
expect(rule1?.id).toBe('RULE-001');
|
|
320
328
|
|
|
321
|
-
const rule2 = commandSlice.server.specs.rules.find(
|
|
322
|
-
(r) => r.description === 'Invalid test items should be rejected',
|
|
323
|
-
);
|
|
329
|
+
const rule2 = commandSlice.server.specs[0].rules.find((r) => r.name === 'Invalid test items should be rejected');
|
|
324
330
|
expect(rule2?.id).toBe('RULE-002');
|
|
325
331
|
});
|
|
326
332
|
|
|
@@ -334,11 +340,9 @@ describe('getNarratives', (_mode) => {
|
|
|
334
340
|
const querySlice = testFlowWithIds.slices.find((s) => s.name === 'Get test items');
|
|
335
341
|
if (querySlice?.type !== 'query') return;
|
|
336
342
|
|
|
337
|
-
expect(querySlice.server.specs.rules).toHaveLength(1);
|
|
343
|
+
expect(querySlice.server.specs[0].rules).toHaveLength(1);
|
|
338
344
|
|
|
339
|
-
const rule3 = querySlice.server.specs.rules.find(
|
|
340
|
-
(r) => r.description === 'Items should be retrievable after creation',
|
|
341
|
-
);
|
|
345
|
+
const rule3 = querySlice.server.specs[0].rules.find((r) => r.name === 'Items should be retrievable after creation');
|
|
342
346
|
expect(rule3?.id).toBe('RULE-003');
|
|
343
347
|
});
|
|
344
348
|
|
|
@@ -352,11 +356,9 @@ describe('getNarratives', (_mode) => {
|
|
|
352
356
|
const reactSlice = testFlowWithIds.slices.find((s) => s.name === 'React to test event');
|
|
353
357
|
if (reactSlice?.type !== 'react') return;
|
|
354
358
|
|
|
355
|
-
expect(reactSlice.server.specs.rules).toHaveLength(1);
|
|
359
|
+
expect(reactSlice.server.specs[0].rules).toHaveLength(1);
|
|
356
360
|
|
|
357
|
-
const rule4 = reactSlice.server.specs.rules.find(
|
|
358
|
-
(r) => r.description === 'System should react to test item creation',
|
|
359
|
-
);
|
|
361
|
+
const rule4 = reactSlice.server.specs[0].rules.find((r) => r.name === 'System should react to test item creation');
|
|
360
362
|
expect(rule4?.id).toBe('RULE-004');
|
|
361
363
|
});
|
|
362
364
|
|
|
@@ -376,16 +378,10 @@ describe('getNarratives', (_mode) => {
|
|
|
376
378
|
expect(submitSlice?.type).toBe('command');
|
|
377
379
|
|
|
378
380
|
if (submitSlice?.type === 'command') {
|
|
379
|
-
const example = submitSlice.server?.specs?.rules[0]?.examples[0];
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
typeof example.when === 'object' &&
|
|
384
|
-
example.when !== null &&
|
|
385
|
-
!Array.isArray(example.when) &&
|
|
386
|
-
'commandRef' in example.when
|
|
387
|
-
) {
|
|
388
|
-
expect(example.when.commandRef).toBe('SubmitQuestionnaire');
|
|
381
|
+
const example = submitSlice.server?.specs?.[0]?.rules[0]?.examples[0];
|
|
382
|
+
const whenStep = example?.steps?.find((s) => s.keyword === 'When');
|
|
383
|
+
if (whenStep && 'text' in whenStep) {
|
|
384
|
+
expect(whenStep.text).toBe('SubmitQuestionnaire');
|
|
389
385
|
}
|
|
390
386
|
}
|
|
391
387
|
}
|
|
@@ -408,7 +404,7 @@ describe('getNarratives', (_mode) => {
|
|
|
408
404
|
import { flow, experience, it, specs } from '@auto-engineer/narrative';
|
|
409
405
|
|
|
410
406
|
flow('Test Experience Flow', () => {
|
|
411
|
-
experience('Homepage', '
|
|
407
|
+
experience('Homepage', 'H1a4Bn6Cy').client(() => {
|
|
412
408
|
specs(() => {
|
|
413
409
|
it('show a hero section with a welcome message');
|
|
414
410
|
it('allow user to start the questionnaire');
|
|
@@ -494,8 +490,8 @@ flow('Browser Test Flow', () => {
|
|
|
494
490
|
const flowContent = `
|
|
495
491
|
import { flow, experience } from '@auto-engineer/narrative';
|
|
496
492
|
|
|
497
|
-
flow('Questionnaires', '
|
|
498
|
-
experience('Homepage', '
|
|
493
|
+
flow('Questionnaires', 'Q9m2Kp4Lx', () => {
|
|
494
|
+
experience('Homepage', 'H1a4Bn6Cy').client(() => {});
|
|
499
495
|
});
|
|
500
496
|
`;
|
|
501
497
|
|
|
@@ -644,7 +640,7 @@ flow('questionnaires-test', () => {
|
|
|
644
640
|
|
|
645
641
|
if (querySlice?.type !== 'query') return;
|
|
646
642
|
|
|
647
|
-
const example = querySlice.server.specs.rules[0]?.examples[0];
|
|
643
|
+
const example = querySlice.server.specs[0].rules[0]?.examples[0];
|
|
648
644
|
expect(example).toBeDefined();
|
|
649
645
|
|
|
650
646
|
if (example !== null && example !== undefined) {
|
|
@@ -797,44 +793,42 @@ flow('Todo List', () => {
|
|
|
797
793
|
|
|
798
794
|
if (summarySlice?.type !== 'query') return;
|
|
799
795
|
|
|
800
|
-
const example = summarySlice.server.specs.rules[0]?.examples[0];
|
|
796
|
+
const example = summarySlice.server.specs[0].rules[0]?.examples[0];
|
|
801
797
|
expect(example).toBeDefined();
|
|
802
|
-
expect(example.
|
|
803
|
-
expect(Array.isArray(example.
|
|
804
|
-
expect(example.given).toHaveLength(5);
|
|
798
|
+
expect(example.steps).toBeDefined();
|
|
799
|
+
expect(Array.isArray(example.steps)).toBe(true);
|
|
805
800
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
}
|
|
801
|
+
const givenAndSteps = example.steps.filter((s) => s.keyword === 'Given' || s.keyword === 'And');
|
|
802
|
+
expect(givenAndSteps).toHaveLength(5);
|
|
809
803
|
|
|
810
|
-
validateGivenItemsHaveEventRef(
|
|
811
|
-
validateTodoEventRefs(
|
|
804
|
+
validateGivenItemsHaveEventRef(givenAndSteps);
|
|
805
|
+
validateTodoEventRefs(givenAndSteps);
|
|
812
806
|
validateTodoMessages(model);
|
|
813
807
|
});
|
|
814
808
|
});
|
|
815
809
|
|
|
816
|
-
function validateGivenItemsHaveEventRef(
|
|
817
|
-
for (let i = 0; i <
|
|
818
|
-
const
|
|
819
|
-
if (typeof
|
|
820
|
-
expect('
|
|
821
|
-
expect('
|
|
810
|
+
function validateGivenItemsHaveEventRef(givenSteps: unknown[]): void {
|
|
811
|
+
for (let i = 0; i < givenSteps.length; i++) {
|
|
812
|
+
const step = givenSteps[i] as { keyword?: string; text?: string };
|
|
813
|
+
if (typeof step === 'object' && step !== null) {
|
|
814
|
+
expect(step.keyword === 'Given' || step.keyword === 'And').toBe(true);
|
|
815
|
+
expect('text' in step).toBe(true);
|
|
822
816
|
}
|
|
823
817
|
}
|
|
824
818
|
}
|
|
825
819
|
|
|
826
|
-
function
|
|
827
|
-
if (
|
|
828
|
-
expect(
|
|
820
|
+
function expectStepText(step: unknown, expectedType: string): void {
|
|
821
|
+
if (step !== null && step !== undefined && typeof step === 'object' && 'text' in step) {
|
|
822
|
+
expect((step as { text: string }).text).toBe(expectedType);
|
|
829
823
|
}
|
|
830
824
|
}
|
|
831
825
|
|
|
832
|
-
function validateTodoEventRefs(
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
826
|
+
function validateTodoEventRefs(givenSteps: unknown[]): void {
|
|
827
|
+
expectStepText(givenSteps[0], 'TodoAdded');
|
|
828
|
+
expectStepText(givenSteps[1], 'TodoAdded');
|
|
829
|
+
expectStepText(givenSteps[2], 'TodoAdded');
|
|
830
|
+
expectStepText(givenSteps[3], 'TodoMarkedInProgress');
|
|
831
|
+
expectStepText(givenSteps[4], 'TodoMarkedComplete');
|
|
838
832
|
}
|
|
839
833
|
|
|
840
834
|
function validateTodoMessages(model: Model): void {
|
|
@@ -859,16 +853,10 @@ function validateSubmitQuestionnaireCommand(questionnaireFlow: Narrative): void
|
|
|
859
853
|
const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits questionnaire');
|
|
860
854
|
expect(submitSlice?.type).toBe('command');
|
|
861
855
|
if (submitSlice?.type === 'command') {
|
|
862
|
-
const example = submitSlice.server?.specs?.rules[0]?.examples[0];
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
typeof example.when === 'object' &&
|
|
867
|
-
example.when !== null &&
|
|
868
|
-
!Array.isArray(example.when) &&
|
|
869
|
-
'commandRef' in example.when
|
|
870
|
-
) {
|
|
871
|
-
expect(example.when.commandRef).toBe('SubmitQuestionnaire');
|
|
856
|
+
const example = submitSlice.server?.specs?.[0]?.rules[0]?.examples[0];
|
|
857
|
+
const whenStep = example?.steps?.find((s) => s.keyword === 'When');
|
|
858
|
+
if (whenStep && 'text' in whenStep) {
|
|
859
|
+
expect(whenStep.text).toBe('SubmitQuestionnaire');
|
|
872
860
|
}
|
|
873
861
|
}
|
|
874
862
|
}
|
|
@@ -881,15 +869,11 @@ function validateQuestionAnsweredEvent(model: Model): void {
|
|
|
881
869
|
function validateGivenSectionEventRefs(questionnaireFlow: Narrative): void {
|
|
882
870
|
const viewsSlice = questionnaireFlow.slices.find((s) => s.name === 'views progress');
|
|
883
871
|
if (viewsSlice?.type === 'query') {
|
|
884
|
-
const example = viewsSlice.server?.specs?.rules[0]?.examples[0];
|
|
885
|
-
if (example?.
|
|
886
|
-
const
|
|
887
|
-
if (
|
|
888
|
-
expect(
|
|
889
|
-
expect('stateRef' in givenItem).toBe(false);
|
|
890
|
-
if ('eventRef' in givenItem) {
|
|
891
|
-
expect(givenItem.eventRef).toBe('QuestionAnswered');
|
|
892
|
-
}
|
|
872
|
+
const example = viewsSlice.server?.specs?.[0]?.rules[0]?.examples[0];
|
|
873
|
+
if (example?.steps !== undefined && Array.isArray(example.steps)) {
|
|
874
|
+
const givenStep = example.steps.find((s) => s.keyword === 'Given');
|
|
875
|
+
if (givenStep && 'text' in givenStep) {
|
|
876
|
+
expect(givenStep.text).toBe('QuestionAnswered');
|
|
893
877
|
}
|
|
894
878
|
}
|
|
895
879
|
}
|
|
@@ -903,53 +887,51 @@ function validateCurrentQuestionIdType(model: Model): void {
|
|
|
903
887
|
}
|
|
904
888
|
|
|
905
889
|
function validateMixedGivenTypes(example: Example): void {
|
|
906
|
-
expect(example.
|
|
907
|
-
expect(example.
|
|
908
|
-
expect(Array.isArray(example.
|
|
909
|
-
|
|
910
|
-
if (!example.given) return;
|
|
890
|
+
expect(example.name).toBe('system with 2 items reaches max of 2');
|
|
891
|
+
expect(example.steps).toBeDefined();
|
|
892
|
+
expect(Array.isArray(example.steps)).toBe(true);
|
|
911
893
|
|
|
912
|
-
|
|
894
|
+
const givenSteps = example.steps.filter((s) => s.keyword === 'Given' || s.keyword === 'And');
|
|
895
|
+
expect(givenSteps).toHaveLength(4);
|
|
913
896
|
|
|
914
|
-
const firstGiven =
|
|
915
|
-
expect('
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
expect(firstGiven.stateRef).toBe('ConfigState');
|
|
897
|
+
const firstGiven = givenSteps[0];
|
|
898
|
+
expect('text' in firstGiven).toBe(true);
|
|
899
|
+
if ('text' in firstGiven) {
|
|
900
|
+
expect(firstGiven.text).toBe('ConfigState');
|
|
919
901
|
}
|
|
920
902
|
|
|
921
|
-
const secondGiven =
|
|
922
|
-
expect('
|
|
923
|
-
if ('
|
|
924
|
-
expect(secondGiven.
|
|
903
|
+
const secondGiven = givenSteps[1];
|
|
904
|
+
expect('text' in secondGiven).toBe(true);
|
|
905
|
+
if ('text' in secondGiven) {
|
|
906
|
+
expect(secondGiven.text).toBe('SystemInitialized');
|
|
925
907
|
}
|
|
926
908
|
|
|
927
|
-
const thirdGiven =
|
|
928
|
-
expect('
|
|
929
|
-
if ('
|
|
930
|
-
expect(thirdGiven.
|
|
909
|
+
const thirdGiven = givenSteps[2];
|
|
910
|
+
expect('text' in thirdGiven).toBe(true);
|
|
911
|
+
if ('text' in thirdGiven) {
|
|
912
|
+
expect(thirdGiven.text).toBe('ItemAdded');
|
|
931
913
|
}
|
|
932
914
|
|
|
933
|
-
const fourthGiven =
|
|
934
|
-
expect('
|
|
935
|
-
if ('
|
|
936
|
-
expect(fourthGiven.
|
|
915
|
+
const fourthGiven = givenSteps[3];
|
|
916
|
+
expect('text' in fourthGiven).toBe(true);
|
|
917
|
+
if ('text' in fourthGiven) {
|
|
918
|
+
expect(fourthGiven.text).toBe('ItemAdded');
|
|
937
919
|
}
|
|
938
920
|
}
|
|
939
921
|
|
|
940
922
|
function validateEmptyWhenClause(example: Example): void {
|
|
941
|
-
|
|
923
|
+
const whenStep = example.steps.find((s) => s.keyword === 'When');
|
|
924
|
+
expect(whenStep).toBeUndefined();
|
|
942
925
|
}
|
|
943
926
|
|
|
944
927
|
function validateThenClause(example: Example): void {
|
|
945
|
-
|
|
946
|
-
expect(
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
expect(thenOutcome.stateRef).toBe('SystemStatus');
|
|
928
|
+
const thenSteps = example.steps.filter((s) => s.keyword === 'Then');
|
|
929
|
+
expect(thenSteps).toHaveLength(1);
|
|
930
|
+
|
|
931
|
+
const thenOutcome = thenSteps[0];
|
|
932
|
+
expect('text' in thenOutcome).toBe(true);
|
|
933
|
+
if ('text' in thenOutcome) {
|
|
934
|
+
expect(thenOutcome.text).toBe('SystemStatus');
|
|
953
935
|
}
|
|
954
936
|
}
|
|
955
937
|
|
|
@@ -1035,11 +1017,11 @@ type SubmitQuestionnaire = Command<
|
|
|
1035
1017
|
}
|
|
1036
1018
|
>;
|
|
1037
1019
|
|
|
1038
|
-
flow('Questionnaires', '
|
|
1039
|
-
command('sends the questionnaire link', '
|
|
1020
|
+
flow('Questionnaires', 'Q9m2Kp4Lx', () => {
|
|
1021
|
+
command('sends the questionnaire link', 'S2b5Cp7Dz')
|
|
1040
1022
|
.server(() => {
|
|
1041
1023
|
specs(() => {
|
|
1042
|
-
rule('questionnaire link is sent to participant', '
|
|
1024
|
+
rule('questionnaire link is sent to participant', 'r0A1Bo8X', () => {
|
|
1043
1025
|
example('sends the questionnaire link successfully')
|
|
1044
1026
|
.when<SendQuestionnaireLink>({
|
|
1045
1027
|
questionnaireId: 'q-001',
|
|
@@ -1069,10 +1051,10 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
|
1069
1051
|
});
|
|
1070
1052
|
});
|
|
1071
1053
|
|
|
1072
|
-
command('submits the questionnaire', '
|
|
1054
|
+
command('submits the questionnaire', 'T5k9Jw3V')
|
|
1073
1055
|
.server(() => {
|
|
1074
1056
|
specs(() => {
|
|
1075
|
-
rule('questionnaire allowed to be submitted when all questions are answered', '
|
|
1057
|
+
rule('questionnaire allowed to be submitted when all questions are answered', 'r4H0Lx4U', () => {
|
|
1076
1058
|
example('submits the questionnaire successfully')
|
|
1077
1059
|
.when<SubmitQuestionnaire>({
|
|
1078
1060
|
questionnaireId: 'q-001',
|
|
@@ -1122,7 +1104,7 @@ function getQuestionnaireFlowFromModel(model: Model): Narrative {
|
|
|
1122
1104
|
|
|
1123
1105
|
function getSubmitSlice(questionnaireFlow: Narrative): {
|
|
1124
1106
|
type: 'command';
|
|
1125
|
-
server: { specs: { rules: { examples: unknown[] }[] } };
|
|
1107
|
+
server: { specs: { type: 'gherkin'; feature: string; rules: { examples: unknown[] }[] }[] };
|
|
1126
1108
|
} {
|
|
1127
1109
|
const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits the questionnaire');
|
|
1128
1110
|
expect(submitSlice).toBeDefined();
|
|
@@ -1130,26 +1112,32 @@ function getSubmitSlice(questionnaireFlow: Narrative): {
|
|
|
1130
1112
|
if (submitSlice?.type !== 'command') {
|
|
1131
1113
|
throw new Error('Submit slice is not a command');
|
|
1132
1114
|
}
|
|
1133
|
-
return submitSlice as {
|
|
1115
|
+
return submitSlice as {
|
|
1116
|
+
type: 'command';
|
|
1117
|
+
server: { specs: { type: 'gherkin'; feature: string; rules: { examples: unknown[] }[] }[] };
|
|
1118
|
+
};
|
|
1134
1119
|
}
|
|
1135
1120
|
|
|
1136
|
-
function getSubmitExample(submitSlice: {
|
|
1137
|
-
|
|
1121
|
+
function getSubmitExample(submitSlice: {
|
|
1122
|
+
server: { specs: { type: 'gherkin'; feature: string; rules: { examples: unknown[] }[] }[] };
|
|
1123
|
+
}): unknown {
|
|
1124
|
+
const rule = submitSlice.server?.specs?.[0]?.rules[0];
|
|
1138
1125
|
expect(rule).toBeDefined();
|
|
1139
1126
|
expect(rule?.examples).toHaveLength(1);
|
|
1140
1127
|
const example = rule?.examples[0];
|
|
1141
|
-
expect((example as {
|
|
1128
|
+
expect((example as { name?: string })?.name).toBe('submits the questionnaire successfully');
|
|
1142
1129
|
return example;
|
|
1143
1130
|
}
|
|
1144
1131
|
|
|
1145
1132
|
function validateSubmitCommandRef(example: unknown): void {
|
|
1146
|
-
const ex = example as {
|
|
1147
|
-
expect(ex?.
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
expect(
|
|
1133
|
+
const ex = example as { steps?: { keyword: string; text?: string }[] };
|
|
1134
|
+
expect(ex?.steps).toBeDefined();
|
|
1135
|
+
const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
|
|
1136
|
+
if (whenStep && 'text' in whenStep) {
|
|
1137
|
+
expect(whenStep.text).toBe('SubmitQuestionnaire');
|
|
1138
|
+
expect(whenStep.text).not.toBe('SendQuestionnaireLink');
|
|
1151
1139
|
} else {
|
|
1152
|
-
throw new Error('Expected
|
|
1140
|
+
throw new Error('Expected steps to have a When step with text property');
|
|
1153
1141
|
}
|
|
1154
1142
|
}
|
|
1155
1143
|
|
|
@@ -1157,10 +1145,11 @@ function validateLinkSliceCommandRef(questionnaireFlow: Narrative): void {
|
|
|
1157
1145
|
const linkSlice = questionnaireFlow.slices.find((s) => s.name === 'sends the questionnaire link');
|
|
1158
1146
|
expect(linkSlice?.type).toBe('command');
|
|
1159
1147
|
if (linkSlice?.type === 'command') {
|
|
1160
|
-
const linkExample = linkSlice.server?.specs?.rules[0]?.examples[0];
|
|
1161
|
-
const ex = linkExample as {
|
|
1162
|
-
|
|
1163
|
-
|
|
1148
|
+
const linkExample = linkSlice.server?.specs?.[0]?.rules[0]?.examples[0];
|
|
1149
|
+
const ex = linkExample as { steps?: { keyword: string; text?: string }[] };
|
|
1150
|
+
const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
|
|
1151
|
+
if (whenStep && 'text' in whenStep) {
|
|
1152
|
+
expect(whenStep.text).toBe('SendQuestionnaireLink');
|
|
1164
1153
|
}
|
|
1165
1154
|
}
|
|
1166
1155
|
}
|
|
@@ -1187,21 +1176,23 @@ function getSubmitSliceFromFlow(questionnaireFlow: Narrative): unknown {
|
|
|
1187
1176
|
}
|
|
1188
1177
|
|
|
1189
1178
|
function getServerSpecsFromSlice(submitSlice: unknown): unknown {
|
|
1190
|
-
const slice = submitSlice as { server?: { specs?: unknown } };
|
|
1179
|
+
const slice = submitSlice as { server?: { specs?: unknown[] } };
|
|
1191
1180
|
const serverSpecs = slice.server?.specs;
|
|
1192
1181
|
expect(serverSpecs).toBeDefined();
|
|
1193
|
-
|
|
1194
|
-
expect(
|
|
1195
|
-
|
|
1196
|
-
|
|
1182
|
+
expect(Array.isArray(serverSpecs)).toBe(true);
|
|
1183
|
+
expect(serverSpecs).toHaveLength(1);
|
|
1184
|
+
const firstSpec = (serverSpecs as unknown[])[0] as { rules?: unknown[] };
|
|
1185
|
+
expect(firstSpec?.rules).toBeDefined();
|
|
1186
|
+
expect(firstSpec?.rules).toHaveLength(1);
|
|
1187
|
+
return firstSpec;
|
|
1197
1188
|
}
|
|
1198
1189
|
|
|
1199
1190
|
function getFirstRuleFromSpecs(serverSpecs: unknown): unknown {
|
|
1200
1191
|
const specs = serverSpecs as { rules?: unknown[] };
|
|
1201
1192
|
const rule = specs?.rules?.[0];
|
|
1202
1193
|
expect(rule).toBeDefined();
|
|
1203
|
-
const r = rule as {
|
|
1204
|
-
expect(r?.
|
|
1194
|
+
const r = rule as { name?: string; examples?: unknown[] };
|
|
1195
|
+
expect(r?.name).toBe('questionnaire allowed to be submitted when all questions are answered');
|
|
1205
1196
|
expect(r?.examples).toBeDefined();
|
|
1206
1197
|
expect(r?.examples).toHaveLength(1);
|
|
1207
1198
|
return rule;
|
|
@@ -1211,37 +1202,38 @@ function getFirstExampleFromRule(rule: unknown): unknown {
|
|
|
1211
1202
|
const r = rule as { examples?: unknown[] };
|
|
1212
1203
|
const example = r?.examples?.[0];
|
|
1213
1204
|
expect(example).toBeDefined();
|
|
1214
|
-
const ex = example as {
|
|
1215
|
-
expect(ex?.
|
|
1205
|
+
const ex = example as { name?: string };
|
|
1206
|
+
expect(ex?.name).toBe('submits the questionnaire successfully');
|
|
1216
1207
|
return example;
|
|
1217
1208
|
}
|
|
1218
1209
|
|
|
1219
1210
|
function validateExampleCommandRef(example: unknown): void {
|
|
1220
|
-
const ex = example as {
|
|
1221
|
-
expect(ex?.
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
expect(
|
|
1211
|
+
const ex = example as { steps?: Array<{ keyword: string; text?: string; docString?: unknown }> };
|
|
1212
|
+
expect(ex?.steps).toBeDefined();
|
|
1213
|
+
const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
|
|
1214
|
+
expect(whenStep).toBeDefined();
|
|
1215
|
+
if (whenStep && 'text' in whenStep) {
|
|
1216
|
+
expect(whenStep.text).toBe('SubmitQuestionnaire');
|
|
1217
|
+
expect(whenStep.text).not.toBe('SendQuestionnaireLink');
|
|
1218
|
+
expect(whenStep.docString).toEqual({
|
|
1226
1219
|
questionnaireId: 'q-001',
|
|
1227
1220
|
participantId: 'participant-abc',
|
|
1228
1221
|
});
|
|
1229
1222
|
} else {
|
|
1230
|
-
throw new Error('Expected when to have
|
|
1223
|
+
throw new Error('Expected when step to have text property');
|
|
1231
1224
|
}
|
|
1232
1225
|
}
|
|
1233
1226
|
|
|
1234
1227
|
function validateThenEvents(example: unknown): void {
|
|
1235
|
-
const ex = example as {
|
|
1236
|
-
expect(ex?.
|
|
1237
|
-
|
|
1238
|
-
expect(
|
|
1239
|
-
|
|
1240
|
-
const
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1243
|
-
expect(
|
|
1244
|
-
expect(event.exampleData).toEqual({
|
|
1228
|
+
const ex = example as { steps?: Array<{ keyword: string; text?: string; docString?: unknown }> };
|
|
1229
|
+
expect(ex?.steps).toBeDefined();
|
|
1230
|
+
const thenSteps = ex?.steps?.filter((s) => s.keyword === 'Then');
|
|
1231
|
+
expect(thenSteps).toHaveLength(1);
|
|
1232
|
+
|
|
1233
|
+
const thenStep = thenSteps?.[0];
|
|
1234
|
+
if (thenStep && 'text' in thenStep) {
|
|
1235
|
+
expect(thenStep.text).toBe('QuestionnaireSubmitted');
|
|
1236
|
+
expect(thenStep.docString).toEqual({
|
|
1245
1237
|
questionnaireId: 'q-001',
|
|
1246
1238
|
participantId: 'participant-abc',
|
|
1247
1239
|
submittedAt: new Date('2030-01-01T09:00:00.000Z'),
|