@auto-engineer/narrative 0.13.0 → 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 +11 -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 +149 -153
- 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 +54 -45
- package/dist/src/id/addAutoIds.specs.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 +6 -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 +531 -449
- 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/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 +168 -176
- package/src/id/addAutoIds.specs.ts +54 -47
- package/src/id/addAutoIds.ts +28 -11
- package/src/id/hasAllIds.specs.ts +147 -245
- package/src/id/hasAllIds.ts +11 -4
- package/src/index.ts +9 -12
- package/src/loader/graph.ts +23 -6
- package/src/loader/ts-utils.ts +169 -26
- package/src/model-to-narrative.specs.ts +531 -449
- package/src/narrative-context.specs.ts +73 -116
- package/src/narrative-context.ts +127 -374
- package/src/narrative.ts +70 -120
- 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
|
@@ -15,20 +15,23 @@ describe('addAutoIds', () => {
|
|
|
15
15
|
client: { specs: [] },
|
|
16
16
|
server: {
|
|
17
17
|
description: 'Test server',
|
|
18
|
-
specs:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
specs: [
|
|
19
|
+
{
|
|
20
|
+
type: 'gherkin',
|
|
21
|
+
feature: 'Test Specs',
|
|
22
|
+
rules: [
|
|
23
|
+
{
|
|
24
|
+
name: 'Test rule without ID',
|
|
25
|
+
examples: [],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'EXISTING-RULE-001',
|
|
29
|
+
name: 'Test rule with existing ID',
|
|
30
|
+
examples: [],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
{
|
|
@@ -38,10 +41,13 @@ describe('addAutoIds', () => {
|
|
|
38
41
|
client: { specs: [] },
|
|
39
42
|
server: {
|
|
40
43
|
description: 'Test server',
|
|
41
|
-
specs:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
specs: [
|
|
45
|
+
{
|
|
46
|
+
type: 'gherkin',
|
|
47
|
+
feature: 'Test Specs',
|
|
48
|
+
rules: [],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
45
51
|
},
|
|
46
52
|
},
|
|
47
53
|
],
|
|
@@ -54,16 +60,18 @@ describe('addAutoIds', () => {
|
|
|
54
60
|
type: 'react',
|
|
55
61
|
name: 'React Slice',
|
|
56
62
|
server: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
specs: [
|
|
64
|
+
{
|
|
65
|
+
type: 'gherkin',
|
|
66
|
+
feature: 'React Specs',
|
|
67
|
+
rules: [
|
|
68
|
+
{
|
|
69
|
+
name: 'React rule',
|
|
70
|
+
examples: [],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
67
75
|
},
|
|
68
76
|
},
|
|
69
77
|
],
|
|
@@ -86,13 +94,13 @@ describe('addAutoIds', () => {
|
|
|
86
94
|
const slice0 = result.narratives[0].slices[0];
|
|
87
95
|
const slice1 = result.narratives[1].slices[0];
|
|
88
96
|
|
|
89
|
-
if ('server' in slice0 && slice0.server?.specs
|
|
90
|
-
expect(slice0.server.specs.rules[0].id).toMatch(AUTO_ID_REGEX);
|
|
91
|
-
expect(slice0.server.specs.rules[1].id).toBe('EXISTING-RULE-001');
|
|
97
|
+
if ('server' in slice0 && slice0.server?.specs != null && Array.isArray(slice0.server.specs)) {
|
|
98
|
+
expect(slice0.server.specs[0].rules[0].id).toMatch(AUTO_ID_REGEX);
|
|
99
|
+
expect(slice0.server.specs[0].rules[1].id).toBe('EXISTING-RULE-001');
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
if ('server' in slice1 && slice1.server?.specs
|
|
95
|
-
expect(slice1.server.specs.rules[0].id).toMatch(AUTO_ID_REGEX);
|
|
102
|
+
if ('server' in slice1 && slice1.server?.specs != null && Array.isArray(slice1.server.specs)) {
|
|
103
|
+
expect(slice1.server.specs[0].rules[0].id).toMatch(AUTO_ID_REGEX);
|
|
96
104
|
}
|
|
97
105
|
});
|
|
98
106
|
|
|
@@ -106,10 +114,11 @@ describe('addAutoIds', () => {
|
|
|
106
114
|
expect(originalSlice.id).toBeUndefined();
|
|
107
115
|
if (
|
|
108
116
|
'server' in originalSlice &&
|
|
109
|
-
originalSlice.server?.specs
|
|
110
|
-
originalSlice.server.specs
|
|
117
|
+
originalSlice.server?.specs !== undefined &&
|
|
118
|
+
Array.isArray(originalSlice.server.specs) &&
|
|
119
|
+
originalSlice.server.specs.length > 0
|
|
111
120
|
) {
|
|
112
|
-
expect(originalSlice.server.specs.rules[0].id).toBeUndefined();
|
|
121
|
+
expect(originalSlice.server.specs[0].rules[0].id).toBeUndefined();
|
|
113
122
|
}
|
|
114
123
|
});
|
|
115
124
|
|
|
@@ -120,8 +129,8 @@ describe('addAutoIds', () => {
|
|
|
120
129
|
expect(result.narratives[0].slices[1].id).toBe('EXISTING-SLICE-001');
|
|
121
130
|
|
|
122
131
|
const testSlice = result.narratives[0].slices[0];
|
|
123
|
-
if ('server' in testSlice && testSlice.server?.specs
|
|
124
|
-
expect(testSlice.server.specs.rules[1].id).toBe('EXISTING-RULE-001');
|
|
132
|
+
if ('server' in testSlice && testSlice.server?.specs != null && Array.isArray(testSlice.server.specs)) {
|
|
133
|
+
expect(testSlice.server.specs[0].rules[1].id).toBe('EXISTING-RULE-001');
|
|
125
134
|
}
|
|
126
135
|
});
|
|
127
136
|
|
|
@@ -138,10 +147,13 @@ describe('addAutoIds', () => {
|
|
|
138
147
|
client: { specs: [] },
|
|
139
148
|
server: {
|
|
140
149
|
description: 'Simple server',
|
|
141
|
-
specs:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
specs: [
|
|
151
|
+
{
|
|
152
|
+
type: 'gherkin',
|
|
153
|
+
feature: 'Simple specs',
|
|
154
|
+
rules: [],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
145
157
|
},
|
|
146
158
|
},
|
|
147
159
|
],
|
|
@@ -199,14 +211,9 @@ describe('addAutoIds', () => {
|
|
|
199
211
|
|
|
200
212
|
const result = addAutoIds(modelWithExperienceSlice);
|
|
201
213
|
|
|
202
|
-
// Flow should get an auto ID
|
|
203
214
|
expect(result.narratives[0].id).toMatch(AUTO_ID_REGEX);
|
|
204
|
-
|
|
205
|
-
// Experience slices should get auto IDs where missing
|
|
206
215
|
expect(result.narratives[0].slices[0].id).toMatch(AUTO_ID_REGEX);
|
|
207
216
|
expect(result.narratives[0].slices[1].id).toBe('EXISTING-EXPERIENCE-SLICE-001');
|
|
208
|
-
|
|
209
|
-
// Experience slices only have client specs (no server specs to test)
|
|
210
217
|
});
|
|
211
218
|
|
|
212
219
|
it('should assign unique IDs to multiple flows with same sourceFile', () => {
|
package/src/id/addAutoIds.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { generateAutoId } from './generators';
|
|
2
|
-
import { Model, Slice } from '../index';
|
|
2
|
+
import { Model, Slice, Spec, Rule, Example } from '../index';
|
|
3
3
|
|
|
4
4
|
function ensureId(item: { id?: string }): void {
|
|
5
5
|
if (item.id === undefined || item.id === '') {
|
|
@@ -7,23 +7,40 @@ function ensureId(item: { id?: string }): void {
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function processExamples(examples: Example[]): Example[] {
|
|
11
|
+
return examples.map((example) => {
|
|
12
|
+
const exampleCopy = { ...example };
|
|
13
|
+
ensureId(exampleCopy);
|
|
14
|
+
return exampleCopy;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function processRules(rules: Rule[]): Rule[] {
|
|
11
19
|
return rules.map((rule) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
return rule;
|
|
20
|
+
const ruleCopy = { ...rule };
|
|
21
|
+
ensureId(ruleCopy);
|
|
22
|
+
ruleCopy.examples = processExamples(rule.examples);
|
|
23
|
+
return ruleCopy;
|
|
18
24
|
});
|
|
19
25
|
}
|
|
20
26
|
|
|
27
|
+
function processSpecs(specs: Spec[]): Spec[] {
|
|
28
|
+
return specs.map((spec) => ({
|
|
29
|
+
...spec,
|
|
30
|
+
rules: processRules(spec.rules),
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
function processServerSpecs(slice: Slice): Slice {
|
|
22
|
-
if (!('server' in slice) || slice.server?.specs
|
|
35
|
+
if (!('server' in slice) || slice.server?.specs === undefined || !Array.isArray(slice.server.specs)) return slice;
|
|
23
36
|
|
|
24
37
|
const modifiedSlice = structuredClone(slice);
|
|
25
|
-
if (
|
|
26
|
-
|
|
38
|
+
if (
|
|
39
|
+
'server' in modifiedSlice &&
|
|
40
|
+
modifiedSlice.server?.specs !== undefined &&
|
|
41
|
+
Array.isArray(modifiedSlice.server.specs)
|
|
42
|
+
) {
|
|
43
|
+
modifiedSlice.server.specs = processSpecs(modifiedSlice.server.specs);
|
|
27
44
|
}
|
|
28
45
|
return modifiedSlice;
|
|
29
46
|
}
|
|
@@ -1,267 +1,169 @@
|
|
|
1
|
-
import { describe, expect, it
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { hasAllIds, addAutoIds } from './index';
|
|
3
|
-
import {
|
|
4
|
-
import { InMemoryFileStore, type IFileStore } from '@auto-engineer/file-store';
|
|
5
|
-
import * as flowApi from '../narrative';
|
|
6
|
-
import * as fluent from '../fluent-builder';
|
|
7
|
-
import * as dataBuilders from '../data-narrative-builders';
|
|
8
|
-
import * as typesApi from '../types';
|
|
9
|
-
import gql from 'graphql-tag';
|
|
10
|
-
|
|
11
|
-
const importMap = {
|
|
12
|
-
'../flow': flowApi,
|
|
13
|
-
'../fluent-builder': fluent,
|
|
14
|
-
'../data-flow-builders': dataBuilders,
|
|
15
|
-
'../types': typesApi,
|
|
16
|
-
'graphql-tag': gql,
|
|
17
|
-
};
|
|
3
|
+
import type { Model } from '../index';
|
|
18
4
|
|
|
19
5
|
describe('hasAllIds', () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const flowContent1 = new TextEncoder().encode(flowWithoutIds);
|
|
145
|
-
const flowContent2 = new TextEncoder().encode(flowWithIds);
|
|
146
|
-
const flowContent3 = new TextEncoder().encode(multipleFlowsSameSource);
|
|
147
|
-
const flowContent4 = new TextEncoder().encode(multipleFlowsIncomplete);
|
|
148
|
-
const flowContent5 = new TextEncoder().encode(multipleFlowsSliceMissing);
|
|
149
|
-
await vfs.write('/test/flow-without-ids.narrative.ts', flowContent1);
|
|
150
|
-
await vfs.write('/test/flow-with-ids.narrative.ts', flowContent2);
|
|
151
|
-
await vfs.write('/test/homepage.narrative.ts', flowContent3);
|
|
152
|
-
await vfs.write('/test/homepage-incomplete.narrative.ts', flowContent4);
|
|
153
|
-
await vfs.write('/test/homepage-slice-missing.narrative.ts', flowContent5);
|
|
154
|
-
});
|
|
155
|
-
it('should return false for models without IDs', async () => {
|
|
156
|
-
const result = await getNarratives({ vfs, root, pattern: /\.(narrative)\.(ts)$/, fastFsScan: true, importMap });
|
|
157
|
-
const model = result.toModel();
|
|
158
|
-
|
|
159
|
-
const flowWithoutIds = model.narratives.find((f) => f.name === 'Test Flow Without IDs');
|
|
160
|
-
expect(flowWithoutIds).toBeDefined();
|
|
161
|
-
|
|
162
|
-
if (flowWithoutIds) {
|
|
163
|
-
const modelWithoutIds = { ...model, narratives: [flowWithoutIds] };
|
|
164
|
-
expect(hasAllIds(modelWithoutIds)).toBe(false);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should return true for models with complete IDs', async () => {
|
|
169
|
-
const result = await getNarratives({ vfs, root, pattern: /\.(narrative)\.(ts)$/, fastFsScan: true, importMap });
|
|
170
|
-
const model = result.toModel();
|
|
171
|
-
|
|
6
|
+
const createModelWithoutIds = (): Model => ({
|
|
7
|
+
variant: 'specs',
|
|
8
|
+
narratives: [
|
|
9
|
+
{
|
|
10
|
+
name: 'Test Flow Without IDs',
|
|
11
|
+
slices: [
|
|
12
|
+
{
|
|
13
|
+
type: 'command',
|
|
14
|
+
name: 'Test slice without ID',
|
|
15
|
+
client: { specs: [] },
|
|
16
|
+
server: {
|
|
17
|
+
description: 'Test server',
|
|
18
|
+
specs: [
|
|
19
|
+
{
|
|
20
|
+
type: 'gherkin',
|
|
21
|
+
feature: 'Test specs',
|
|
22
|
+
rules: [
|
|
23
|
+
{
|
|
24
|
+
name: 'Test rule without ID',
|
|
25
|
+
examples: [],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
messages: [],
|
|
36
|
+
integrations: [],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const createModelWithIds = (): Model => ({
|
|
40
|
+
variant: 'specs',
|
|
41
|
+
narratives: [
|
|
42
|
+
{
|
|
43
|
+
name: 'Test Flow with IDs',
|
|
44
|
+
id: 'FLOW-001',
|
|
45
|
+
slices: [
|
|
46
|
+
{
|
|
47
|
+
type: 'command',
|
|
48
|
+
name: 'Test slice with ID',
|
|
49
|
+
id: 'SLICE-001',
|
|
50
|
+
client: { specs: [] },
|
|
51
|
+
server: {
|
|
52
|
+
description: 'Test server',
|
|
53
|
+
specs: [
|
|
54
|
+
{
|
|
55
|
+
type: 'gherkin',
|
|
56
|
+
feature: 'Test specs',
|
|
57
|
+
rules: [
|
|
58
|
+
{
|
|
59
|
+
id: 'RULE-001',
|
|
60
|
+
name: 'Test rule with ID',
|
|
61
|
+
examples: [],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
messages: [],
|
|
72
|
+
integrations: [],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const createMultipleFlowsModel = (includeAllIds: boolean, includeAllSliceIds: boolean): Model => ({
|
|
76
|
+
variant: 'specs',
|
|
77
|
+
narratives: [
|
|
78
|
+
{
|
|
79
|
+
name: 'Home Screen',
|
|
80
|
+
id: 'aifPcU3hw',
|
|
81
|
+
sourceFile: '/path/to/homepage.narrative.ts',
|
|
82
|
+
slices: [
|
|
83
|
+
{
|
|
84
|
+
name: 'Active Surveys Summary',
|
|
85
|
+
id: 'slice1',
|
|
86
|
+
type: 'experience',
|
|
87
|
+
client: { specs: [{ type: 'it', title: 'show active surveys summary' }] },
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'Create Survey',
|
|
93
|
+
id: includeAllIds ? 'MPviTMrQC' : undefined,
|
|
94
|
+
sourceFile: '/path/to/homepage.narrative.ts',
|
|
95
|
+
slices: [
|
|
96
|
+
{
|
|
97
|
+
name: 'Create Survey Form',
|
|
98
|
+
id: includeAllSliceIds ? 'slice2' : undefined,
|
|
99
|
+
type: 'experience',
|
|
100
|
+
client: { specs: [{ type: 'it', title: 'allow entering survey title' }] },
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'Response Analytics',
|
|
106
|
+
id: 'eME978Euk',
|
|
107
|
+
sourceFile: '/path/to/homepage.narrative.ts',
|
|
108
|
+
slices: [
|
|
109
|
+
{
|
|
110
|
+
name: 'Response Rate Charts',
|
|
111
|
+
id: 'slice3',
|
|
112
|
+
type: 'experience',
|
|
113
|
+
client: { specs: [{ type: 'it', title: 'show daily response rate charts' }] },
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
messages: [],
|
|
119
|
+
integrations: [],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should return false for models without IDs', () => {
|
|
123
|
+
const model = createModelWithoutIds();
|
|
124
|
+
expect(hasAllIds(model)).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return true for models with complete IDs', () => {
|
|
128
|
+
const model = createModelWithoutIds();
|
|
172
129
|
const modelWithIds = addAutoIds(model);
|
|
173
130
|
expect(hasAllIds(modelWithIds)).toBe(true);
|
|
174
131
|
});
|
|
175
132
|
|
|
176
|
-
it('should return true for flows that already have IDs',
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const testFlowWithIds = model.narratives.find((f) => f.name === 'Test Flow with IDs');
|
|
181
|
-
expect(testFlowWithIds).toBeDefined();
|
|
182
|
-
|
|
183
|
-
if (testFlowWithIds) {
|
|
184
|
-
const modelWithExistingIds = { ...model, narratives: [testFlowWithIds] };
|
|
185
|
-
expect(hasAllIds(modelWithExistingIds)).toBe(true);
|
|
186
|
-
}
|
|
133
|
+
it('should return true for flows that already have IDs', () => {
|
|
134
|
+
const model = createModelWithIds();
|
|
135
|
+
expect(hasAllIds(model)).toBe(true);
|
|
187
136
|
});
|
|
188
137
|
|
|
189
|
-
it('should return false if any slice is missing an ID',
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const modelWithIds = addAutoIds(model);
|
|
194
|
-
expect(modelWithIds.narratives.length).toBeGreaterThan(0);
|
|
195
|
-
expect(modelWithIds.narratives[0].slices.length).toBeGreaterThan(0);
|
|
196
|
-
|
|
197
|
-
const modifiedModel = structuredClone(modelWithIds);
|
|
138
|
+
it('should return false if any slice is missing an ID', () => {
|
|
139
|
+
const model = createModelWithIds();
|
|
140
|
+
const modifiedModel = structuredClone(model);
|
|
198
141
|
modifiedModel.narratives[0].slices[0].id = '';
|
|
199
142
|
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
200
143
|
});
|
|
201
144
|
|
|
202
|
-
it('should return false if any rule is missing an ID',
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
let found = false;
|
|
209
|
-
for (const flow of modelWithIds.narratives) {
|
|
210
|
-
for (const slice of flow.slices) {
|
|
211
|
-
if ('server' in slice && slice.server?.specs?.rules !== undefined && slice.server.specs.rules.length > 0) {
|
|
212
|
-
const modifiedModel = structuredClone(modelWithIds);
|
|
213
|
-
const modifiedFlow = modifiedModel.narratives.find((f) => f.name === flow.name);
|
|
214
|
-
const modifiedSlice = modifiedFlow?.slices.find((s) => s.name === slice.name);
|
|
215
|
-
if (modifiedSlice && 'server' in modifiedSlice && modifiedSlice.server?.specs?.rules !== undefined) {
|
|
216
|
-
modifiedSlice.server.specs.rules[0].id = '';
|
|
217
|
-
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
218
|
-
found = true;
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (found) break;
|
|
145
|
+
it('should return false if any rule is missing an ID', () => {
|
|
146
|
+
const model = createModelWithIds();
|
|
147
|
+
const modifiedModel = structuredClone(model);
|
|
148
|
+
const slice = modifiedModel.narratives[0].slices[0];
|
|
149
|
+
if ('server' in slice && slice.server?.specs !== undefined && Array.isArray(slice.server.specs)) {
|
|
150
|
+
slice.server.specs[0].rules[0].id = '';
|
|
224
151
|
}
|
|
225
|
-
|
|
226
|
-
expect(found).toBe(true);
|
|
152
|
+
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
227
153
|
});
|
|
228
154
|
|
|
229
|
-
it('should return true when multiple flows with same sourceFile all have IDs',
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const homepageFlows = model.narratives.filter(
|
|
234
|
-
(f) => f.sourceFile !== undefined && f.sourceFile.includes('homepage.narrative.ts'),
|
|
235
|
-
);
|
|
236
|
-
expect(homepageFlows.length).toBe(3);
|
|
237
|
-
|
|
238
|
-
const homepageModel = { ...model, narratives: homepageFlows };
|
|
239
|
-
expect(hasAllIds(homepageModel)).toBe(true);
|
|
155
|
+
it('should return true when multiple flows with same sourceFile all have IDs', () => {
|
|
156
|
+
const model = createMultipleFlowsModel(true, true);
|
|
157
|
+
expect(hasAllIds(model)).toBe(true);
|
|
240
158
|
});
|
|
241
159
|
|
|
242
|
-
it('should return false when any flow in multiple flows with same sourceFile is missing ID',
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const homepageFlows = model.narratives.filter(
|
|
247
|
-
(f) => f.sourceFile !== undefined && f.sourceFile.includes('homepage-incomplete.narrative.ts'),
|
|
248
|
-
);
|
|
249
|
-
expect(homepageFlows.length).toBe(3);
|
|
250
|
-
|
|
251
|
-
const homepageModel = { ...model, narratives: homepageFlows };
|
|
252
|
-
expect(hasAllIds(homepageModel)).toBe(false);
|
|
160
|
+
it('should return false when any flow in multiple flows with same sourceFile is missing ID', () => {
|
|
161
|
+
const model = createMultipleFlowsModel(false, true);
|
|
162
|
+
expect(hasAllIds(model)).toBe(false);
|
|
253
163
|
});
|
|
254
164
|
|
|
255
|
-
it('should return false when any slice in multiple flows with same sourceFile is missing ID',
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const homepageFlows = model.narratives.filter(
|
|
260
|
-
(f) => f.sourceFile !== undefined && f.sourceFile.includes('homepage-slice-missing.narrative.ts'),
|
|
261
|
-
);
|
|
262
|
-
expect(homepageFlows.length).toBe(3);
|
|
263
|
-
|
|
264
|
-
const homepageModel = { ...model, narratives: homepageFlows };
|
|
265
|
-
expect(hasAllIds(homepageModel)).toBe(false);
|
|
165
|
+
it('should return false when any slice in multiple flows with same sourceFile is missing ID', () => {
|
|
166
|
+
const model = createMultipleFlowsModel(true, false);
|
|
167
|
+
expect(hasAllIds(model)).toBe(false);
|
|
266
168
|
});
|
|
267
169
|
});
|
package/src/id/hasAllIds.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import { Model, Slice } from '../index';
|
|
1
|
+
import { Model, Slice, Spec, Rule } from '../index';
|
|
2
2
|
|
|
3
3
|
function hasValidId(item: { id?: string }): boolean {
|
|
4
4
|
return item.id !== undefined && item.id !== '';
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
function hasRuleIds(rules: Rule[]): boolean {
|
|
8
|
+
return rules.every((rule) => hasValidId(rule));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function hasSpecIds(specs: Spec[]): boolean {
|
|
12
|
+
return specs.every((spec) => hasRuleIds(spec.rules));
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
function hasServerSpecIds(slice: Slice): boolean {
|
|
8
|
-
if (!('server' in slice) || slice.server?.specs
|
|
9
|
-
return slice.server.specs
|
|
16
|
+
if (!('server' in slice) || slice.server?.specs === undefined || !Array.isArray(slice.server.specs)) return true;
|
|
17
|
+
return hasSpecIds(slice.server.specs);
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
function hasClientSpecIds(_slice: Slice): boolean {
|
|
13
|
-
// Client specs use string rules (no IDs needed), so always valid
|
|
14
21
|
return true;
|
|
15
22
|
}
|
|
16
23
|
|