@auto-engineer/narrative 0.11.20 → 0.12.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/fluent-builder.d.ts.map +1 -1
- package/dist/src/fluent-builder.js +9 -12
- package/dist/src/fluent-builder.js.map +1 -1
- package/dist/src/getNarratives.specs.js +43 -27
- package/dist/src/getNarratives.specs.js.map +1 -1
- package/dist/src/id/addAutoIds.specs.js +11 -25
- package/dist/src/id/addAutoIds.specs.js.map +1 -1
- package/dist/src/id/hasAllIds.specs.js +12 -12
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/model-to-narrative.specs.js +110 -108
- package/dist/src/model-to-narrative.specs.js.map +1 -1
- package/dist/src/narrative-context.d.ts +4 -2
- package/dist/src/narrative-context.d.ts.map +1 -1
- package/dist/src/narrative-context.js +98 -95
- package/dist/src/narrative-context.js.map +1 -1
- package/dist/src/narrative.d.ts +7 -1
- package/dist/src/narrative.d.ts.map +1 -1
- package/dist/src/narrative.js +27 -6
- package/dist/src/narrative.js.map +1 -1
- package/dist/src/samples/items.narrative.js +7 -7
- package/dist/src/samples/items.narrative.js.map +1 -1
- package/dist/src/samples/place-order.narrative.js +4 -4
- package/dist/src/samples/place-order.narrative.js.map +1 -1
- package/dist/src/samples/questionnaires.narrative.js +16 -18
- package/dist/src/samples/questionnaires.narrative.js.map +1 -1
- package/dist/src/samples/seasonal-assistant.schema.json +37 -31
- package/dist/src/schema.d.ts +91 -462
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +22 -24
- package/dist/src/schema.js.map +1 -1
- package/dist/src/slice-builder.js +2 -2
- 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 +34 -10
- package/dist/src/transformers/model-to-narrative/generators/flow.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 +2 -1
- 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 +4 -8
- package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/type-inference.specs.js +3 -3
- 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/fluent-builder.ts +9 -12
- package/src/getNarratives.specs.ts +43 -28
- package/src/id/addAutoIds.specs.ts +11 -25
- package/src/id/hasAllIds.specs.ts +12 -12
- package/src/index.ts +2 -1
- package/src/model-to-narrative.specs.ts +110 -108
- package/src/narrative-context.ts +103 -101
- package/src/narrative.ts +44 -6
- package/src/samples/items.narrative.ts +7 -7
- package/src/samples/place-order.narrative.ts +4 -4
- package/src/samples/questionnaires.narrative.ts +17 -18
- package/src/samples/seasonal-assistant.schema.json +37 -31
- package/src/schema.ts +33 -24
- package/src/slice-builder.ts +2 -2
- package/src/transformers/model-to-narrative/generators/flow.ts +53 -23
- package/src/transformers/model-to-narrative/generators/imports.ts +2 -1
- package/src/transformers/narrative-to-model/index.ts +4 -7
- package/src/transformers/narrative-to-model/type-inference.specs.ts +3 -3
package/src/narrative-context.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import createDebug from 'debug';
|
|
2
2
|
import type { DataSinkItem, DataSourceItem, DataItem, DataSink, DataSource } from './types';
|
|
3
3
|
import type { GivenTypeInfo } from './loader/ts-utils';
|
|
4
|
-
import { Narrative, Slice, Example } from './index';
|
|
4
|
+
import { Narrative, Slice, Example, CommandSlice, QuerySlice, ExperienceSlice } from './index';
|
|
5
|
+
import type { ClientSpecNode } from './schema';
|
|
5
6
|
|
|
6
7
|
function normalizeContext(context?: Partial<Record<string, string>>): Record<string, string> | undefined {
|
|
7
8
|
if (!context) return undefined;
|
|
@@ -25,6 +26,7 @@ interface NarrativeContext {
|
|
|
25
26
|
currentSpecIndex: number | null;
|
|
26
27
|
currentRuleIndex: number | null;
|
|
27
28
|
currentExampleIndex: number | null;
|
|
29
|
+
clientSpecStack: ClientSpecNode[];
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
let context: NarrativeContext | null = null;
|
|
@@ -65,6 +67,7 @@ export function startNarrative(name: string, id?: string): Narrative {
|
|
|
65
67
|
currentSpecIndex: null,
|
|
66
68
|
currentRuleIndex: null,
|
|
67
69
|
currentExampleIndex: null,
|
|
70
|
+
clientSpecStack: [],
|
|
68
71
|
};
|
|
69
72
|
return narrative;
|
|
70
73
|
}
|
|
@@ -88,13 +91,6 @@ export function addSlice(slice: Slice): void {
|
|
|
88
91
|
context.currentSliceIndex = context.narrative.slices.length - 1;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
function getClientSpecs(slice: Slice): { name: string; rules: string[] } | undefined {
|
|
92
|
-
if (slice.type === 'command' || slice.type === 'query') {
|
|
93
|
-
return slice.client.specs;
|
|
94
|
-
}
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
94
|
function getServerSpecs(
|
|
99
95
|
slice: Slice,
|
|
100
96
|
): { name: string; rules: { id?: string; description: string; examples: Example[] }[] } | undefined {
|
|
@@ -104,21 +100,6 @@ function getServerSpecs(
|
|
|
104
100
|
return undefined;
|
|
105
101
|
}
|
|
106
102
|
|
|
107
|
-
function getCurrentSpecs(
|
|
108
|
-
slice: Slice,
|
|
109
|
-
): { name: string; rules: string[] | { id?: string; description: string; examples: Example[] }[] } | undefined {
|
|
110
|
-
if (!context?.currentSpecTarget) return undefined;
|
|
111
|
-
|
|
112
|
-
switch (context.currentSpecTarget) {
|
|
113
|
-
case 'client':
|
|
114
|
-
return getClientSpecs(slice);
|
|
115
|
-
case 'server':
|
|
116
|
-
return getServerSpecs(slice);
|
|
117
|
-
default:
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
103
|
function getCurrentExample(slice: Slice): Example | undefined {
|
|
123
104
|
if (
|
|
124
105
|
!context ||
|
|
@@ -129,39 +110,40 @@ function getCurrentExample(slice: Slice): Example | undefined {
|
|
|
129
110
|
return undefined;
|
|
130
111
|
}
|
|
131
112
|
|
|
132
|
-
const spec =
|
|
113
|
+
const spec = getServerSpecs(slice);
|
|
133
114
|
if (!spec) return undefined;
|
|
134
115
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const objectRules = spec.rules as { id?: string; description: string; examples: Example[] }[];
|
|
138
|
-
return objectRules[context.currentRuleIndex]?.examples[context.currentExampleIndex];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return undefined;
|
|
116
|
+
const objectRules = spec.rules as { id?: string; description: string; examples: Example[] }[];
|
|
117
|
+
return objectRules[context.currentRuleIndex]?.examples[context.currentExampleIndex];
|
|
142
118
|
}
|
|
143
119
|
|
|
144
|
-
export function startClientBlock(slice: Slice
|
|
120
|
+
export function startClientBlock(slice: Slice): void {
|
|
145
121
|
if (!context) throw new Error('No active flow context');
|
|
146
122
|
|
|
147
|
-
if (slice.type === 'command' || slice.type === 'query') {
|
|
148
|
-
slice.client = {
|
|
149
|
-
description,
|
|
150
|
-
specs: undefined,
|
|
151
|
-
};
|
|
152
|
-
context.currentSpecTarget = 'client';
|
|
153
|
-
} else if (slice.type === 'experience') {
|
|
123
|
+
if (slice.type === 'command' || slice.type === 'query' || slice.type === 'experience') {
|
|
154
124
|
slice.client = {
|
|
155
|
-
|
|
156
|
-
specs: undefined,
|
|
125
|
+
specs: [],
|
|
157
126
|
};
|
|
158
127
|
context.currentSpecTarget = 'client';
|
|
128
|
+
context.clientSpecStack = [];
|
|
159
129
|
}
|
|
160
130
|
}
|
|
161
131
|
|
|
162
132
|
export function endClientBlock(): void {
|
|
163
133
|
if (context) {
|
|
134
|
+
if (context.clientSpecStack.length > 0) {
|
|
135
|
+
const unclosedCount = context.clientSpecStack.length;
|
|
136
|
+
const unclosedTitles = context.clientSpecStack
|
|
137
|
+
.map((n) => {
|
|
138
|
+
if (n.title !== undefined && n.title !== '') return n.title;
|
|
139
|
+
if (n.id !== undefined && n.id !== '') return n.id;
|
|
140
|
+
return 'unnamed';
|
|
141
|
+
})
|
|
142
|
+
.join(', ');
|
|
143
|
+
throw new Error(`${unclosedCount} unclosed describe block(s): ${unclosedTitles}`);
|
|
144
|
+
}
|
|
164
145
|
context.currentSpecTarget = null;
|
|
146
|
+
context.clientSpecStack = [];
|
|
165
147
|
}
|
|
166
148
|
}
|
|
167
149
|
|
|
@@ -197,23 +179,6 @@ export function endServerBlock(): void {
|
|
|
197
179
|
}
|
|
198
180
|
}
|
|
199
181
|
|
|
200
|
-
function initializeClientSpecs(slice: Slice, description: string): void {
|
|
201
|
-
if (slice.type === 'command' || slice.type === 'query') {
|
|
202
|
-
slice.client.specs = {
|
|
203
|
-
name: description,
|
|
204
|
-
rules: [],
|
|
205
|
-
};
|
|
206
|
-
} else if (slice.type === 'experience') {
|
|
207
|
-
if (slice.client == null) {
|
|
208
|
-
slice.client = { description: '', specs: undefined };
|
|
209
|
-
}
|
|
210
|
-
slice.client.specs = {
|
|
211
|
-
name: description,
|
|
212
|
-
rules: [],
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
182
|
function initializeServerSpecs(slice: Slice, description: string): void {
|
|
218
183
|
if ('server' in slice && slice.server != null) {
|
|
219
184
|
slice.server.specs = {
|
|
@@ -229,31 +194,74 @@ export function pushSpec(description: string): void {
|
|
|
229
194
|
const slice = getCurrentSlice();
|
|
230
195
|
if (!slice) throw new Error('No active slice');
|
|
231
196
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
197
|
+
if (context.currentSpecTarget === 'server') {
|
|
198
|
+
initializeServerSpecs(slice, description);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function pushDescribe(id?: string, title?: string): void {
|
|
203
|
+
if (!context) throw new Error('No active narrative context');
|
|
204
|
+
|
|
205
|
+
const describeNode: ClientSpecNode = {
|
|
206
|
+
type: 'describe',
|
|
207
|
+
...(id !== undefined && id !== '' ? { id } : {}),
|
|
208
|
+
...(title !== undefined && title !== '' ? { title } : {}),
|
|
209
|
+
children: [],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
context.clientSpecStack.push(describeNode);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function validateSliceSupportsClientSpecs(slice: Slice): void {
|
|
216
|
+
if (slice.type !== 'command' && slice.type !== 'query' && slice.type !== 'experience') {
|
|
217
|
+
throw new Error('Client specs can only be added to command, query, or experience slices');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function addNodeToParentOrRoot(node: ClientSpecNode, slice: CommandSlice | QuerySlice | ExperienceSlice): void {
|
|
222
|
+
if (!context) return;
|
|
223
|
+
|
|
224
|
+
if (context.clientSpecStack.length === 0) {
|
|
225
|
+
slice.client.specs.push(node);
|
|
226
|
+
} else {
|
|
227
|
+
const parent = context.clientSpecStack[context.clientSpecStack.length - 1];
|
|
228
|
+
if (parent.type === 'describe') {
|
|
229
|
+
if (!parent.children) parent.children = [];
|
|
230
|
+
parent.children.push(node);
|
|
253
231
|
}
|
|
254
232
|
}
|
|
255
233
|
}
|
|
256
234
|
|
|
235
|
+
export function popDescribe(): void {
|
|
236
|
+
if (!context) throw new Error('No active narrative context');
|
|
237
|
+
if (context.clientSpecStack.length === 0) throw new Error('No active describe block');
|
|
238
|
+
|
|
239
|
+
const completedDescribe = context.clientSpecStack.pop();
|
|
240
|
+
if (!completedDescribe) return;
|
|
241
|
+
|
|
242
|
+
const slice = getCurrentSlice();
|
|
243
|
+
if (!slice) throw new Error('No active slice');
|
|
244
|
+
|
|
245
|
+
validateSliceSupportsClientSpecs(slice);
|
|
246
|
+
addNodeToParentOrRoot(completedDescribe, slice as CommandSlice | QuerySlice | ExperienceSlice);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function recordIt(id?: string, title: string = ''): void {
|
|
250
|
+
if (!context) throw new Error('No active narrative context');
|
|
251
|
+
|
|
252
|
+
const itNode: ClientSpecNode = {
|
|
253
|
+
type: 'it',
|
|
254
|
+
...(id !== undefined && id !== '' ? { id } : {}),
|
|
255
|
+
title,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const slice = getCurrentSlice();
|
|
259
|
+
if (!slice) throw new Error('No active slice');
|
|
260
|
+
|
|
261
|
+
validateSliceSupportsClientSpecs(slice);
|
|
262
|
+
addNodeToParentOrRoot(itNode, slice as CommandSlice | QuerySlice | ExperienceSlice);
|
|
263
|
+
}
|
|
264
|
+
|
|
257
265
|
export function setQueryRequest(request: string): void {
|
|
258
266
|
const slice = getCurrentSlice();
|
|
259
267
|
if (!slice || slice.type !== 'query') throw new Error('Request can only be set on query slices');
|
|
@@ -290,19 +298,16 @@ export function recordRule(description: string, id?: string): void {
|
|
|
290
298
|
const slice = getCurrentSlice();
|
|
291
299
|
if (!slice) throw new Error('No active slice');
|
|
292
300
|
|
|
293
|
-
const spec =
|
|
301
|
+
const spec = getServerSpecs(slice);
|
|
294
302
|
if (!spec) throw new Error('No active specs for current slice');
|
|
295
303
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
});
|
|
304
|
-
context.currentRuleIndex = objectRules.length - 1;
|
|
305
|
-
}
|
|
304
|
+
const objectRules = spec.rules as { id?: string; description: string; examples: Example[] }[];
|
|
305
|
+
objectRules.push({
|
|
306
|
+
id,
|
|
307
|
+
description,
|
|
308
|
+
examples: [],
|
|
309
|
+
});
|
|
310
|
+
context.currentRuleIndex = objectRules.length - 1;
|
|
306
311
|
}
|
|
307
312
|
|
|
308
313
|
export function recordExample(description: string): void {
|
|
@@ -312,19 +317,16 @@ export function recordExample(description: string): void {
|
|
|
312
317
|
const slice = getCurrentSlice();
|
|
313
318
|
if (!slice) throw new Error('No active slice');
|
|
314
319
|
|
|
315
|
-
const spec =
|
|
320
|
+
const spec = getServerSpecs(slice);
|
|
316
321
|
if (!spec) throw new Error('No active specs for current slice');
|
|
317
322
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
});
|
|
326
|
-
context.currentExampleIndex = rule.examples.length - 1;
|
|
327
|
-
}
|
|
323
|
+
const objectRules = spec.rules as { id?: string; description: string; examples: Example[] }[];
|
|
324
|
+
const rule = objectRules[context.currentRuleIndex];
|
|
325
|
+
rule.examples.push({
|
|
326
|
+
description,
|
|
327
|
+
then: [],
|
|
328
|
+
});
|
|
329
|
+
context.currentExampleIndex = rule.examples.length - 1;
|
|
328
330
|
}
|
|
329
331
|
|
|
330
332
|
function processItemWithASTMatch(
|
package/src/narrative.ts
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
startServerBlock,
|
|
9
9
|
endServerBlock,
|
|
10
10
|
pushSpec,
|
|
11
|
-
|
|
11
|
+
pushDescribe,
|
|
12
|
+
popDescribe,
|
|
13
|
+
recordIt,
|
|
12
14
|
setSliceData,
|
|
13
15
|
recordRule,
|
|
14
16
|
recordExample,
|
|
@@ -45,7 +47,7 @@ export function narrative(name: string, idOrFn: string | (() => void), fn?: () =
|
|
|
45
47
|
export const client = (fn: () => void) => {
|
|
46
48
|
const slice = getCurrentSlice();
|
|
47
49
|
if (slice) {
|
|
48
|
-
startClientBlock(slice
|
|
50
|
+
startClientBlock(slice);
|
|
49
51
|
fn();
|
|
50
52
|
endClientBlock();
|
|
51
53
|
}
|
|
@@ -64,9 +66,46 @@ export const request = (_query: unknown) => ({
|
|
|
64
66
|
with: (..._dependencies: unknown[]) => {},
|
|
65
67
|
});
|
|
66
68
|
|
|
67
|
-
export
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
export function describe(fn: () => void): void;
|
|
70
|
+
export function describe(title: string, fn: () => void): void;
|
|
71
|
+
export function describe(id: string, title: string, fn: () => void): void;
|
|
72
|
+
export function describe(
|
|
73
|
+
idOrTitleOrFn: string | (() => void),
|
|
74
|
+
titleOrFn?: string | (() => void),
|
|
75
|
+
fn?: () => void,
|
|
76
|
+
): void {
|
|
77
|
+
if (typeof idOrTitleOrFn === 'function') {
|
|
78
|
+
const slice = getCurrentSlice();
|
|
79
|
+
const inferredTitle = slice?.name ?? '';
|
|
80
|
+
pushDescribe(undefined, inferredTitle);
|
|
81
|
+
idOrTitleOrFn();
|
|
82
|
+
popDescribe();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const hasId = typeof titleOrFn === 'string';
|
|
87
|
+
const id = hasId ? idOrTitleOrFn : undefined;
|
|
88
|
+
const title = hasId ? titleOrFn : idOrTitleOrFn;
|
|
89
|
+
const callback = hasId ? fn! : (titleOrFn as () => void);
|
|
90
|
+
|
|
91
|
+
pushDescribe(id, title);
|
|
92
|
+
callback();
|
|
93
|
+
popDescribe();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function it(title: string): void;
|
|
97
|
+
export function it(id: string, title: string): void;
|
|
98
|
+
export function it(idOrTitle: string, title?: string): void {
|
|
99
|
+
const hasId = title !== undefined;
|
|
100
|
+
recordIt(hasId ? idOrTitle : undefined, hasId ? title : idOrTitle);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function should(title: string): void;
|
|
104
|
+
export function should(id: string, title: string): void;
|
|
105
|
+
export function should(idOrTitle: string, title?: string): void {
|
|
106
|
+
const hasId = title !== undefined;
|
|
107
|
+
recordIt(hasId ? idOrTitle : undefined, hasId ? title : idOrTitle);
|
|
108
|
+
}
|
|
70
109
|
|
|
71
110
|
export function specs(description: string, fn: () => void): void;
|
|
72
111
|
export function specs(fn: () => void): void;
|
|
@@ -75,7 +114,6 @@ export function specs(descriptionOrFn: string | (() => void), fn?: () => void):
|
|
|
75
114
|
const callback = typeof descriptionOrFn === 'function' ? descriptionOrFn : fn!;
|
|
76
115
|
|
|
77
116
|
pushSpec(description);
|
|
78
|
-
recordShouldBlock();
|
|
79
117
|
callback();
|
|
80
118
|
}
|
|
81
119
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { data, flow,
|
|
1
|
+
import { data, flow, describe, it, specs, rule, example } from '../narrative';
|
|
2
2
|
import { command, query } from '../fluent-builder';
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
4
|
import { source } from '../data-narrative-builders';
|
|
@@ -33,8 +33,8 @@ flow('items', () => {
|
|
|
33
33
|
command('Create item')
|
|
34
34
|
.stream('item-${id}')
|
|
35
35
|
.client(() => {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
describe('A form that allows users to add items', () => {
|
|
37
|
+
it('have fields for id and description');
|
|
38
38
|
});
|
|
39
39
|
})
|
|
40
40
|
.server(() => {
|
|
@@ -64,10 +64,10 @@ flow('items', () => {
|
|
|
64
64
|
}
|
|
65
65
|
`)
|
|
66
66
|
.client(() => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
describe('view Items Screen', () => {
|
|
68
|
+
it('display all items');
|
|
69
|
+
it('show quantity selectors for each item');
|
|
70
|
+
it('allow removing items');
|
|
71
71
|
});
|
|
72
72
|
})
|
|
73
73
|
.server(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { flow,
|
|
1
|
+
import { flow, describe, it, specs, rule, example } from '../narrative';
|
|
2
2
|
import { command } from '../fluent-builder';
|
|
3
3
|
|
|
4
4
|
export interface OrderPlaced {
|
|
@@ -32,9 +32,9 @@ flow('Place order', () => {
|
|
|
32
32
|
command('Submit order')
|
|
33
33
|
.stream('order-${orderId}')
|
|
34
34
|
.client(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
describe('Order submission form', () => {
|
|
36
|
+
it('allow product selection');
|
|
37
|
+
it('allow quantity input');
|
|
38
38
|
});
|
|
39
39
|
})
|
|
40
40
|
.server(() => {
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
query,
|
|
4
4
|
experience,
|
|
5
5
|
flow,
|
|
6
|
-
|
|
6
|
+
it,
|
|
7
|
+
describe,
|
|
7
8
|
specs,
|
|
8
9
|
rule,
|
|
9
10
|
example,
|
|
@@ -96,10 +97,8 @@ type QuestionnaireProgress = State<
|
|
|
96
97
|
|
|
97
98
|
flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
98
99
|
experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
should('allow user to start the questionnaire');
|
|
102
|
-
});
|
|
100
|
+
it('show a hero section with a welcome message');
|
|
101
|
+
it('allow user to start the questionnaire');
|
|
103
102
|
});
|
|
104
103
|
|
|
105
104
|
query('views the questionnaire', 'AUTO-V7n8Rq5M')
|
|
@@ -163,11 +162,11 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
|
163
162
|
}
|
|
164
163
|
`)
|
|
165
164
|
.client(() => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
describe('Questionnaire Progress', () => {
|
|
166
|
+
it('focus on the current question based on the progress state');
|
|
167
|
+
it('display the list of answered questions');
|
|
168
|
+
it('display the list of remaining questions');
|
|
169
|
+
it('show a progress indicator that is always visible as the user scrolls');
|
|
171
170
|
});
|
|
172
171
|
});
|
|
173
172
|
|
|
@@ -223,9 +222,9 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
|
223
222
|
}
|
|
224
223
|
`)
|
|
225
224
|
.client(() => {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
describe('Submissions', () => {
|
|
226
|
+
it('displays a success message when the answer is submitted');
|
|
227
|
+
it('display an error message when the answer submission is rejected');
|
|
229
228
|
});
|
|
230
229
|
});
|
|
231
230
|
|
|
@@ -314,9 +313,9 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
|
314
313
|
}
|
|
315
314
|
`)
|
|
316
315
|
.client(() => {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
describe('Submission Readiness', () => {
|
|
317
|
+
it('enable the submit button when all questions are answered');
|
|
318
|
+
it('disable the submit button when all questions have not been answered');
|
|
320
319
|
});
|
|
321
320
|
});
|
|
322
321
|
|
|
@@ -346,8 +345,8 @@ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
|
|
|
346
345
|
}
|
|
347
346
|
`)
|
|
348
347
|
.client(() => {
|
|
349
|
-
|
|
350
|
-
|
|
348
|
+
describe('Submission Confirmation', () => {
|
|
349
|
+
it('display a confirmation message upon successful submission');
|
|
351
350
|
});
|
|
352
351
|
});
|
|
353
352
|
});
|
|
@@ -8,18 +8,20 @@
|
|
|
8
8
|
"name": "enters shopping criteria into assistant",
|
|
9
9
|
"type": "command",
|
|
10
10
|
"client": {
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
"specs": [
|
|
12
|
+
{
|
|
13
|
+
"type": "describe",
|
|
14
|
+
"title": "Assistant Chat Interface",
|
|
15
|
+
"children": [
|
|
16
|
+
{ "type": "it", "title": "allow shopper to describe their shopping needs in natural language" },
|
|
17
|
+
{ "type": "it", "title": "provide a text input for entering criteria" },
|
|
18
|
+
{ "type": "it", "title": "show examples of what to include (age, interests, budget)" },
|
|
19
|
+
{ "type": "it", "title": "show a button to submit the criteria" },
|
|
20
|
+
{ "type": "it", "title": "generate a persisted session id for a visit" },
|
|
21
|
+
{ "type": "it", "title": "show the header on top of the page" }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
23
25
|
},
|
|
24
26
|
"request": "mutation EnterShoppingCriteria($input: EnterShoppingCriteriaInput!) {\n enterShoppingCriteria(input: $input) {\n success\n error {\n type\n message\n }\n }\n}",
|
|
25
27
|
"server": {
|
|
@@ -250,16 +252,18 @@
|
|
|
250
252
|
"name": "views suggested items",
|
|
251
253
|
"type": "query",
|
|
252
254
|
"client": {
|
|
253
|
-
"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
"
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
"specs": [
|
|
256
|
+
{
|
|
257
|
+
"type": "describe",
|
|
258
|
+
"title": "Suggested Items Screen",
|
|
259
|
+
"children": [
|
|
260
|
+
{ "type": "it", "title": "display all suggested items with names and reasons" },
|
|
261
|
+
{ "type": "it", "title": "show quantity selectors for each item" },
|
|
262
|
+
{ "type": "it", "title": "have an \"Add to Cart\" button for selected items" },
|
|
263
|
+
{ "type": "it", "title": "allow removing items from the suggestions" }
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
]
|
|
263
267
|
},
|
|
264
268
|
"request": "query GetSuggestedItems($sessionId: ID!) {\n suggestedItems(sessionId: $sessionId) {\n items {\n productId\n name\n quantity\n reason\n }\n }\n}",
|
|
265
269
|
"server": {
|
|
@@ -362,15 +366,17 @@
|
|
|
362
366
|
"name": "accepts items and adds to their cart",
|
|
363
367
|
"type": "command",
|
|
364
368
|
"client": {
|
|
365
|
-
"
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
"specs": [
|
|
370
|
+
{
|
|
371
|
+
"type": "describe",
|
|
372
|
+
"title": "Suggested Items Screen",
|
|
373
|
+
"children": [
|
|
374
|
+
{ "type": "it", "title": "allow selecting specific items to add" },
|
|
375
|
+
{ "type": "it", "title": "update quantities before adding to cart" },
|
|
376
|
+
{ "type": "it", "title": "provide feedback when items are added" }
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
]
|
|
374
380
|
},
|
|
375
381
|
"server": {
|
|
376
382
|
"description": "",
|