@specverse/engines 4.1.22 → 4.1.24
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/dist/inference/ui-contracts/index.d.ts +38 -0
- package/dist/inference/ui-contracts/index.d.ts.map +1 -0
- package/dist/inference/ui-contracts/index.js +212 -0
- package/dist/inference/ui-contracts/index.js.map +1 -0
- package/dist/inference/ui-contracts/rules/_shared.d.ts +32 -0
- package/dist/inference/ui-contracts/rules/_shared.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/_shared.js +103 -0
- package/dist/inference/ui-contracts/rules/_shared.js.map +1 -0
- package/dist/inference/ui-contracts/rules/action-buttons-present.d.ts +21 -0
- package/dist/inference/ui-contracts/rules/action-buttons-present.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/action-buttons-present.js +62 -0
- package/dist/inference/ui-contracts/rules/action-buttons-present.js.map +1 -0
- package/dist/inference/ui-contracts/rules/create-reflects-in-list.d.ts +22 -0
- package/dist/inference/ui-contracts/rules/create-reflects-in-list.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/create-reflects-in-list.js +58 -0
- package/dist/inference/ui-contracts/rules/create-reflects-in-list.js.map +1 -0
- package/dist/inference/ui-contracts/rules/delete-reflects-in-list.d.ts +22 -0
- package/dist/inference/ui-contracts/rules/delete-reflects-in-list.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/delete-reflects-in-list.js +64 -0
- package/dist/inference/ui-contracts/rules/delete-reflects-in-list.js.map +1 -0
- package/dist/inference/ui-contracts/rules/detail-view-renders.d.ts +24 -0
- package/dist/inference/ui-contracts/rules/detail-view-renders.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/detail-view-renders.js +34 -0
- package/dist/inference/ui-contracts/rules/detail-view-renders.js.map +1 -0
- package/dist/inference/ui-contracts/rules/evolve-reflects-in-list.d.ts +21 -0
- package/dist/inference/ui-contracts/rules/evolve-reflects-in-list.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/evolve-reflects-in-list.js +63 -0
- package/dist/inference/ui-contracts/rules/evolve-reflects-in-list.js.map +1 -0
- package/dist/inference/ui-contracts/rules/form-shows-required-indicators.d.ts +15 -0
- package/dist/inference/ui-contracts/rules/form-shows-required-indicators.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/form-shows-required-indicators.js +38 -0
- package/dist/inference/ui-contracts/rules/form-shows-required-indicators.js.map +1 -0
- package/dist/inference/ui-contracts/rules/hasmany-shows-children-in-detail.d.ts +17 -0
- package/dist/inference/ui-contracts/rules/hasmany-shows-children-in-detail.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/hasmany-shows-children-in-detail.js +39 -0
- package/dist/inference/ui-contracts/rules/hasmany-shows-children-in-detail.js.map +1 -0
- package/dist/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.d.ts +25 -0
- package/dist/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.js +66 -0
- package/dist/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.js.map +1 -0
- package/dist/inference/ui-contracts/rules/list-shows-business-columns.d.ts +17 -0
- package/dist/inference/ui-contracts/rules/list-shows-business-columns.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/list-shows-business-columns.js +39 -0
- package/dist/inference/ui-contracts/rules/list-shows-business-columns.js.map +1 -0
- package/dist/inference/ui-contracts/rules/list-view-renders.d.ts +19 -0
- package/dist/inference/ui-contracts/rules/list-view-renders.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/list-view-renders.js +29 -0
- package/dist/inference/ui-contracts/rules/list-view-renders.js.map +1 -0
- package/dist/inference/ui-contracts/rules/nav-has-model-entries.d.ts +20 -0
- package/dist/inference/ui-contracts/rules/nav-has-model-entries.d.ts.map +1 -0
- package/dist/inference/ui-contracts/rules/nav-has-model-entries.js +29 -0
- package/dist/inference/ui-contracts/rules/nav-has-model-entries.js.map +1 -0
- package/dist/inference/ui-contracts/test-case-types.d.ts +135 -0
- package/dist/inference/ui-contracts/test-case-types.d.ts.map +1 -0
- package/dist/inference/ui-contracts/test-case-types.js +14 -0
- package/dist/inference/ui-contracts/test-case-types.js.map +1 -0
- package/dist/inference/ui-contracts/translator.d.ts +17 -0
- package/dist/inference/ui-contracts/translator.d.ts.map +1 -0
- package/dist/inference/ui-contracts/translator.js +142 -0
- package/dist/inference/ui-contracts/translator.js.map +1 -0
- package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +5 -2
- package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +11 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/package-json-generator.js +10 -0
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +34 -23
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +51 -81
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +204 -0
- package/dist/realize/index.js.map +1 -1
- package/libs/instance-factories/applications/templates/react/api-client-generator.ts +5 -2
- package/libs/instance-factories/applications/templates/react/vite-config-generator.ts +11 -0
- package/libs/instance-factories/scaffolding/templates/generic/package-json-generator.ts +13 -0
- package/libs/instance-factories/views/templates/react/components-generator.ts +34 -23
- package/libs/instance-factories/views/templates/react/hooks-generator.ts +72 -88
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @specverse/engines/inference/ui-contracts
|
|
3
|
+
*
|
|
4
|
+
* Spec-inferred UI contract test generation. The realize pipeline
|
|
5
|
+
* calls `generateContractTests(spec)` which runs every registered
|
|
6
|
+
* rule over the normalized spec and returns a map of filename →
|
|
7
|
+
* spec file content. The caller writes those to disk.
|
|
8
|
+
*
|
|
9
|
+
* Design: see docs/proposals/UI-CONTRACT-INFERENCE.md.
|
|
10
|
+
*/
|
|
11
|
+
import type { NormalizedSpec } from './test-case-types.js';
|
|
12
|
+
export * from './test-case-types.js';
|
|
13
|
+
export { renderSpecFile } from './translator.js';
|
|
14
|
+
export interface ContractTestFile {
|
|
15
|
+
/** Filename relative to the contract test output directory. */
|
|
16
|
+
filename: string;
|
|
17
|
+
/** The Playwright .spec.ts contents. */
|
|
18
|
+
contents: string;
|
|
19
|
+
/** Number of individual test() blocks in the file. */
|
|
20
|
+
testCount: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Run every rule over the spec and render each rule's output as a
|
|
24
|
+
* single .spec.ts file. Rules that produce zero test cases (e.g.
|
|
25
|
+
* because no spec elements matched) are skipped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function generateContractTests(spec: NormalizedSpec): ContractTestFile[];
|
|
28
|
+
/**
|
|
29
|
+
* Convert a parsed/inferred spec (as produced by the parser or the
|
|
30
|
+
* inference engine) into the NormalizedSpec shape that rules consume.
|
|
31
|
+
*
|
|
32
|
+
* The parser returns components as an array with each holding
|
|
33
|
+
* `models`, `controllers`, etc. This function flattens to a single
|
|
34
|
+
* top-level view. It handles both object-keyed and array-shaped
|
|
35
|
+
* collections.
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeSpec(raw: any): NormalizedSpec;
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/inference/ui-contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,sBAAsB,CAAC;AAcrF,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAwBjD,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,EAAE,CAc9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CA6ItD"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @specverse/engines/inference/ui-contracts
|
|
3
|
+
*
|
|
4
|
+
* Spec-inferred UI contract test generation. The realize pipeline
|
|
5
|
+
* calls `generateContractTests(spec)` which runs every registered
|
|
6
|
+
* rule over the normalized spec and returns a map of filename →
|
|
7
|
+
* spec file content. The caller writes those to disk.
|
|
8
|
+
*
|
|
9
|
+
* Design: see docs/proposals/UI-CONTRACT-INFERENCE.md.
|
|
10
|
+
*/
|
|
11
|
+
import { renderSpecFile } from './translator.js';
|
|
12
|
+
import { navHasModelEntries, RULE_ID as NAV_HAS_MODEL_ENTRIES_RULE_ID } from './rules/nav-has-model-entries.js';
|
|
13
|
+
import { listViewRenders, RULE_ID as LIST_VIEW_RENDERS_RULE_ID } from './rules/list-view-renders.js';
|
|
14
|
+
import { detailViewRenders, RULE_ID as DETAIL_VIEW_RENDERS_RULE_ID } from './rules/detail-view-renders.js';
|
|
15
|
+
import { listShowsBusinessColumns, RULE_ID as LIST_SHOWS_BUSINESS_COLUMNS_RULE_ID } from './rules/list-shows-business-columns.js';
|
|
16
|
+
import { formShowsRequiredIndicators, RULE_ID as FORM_SHOWS_REQUIRED_INDICATORS_RULE_ID } from './rules/form-shows-required-indicators.js';
|
|
17
|
+
import { lifecycleStateVisibleInDetail, RULE_ID as LIFECYCLE_STATE_VISIBLE_RULE_ID } from './rules/lifecycle-state-visible-in-detail.js';
|
|
18
|
+
import { hasManyShowsChildrenInDetail, RULE_ID as HASMANY_SHOWS_CHILDREN_RULE_ID } from './rules/hasmany-shows-children-in-detail.js';
|
|
19
|
+
import { actionButtonsPresent, RULE_ID as ACTION_BUTTONS_PRESENT_RULE_ID } from './rules/action-buttons-present.js';
|
|
20
|
+
import { createReflectsInList, RULE_ID as CREATE_REFLECTS_RULE_ID } from './rules/create-reflects-in-list.js';
|
|
21
|
+
import { deleteReflectsInList, RULE_ID as DELETE_REFLECTS_RULE_ID } from './rules/delete-reflects-in-list.js';
|
|
22
|
+
import { evolveReflectsInList, RULE_ID as EVOLVE_REFLECTS_RULE_ID } from './rules/evolve-reflects-in-list.js';
|
|
23
|
+
export * from './test-case-types.js';
|
|
24
|
+
export { renderSpecFile } from './translator.js';
|
|
25
|
+
/**
|
|
26
|
+
* Registered rules. Order doesn't matter for output — each rule
|
|
27
|
+
* produces its own spec file. Adding a rule = pushing a new entry
|
|
28
|
+
* here + creating a file under rules/.
|
|
29
|
+
*/
|
|
30
|
+
const RULES = [
|
|
31
|
+
// Rendering
|
|
32
|
+
{ id: NAV_HAS_MODEL_ENTRIES_RULE_ID, rule: navHasModelEntries },
|
|
33
|
+
{ id: LIST_VIEW_RENDERS_RULE_ID, rule: listViewRenders },
|
|
34
|
+
{ id: DETAIL_VIEW_RENDERS_RULE_ID, rule: detailViewRenders },
|
|
35
|
+
// Shape
|
|
36
|
+
{ id: LIST_SHOWS_BUSINESS_COLUMNS_RULE_ID, rule: listShowsBusinessColumns },
|
|
37
|
+
{ id: FORM_SHOWS_REQUIRED_INDICATORS_RULE_ID, rule: formShowsRequiredIndicators },
|
|
38
|
+
{ id: LIFECYCLE_STATE_VISIBLE_RULE_ID, rule: lifecycleStateVisibleInDetail },
|
|
39
|
+
{ id: HASMANY_SHOWS_CHILDREN_RULE_ID, rule: hasManyShowsChildrenInDetail },
|
|
40
|
+
{ id: ACTION_BUTTONS_PRESENT_RULE_ID, rule: actionButtonsPresent },
|
|
41
|
+
// State-sync
|
|
42
|
+
{ id: CREATE_REFLECTS_RULE_ID, rule: createReflectsInList },
|
|
43
|
+
{ id: DELETE_REFLECTS_RULE_ID, rule: deleteReflectsInList },
|
|
44
|
+
{ id: EVOLVE_REFLECTS_RULE_ID, rule: evolveReflectsInList },
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* Run every rule over the spec and render each rule's output as a
|
|
48
|
+
* single .spec.ts file. Rules that produce zero test cases (e.g.
|
|
49
|
+
* because no spec elements matched) are skipped.
|
|
50
|
+
*/
|
|
51
|
+
export function generateContractTests(spec) {
|
|
52
|
+
const files = [];
|
|
53
|
+
for (const { id, rule } of RULES) {
|
|
54
|
+
const cases = rule(spec);
|
|
55
|
+
if (cases.length === 0)
|
|
56
|
+
continue;
|
|
57
|
+
files.push({
|
|
58
|
+
filename: `${id}.spec.ts`,
|
|
59
|
+
contents: renderSpecFile(id, cases),
|
|
60
|
+
testCount: cases.length,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return files;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convert a parsed/inferred spec (as produced by the parser or the
|
|
67
|
+
* inference engine) into the NormalizedSpec shape that rules consume.
|
|
68
|
+
*
|
|
69
|
+
* The parser returns components as an array with each holding
|
|
70
|
+
* `models`, `controllers`, etc. This function flattens to a single
|
|
71
|
+
* top-level view. It handles both object-keyed and array-shaped
|
|
72
|
+
* collections.
|
|
73
|
+
*/
|
|
74
|
+
export function normalizeSpec(raw) {
|
|
75
|
+
const components = raw?.components || {};
|
|
76
|
+
const componentList = Array.isArray(components)
|
|
77
|
+
? components
|
|
78
|
+
: Object.values(components);
|
|
79
|
+
const models = [];
|
|
80
|
+
const controllers = [];
|
|
81
|
+
const services = [];
|
|
82
|
+
const views = [];
|
|
83
|
+
// Root-level collections (used by inferred specs that have already
|
|
84
|
+
// been flattened by realize)
|
|
85
|
+
const collect = (collection, into) => {
|
|
86
|
+
if (!collection)
|
|
87
|
+
return;
|
|
88
|
+
if (Array.isArray(collection)) {
|
|
89
|
+
into.push(...collection);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
for (const [name, value] of Object.entries(collection)) {
|
|
93
|
+
into.push({ name, ...value });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
collect(raw?.models, models);
|
|
98
|
+
collect(raw?.controllers, controllers);
|
|
99
|
+
collect(raw?.services, services);
|
|
100
|
+
collect(raw?.views, views);
|
|
101
|
+
for (const comp of componentList) {
|
|
102
|
+
collect(comp?.models, models);
|
|
103
|
+
collect(comp?.controllers, controllers);
|
|
104
|
+
collect(comp?.services, services);
|
|
105
|
+
collect(comp?.views, views);
|
|
106
|
+
}
|
|
107
|
+
// Dedupe by name — the realize pipeline often passes both a
|
|
108
|
+
// root-level flattened collection AND the component-level source,
|
|
109
|
+
// so we'd otherwise double-count every model.
|
|
110
|
+
const dedupeByName = (items) => {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const out = [];
|
|
113
|
+
for (const item of items) {
|
|
114
|
+
const name = item?.name;
|
|
115
|
+
if (!name || seen.has(name))
|
|
116
|
+
continue;
|
|
117
|
+
seen.add(name);
|
|
118
|
+
out.push(item);
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
};
|
|
122
|
+
const uniqModels = dedupeByName(models);
|
|
123
|
+
const uniqControllers = dedupeByName(controllers);
|
|
124
|
+
const uniqServices = dedupeByName(services);
|
|
125
|
+
const uniqViews = dedupeByName(views);
|
|
126
|
+
/**
|
|
127
|
+
* Normalize an attributes collection from either array or object
|
|
128
|
+
* form, and each entry from either shorthand string
|
|
129
|
+
* ("String required") or object form.
|
|
130
|
+
*/
|
|
131
|
+
const normalizeAttributes = (raw) => {
|
|
132
|
+
if (!raw)
|
|
133
|
+
return undefined;
|
|
134
|
+
const entries = Array.isArray(raw)
|
|
135
|
+
? raw.map((a) => [a?.name || '', a])
|
|
136
|
+
: Object.entries(raw);
|
|
137
|
+
return entries
|
|
138
|
+
.filter(([name]) => !!name)
|
|
139
|
+
.map(([name, def]) => {
|
|
140
|
+
if (typeof def === 'string') {
|
|
141
|
+
const tokens = def.split(/\s+/);
|
|
142
|
+
const type = tokens[0] || 'String';
|
|
143
|
+
const required = /\brequired\b/.test(def);
|
|
144
|
+
const category = /\bcategory=metadata\b/.test(def)
|
|
145
|
+
? 'metadata'
|
|
146
|
+
: /\bcategory=relationship\b/.test(def)
|
|
147
|
+
? 'relationship'
|
|
148
|
+
: /\bauto=now\b/.test(def) || name === 'id'
|
|
149
|
+
? 'metadata'
|
|
150
|
+
: 'business';
|
|
151
|
+
return { name, type, required, category };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
name,
|
|
155
|
+
type: def?.type || 'String',
|
|
156
|
+
required: !!def?.required,
|
|
157
|
+
category: def?.category || (name === 'id' ? 'metadata' : 'business'),
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Normalize a relationships collection. Supports:
|
|
163
|
+
* - Array of {name, type, target}
|
|
164
|
+
* - Object keyed by name, values as {type, target} or shorthand "hasMany Post"
|
|
165
|
+
*/
|
|
166
|
+
const normalizeRelationships = (raw) => {
|
|
167
|
+
if (!raw)
|
|
168
|
+
return undefined;
|
|
169
|
+
const entries = Array.isArray(raw)
|
|
170
|
+
? raw.map((r) => [r?.name || '', r])
|
|
171
|
+
: Object.entries(raw);
|
|
172
|
+
return entries
|
|
173
|
+
.filter(([name]) => !!name)
|
|
174
|
+
.map(([name, def]) => {
|
|
175
|
+
if (typeof def === 'string') {
|
|
176
|
+
// "hasMany Post cascade" → type=hasMany, target=Post
|
|
177
|
+
const tokens = def.split(/\s+/);
|
|
178
|
+
const type = tokens[0] || '';
|
|
179
|
+
const target = tokens[1];
|
|
180
|
+
return { name, type, target };
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
name,
|
|
184
|
+
type: def?.type || '',
|
|
185
|
+
target: def?.target || def?.targetModel,
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
models: uniqModels.map(m => ({
|
|
191
|
+
name: m.name,
|
|
192
|
+
attributes: normalizeAttributes(m.attributes),
|
|
193
|
+
lifecycles: m.lifecycles,
|
|
194
|
+
relationships: normalizeRelationships(m.relationships),
|
|
195
|
+
})),
|
|
196
|
+
controllers: uniqControllers.map(c => ({
|
|
197
|
+
name: c.name,
|
|
198
|
+
model: c.model || c.modelReference,
|
|
199
|
+
cured: c.cured,
|
|
200
|
+
})),
|
|
201
|
+
services: uniqServices.map(s => ({
|
|
202
|
+
name: s.name,
|
|
203
|
+
operations: s.operations,
|
|
204
|
+
})),
|
|
205
|
+
views: uniqViews.map(v => ({
|
|
206
|
+
name: v.name,
|
|
207
|
+
type: v.type,
|
|
208
|
+
model: v.primaryModel || v.model,
|
|
209
|
+
})),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/inference/ui-contracts/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for rules that need sample data / seedability
|
|
3
|
+
* analysis. Kept separate from test-case-types.ts so rules can
|
|
4
|
+
* import pure utilities without pulling in the TestStep union.
|
|
5
|
+
*/
|
|
6
|
+
import type { ModelInfo, AttributeInfo } from '../test-case-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Pick the attribute to use as the entity's human-readable display
|
|
9
|
+
* value in assertions. Preference order:
|
|
10
|
+
* 1. First required business String named 'title'/'name'/'label'
|
|
11
|
+
* 2. First required business String attribute
|
|
12
|
+
* 3. First business String attribute
|
|
13
|
+
* 4. null — caller should skip this rule for this model
|
|
14
|
+
*/
|
|
15
|
+
export declare function pickDisplayAttribute(model: ModelInfo): AttributeInfo | null;
|
|
16
|
+
/**
|
|
17
|
+
* Build a seed-data payload for a model that's plausible enough for
|
|
18
|
+
* the backend to accept. Fills in every required business attribute
|
|
19
|
+
* with a marker-tagged value so contract-test-created entities are
|
|
20
|
+
* easy to identify.
|
|
21
|
+
*
|
|
22
|
+
* Returns null if the model has any required belongsTo relationship —
|
|
23
|
+
* seeding parent entities is out of scope for v1 state-sync rules.
|
|
24
|
+
* Phase 5+ may add a topological-seed helper that handles dependents.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildSeedData(model: ModelInfo, marker: string): Record<string, unknown> | null;
|
|
27
|
+
/**
|
|
28
|
+
* Extract the list of lifecycle state names from a model's lifecycles
|
|
29
|
+
* block. Returns null if the model has no lifecycle.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getLifecycleStates(model: ModelInfo): string[] | null;
|
|
32
|
+
//# sourceMappingURL=_shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/_shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,IAAI,CAa3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAiBhC;AAuBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,IAAI,CAepE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for rules that need sample data / seedability
|
|
3
|
+
* analysis. Kept separate from test-case-types.ts so rules can
|
|
4
|
+
* import pure utilities without pulling in the TestStep union.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Pick the attribute to use as the entity's human-readable display
|
|
8
|
+
* value in assertions. Preference order:
|
|
9
|
+
* 1. First required business String named 'title'/'name'/'label'
|
|
10
|
+
* 2. First required business String attribute
|
|
11
|
+
* 3. First business String attribute
|
|
12
|
+
* 4. null — caller should skip this rule for this model
|
|
13
|
+
*/
|
|
14
|
+
export function pickDisplayAttribute(model) {
|
|
15
|
+
const attrs = model.attributes || [];
|
|
16
|
+
const business = attrs.filter(a => a.category !== 'metadata' && a.category !== 'relationship');
|
|
17
|
+
const strings = business.filter(a => !a.type || /^String$|^Email$/i.test(a.type));
|
|
18
|
+
const named = strings.find(a => /^(title|name|label)$/i.test(a.name) && a.required);
|
|
19
|
+
if (named)
|
|
20
|
+
return named;
|
|
21
|
+
const requiredStr = strings.find(a => a.required);
|
|
22
|
+
if (requiredStr)
|
|
23
|
+
return requiredStr;
|
|
24
|
+
if (strings.length > 0)
|
|
25
|
+
return strings[0];
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build a seed-data payload for a model that's plausible enough for
|
|
30
|
+
* the backend to accept. Fills in every required business attribute
|
|
31
|
+
* with a marker-tagged value so contract-test-created entities are
|
|
32
|
+
* easy to identify.
|
|
33
|
+
*
|
|
34
|
+
* Returns null if the model has any required belongsTo relationship —
|
|
35
|
+
* seeding parent entities is out of scope for v1 state-sync rules.
|
|
36
|
+
* Phase 5+ may add a topological-seed helper that handles dependents.
|
|
37
|
+
*/
|
|
38
|
+
export function buildSeedData(model, marker) {
|
|
39
|
+
// If the model has a required belongsTo relationship, we'd need to
|
|
40
|
+
// seed the parent first. v1 skips these; only top-level models get
|
|
41
|
+
// state-sync rules.
|
|
42
|
+
const rels = model.relationships || [];
|
|
43
|
+
const hasRequiredBelongsTo = rels.some(r => /belongsTo/i.test(String(r.type || '')));
|
|
44
|
+
if (hasRequiredBelongsTo)
|
|
45
|
+
return null;
|
|
46
|
+
const data = {};
|
|
47
|
+
const attrs = model.attributes || [];
|
|
48
|
+
for (const attr of attrs) {
|
|
49
|
+
if (attr.category === 'metadata')
|
|
50
|
+
continue;
|
|
51
|
+
if (attr.category === 'relationship')
|
|
52
|
+
continue;
|
|
53
|
+
if (!attr.required && !isLifecycleField(attr))
|
|
54
|
+
continue;
|
|
55
|
+
data[attr.name] = sampleValueFor(attr, marker);
|
|
56
|
+
}
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
function isLifecycleField(attr) {
|
|
60
|
+
// Heuristic: `status` / `state` attributes with an enumerated
|
|
61
|
+
// value list belong to a lifecycle. The parser usually strips
|
|
62
|
+
// the value list from the type at normalization time, so we
|
|
63
|
+
// rely on naming convention here. Good enough for v1.
|
|
64
|
+
return /^(status|state)$/i.test(attr.name);
|
|
65
|
+
}
|
|
66
|
+
function sampleValueFor(attr, marker) {
|
|
67
|
+
const type = (attr.type || 'String').trim();
|
|
68
|
+
// Lifecycle/enum fields: pick a plausible initial state. For poll-app
|
|
69
|
+
// it's 'draft'. Future work: parse the enum list from the spec and
|
|
70
|
+
// pick the first value.
|
|
71
|
+
if (isLifecycleField(attr))
|
|
72
|
+
return 'draft';
|
|
73
|
+
if (/^Integer$|^Int$|^Number$|^Float$/i.test(type))
|
|
74
|
+
return 0;
|
|
75
|
+
if (/^Boolean$/i.test(type))
|
|
76
|
+
return false;
|
|
77
|
+
if (/^DateTime$|^Date$/i.test(type))
|
|
78
|
+
return new Date().toISOString();
|
|
79
|
+
// String, Email, UUID → tag with the marker so assertions find it
|
|
80
|
+
return `${marker}-${attr.name}`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract the list of lifecycle state names from a model's lifecycles
|
|
84
|
+
* block. Returns null if the model has no lifecycle.
|
|
85
|
+
*/
|
|
86
|
+
export function getLifecycleStates(model) {
|
|
87
|
+
const lifecycles = model.lifecycles;
|
|
88
|
+
if (!lifecycles)
|
|
89
|
+
return null;
|
|
90
|
+
const first = Object.values(lifecycles)[0];
|
|
91
|
+
if (!first)
|
|
92
|
+
return null;
|
|
93
|
+
// Form 1: { flow: 'draft -> open -> closed' }
|
|
94
|
+
if (typeof first.flow === 'string') {
|
|
95
|
+
return first.flow.split('->').map((s) => s.trim()).filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
// Form 2: { states: [...] }
|
|
98
|
+
if (Array.isArray(first.states)) {
|
|
99
|
+
return first.states.map((s) => (typeof s === 'string' ? s : s?.name)).filter(Boolean);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=_shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/_shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAgB;IACnD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAElF,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;IACpF,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAgB,EAChB,MAAc;IAEd,mEAAmE;IACnE,mEAAmE;IACnE,oBAAoB;IACpB,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;IACvC,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,IAAI,oBAAoB;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;YAAE,SAAS;QAC3C,IAAI,IAAI,CAAC,QAAQ,KAAK,cAAc;YAAE,SAAS;QAC/C,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,SAAS;QACxD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAmB;IAC3C,8DAA8D;IAC9D,8DAA8D;IAC9D,4DAA4D;IAC5D,sDAAsD;IACtD,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,cAAc,CAAC,IAAmB,EAAE,MAAc;IACzD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,sEAAsE;IACtE,mEAAmE;IACnE,wBAAwB;IACxB,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7D,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrE,kEAAkE;IAClE,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAgB;IACjD,MAAM,UAAU,GAAG,KAAK,CAAC,UAA6C,CAAC;IACvE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,8CAA8C;IAC9C,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7E,CAAC;IACD,4BAA4B;IAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: action-buttons-present
|
|
3
|
+
*
|
|
4
|
+
* For every CURED operation supported by a controller, the
|
|
5
|
+
* corresponding model's view should expose a visible action trigger
|
|
6
|
+
* (button, menu item, etc.) labeled with the operation name.
|
|
7
|
+
*
|
|
8
|
+
* CURED = create / update / retrieve / validate / evolve / delete.
|
|
9
|
+
* The spec declares which ones a controller supports via
|
|
10
|
+
* `cured: { create: {}, retrieve: {}, ... }`.
|
|
11
|
+
*
|
|
12
|
+
* v1 looks for operation labels as visible text. A stricter
|
|
13
|
+
* variant could use `getByRole('button', { name })` once the
|
|
14
|
+
* runtime's button markup stabilizes.
|
|
15
|
+
*
|
|
16
|
+
* One TestCase per (controller, cured-operation) pair.
|
|
17
|
+
*/
|
|
18
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
19
|
+
export declare const RULE_ID = "action-buttons-present";
|
|
20
|
+
export declare const actionButtonsPresent: UiContractRule;
|
|
21
|
+
//# sourceMappingURL=action-buttons-present.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-buttons-present.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/action-buttons-present.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,2BAA2B,CAAC;AAiBhD,eAAO,MAAM,oBAAoB,EAAE,cAyBlC,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: action-buttons-present
|
|
3
|
+
*
|
|
4
|
+
* For every CURED operation supported by a controller, the
|
|
5
|
+
* corresponding model's view should expose a visible action trigger
|
|
6
|
+
* (button, menu item, etc.) labeled with the operation name.
|
|
7
|
+
*
|
|
8
|
+
* CURED = create / update / retrieve / validate / evolve / delete.
|
|
9
|
+
* The spec declares which ones a controller supports via
|
|
10
|
+
* `cured: { create: {}, retrieve: {}, ... }`.
|
|
11
|
+
*
|
|
12
|
+
* v1 looks for operation labels as visible text. A stricter
|
|
13
|
+
* variant could use `getByRole('button', { name })` once the
|
|
14
|
+
* runtime's button markup stabilizes.
|
|
15
|
+
*
|
|
16
|
+
* One TestCase per (controller, cured-operation) pair.
|
|
17
|
+
*/
|
|
18
|
+
export const RULE_ID = 'action-buttons-present';
|
|
19
|
+
/**
|
|
20
|
+
* Canonical CURED labels. Used both as the trigger text to search for
|
|
21
|
+
* AND as the keys inside `controller.cured`. If a controller declares
|
|
22
|
+
* a custom action, we'll pick it up too via the Object.keys iteration
|
|
23
|
+
* below — this set just records which labels are considered canonical.
|
|
24
|
+
*/
|
|
25
|
+
const CURED_LABELS = new Set([
|
|
26
|
+
'create',
|
|
27
|
+
'retrieve',
|
|
28
|
+
'update',
|
|
29
|
+
'validate',
|
|
30
|
+
'evolve',
|
|
31
|
+
'delete',
|
|
32
|
+
]);
|
|
33
|
+
export const actionButtonsPresent = (spec) => {
|
|
34
|
+
const cases = [];
|
|
35
|
+
for (const controller of spec.controllers || []) {
|
|
36
|
+
const cured = controller.cured;
|
|
37
|
+
if (!cured)
|
|
38
|
+
continue;
|
|
39
|
+
const modelName = controller.model || controller.name.replace(/Controller$/, '');
|
|
40
|
+
for (const opName of Object.keys(cured)) {
|
|
41
|
+
// Skip the common 'retrieve' and 'validate' which typically
|
|
42
|
+
// don't have their own button (they fire implicitly). Keep
|
|
43
|
+
// create/update/delete/evolve — the ones users trigger.
|
|
44
|
+
if (!CURED_LABELS.has(opName))
|
|
45
|
+
continue;
|
|
46
|
+
if (opName === 'retrieve' || opName === 'validate')
|
|
47
|
+
continue;
|
|
48
|
+
cases.push({
|
|
49
|
+
ruleId: RULE_ID,
|
|
50
|
+
specElement: `${controller.name}.${opName}`,
|
|
51
|
+
name: `[${RULE_ID}] ${modelName}.${opName} action button is visible`,
|
|
52
|
+
steps: [
|
|
53
|
+
{ action: 'bootRuntime' },
|
|
54
|
+
{ action: 'navigateToModel', modelName },
|
|
55
|
+
{ action: 'expectActionButton', modelName, actionLabel: opName },
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return cases;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=action-buttons-present.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-buttons-present.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/action-buttons-present.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,wBAAwB,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;IAC3B,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAmB,CAAC,IAAoB,EAAc,EAAE;IACvF,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,UAAU,CAAC,KAA4C,CAAC;QACtE,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACjF,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,4DAA4D;YAC5D,2DAA2D;YAC3D,wDAAwD;YACxD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,SAAS;YACxC,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU;gBAAE,SAAS;YAC7D,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,OAAO;gBACf,WAAW,EAAE,GAAG,UAAU,CAAC,IAAI,IAAI,MAAM,EAAE;gBAC3C,IAAI,EAAE,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,2BAA2B;gBACpE,KAAK,EAAE;oBACL,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE;oBACxC,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE;iBACjE;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: create-reflects-in-list
|
|
3
|
+
*
|
|
4
|
+
* State-sync contract: after a mutation creates an entity, every
|
|
5
|
+
* view reflecting that model must converge to show it within one
|
|
6
|
+
* WebSocket round-trip. v1 seeds the entity via a direct backend
|
|
7
|
+
* POST (through the vite dev proxy) rather than driving the UI
|
|
8
|
+
* form flow — the form UI has known gaps in empty state (see
|
|
9
|
+
* docs/plans/2026-04-14-RUNTIME-GAPS-FROM-CONTRACT-TESTS.md), so
|
|
10
|
+
* this rule deliberately tests the state-sync path on its own.
|
|
11
|
+
*
|
|
12
|
+
* Skipped for models whose creation would require seeding parent
|
|
13
|
+
* entities first (any required belongsTo relationship). For
|
|
14
|
+
* poll-app this means: Poll gets a test; Option and Vote are
|
|
15
|
+
* skipped until topological seed support lands.
|
|
16
|
+
*
|
|
17
|
+
* One TestCase per seedable model.
|
|
18
|
+
*/
|
|
19
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
20
|
+
export declare const RULE_ID = "create-reflects-in-list";
|
|
21
|
+
export declare const createReflectsInList: UiContractRule;
|
|
22
|
+
//# sourceMappingURL=create-reflects-in-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-reflects-in-list.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/create-reflects-in-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAGtF,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAEjD,eAAO,MAAM,oBAAoB,EAAE,cAkClC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: create-reflects-in-list
|
|
3
|
+
*
|
|
4
|
+
* State-sync contract: after a mutation creates an entity, every
|
|
5
|
+
* view reflecting that model must converge to show it within one
|
|
6
|
+
* WebSocket round-trip. v1 seeds the entity via a direct backend
|
|
7
|
+
* POST (through the vite dev proxy) rather than driving the UI
|
|
8
|
+
* form flow — the form UI has known gaps in empty state (see
|
|
9
|
+
* docs/plans/2026-04-14-RUNTIME-GAPS-FROM-CONTRACT-TESTS.md), so
|
|
10
|
+
* this rule deliberately tests the state-sync path on its own.
|
|
11
|
+
*
|
|
12
|
+
* Skipped for models whose creation would require seeding parent
|
|
13
|
+
* entities first (any required belongsTo relationship). For
|
|
14
|
+
* poll-app this means: Poll gets a test; Option and Vote are
|
|
15
|
+
* skipped until topological seed support lands.
|
|
16
|
+
*
|
|
17
|
+
* One TestCase per seedable model.
|
|
18
|
+
*/
|
|
19
|
+
import { pickDisplayAttribute, buildSeedData } from './_shared.js';
|
|
20
|
+
export const RULE_ID = 'create-reflects-in-list';
|
|
21
|
+
export const createReflectsInList = (spec) => {
|
|
22
|
+
const cases = [];
|
|
23
|
+
for (const model of spec.models) {
|
|
24
|
+
const display = pickDisplayAttribute(model);
|
|
25
|
+
if (!display)
|
|
26
|
+
continue;
|
|
27
|
+
const marker = `ct-create-${model.name}`;
|
|
28
|
+
const seedData = buildSeedData(model, marker);
|
|
29
|
+
if (!seedData)
|
|
30
|
+
continue; // skip — requires parent entities
|
|
31
|
+
const displayValue = seedData[display.name];
|
|
32
|
+
if (typeof displayValue !== 'string')
|
|
33
|
+
continue;
|
|
34
|
+
cases.push({
|
|
35
|
+
ruleId: RULE_ID,
|
|
36
|
+
specElement: model.name,
|
|
37
|
+
name: `[${RULE_ID}] creating a ${model.name} reflects in its list view`,
|
|
38
|
+
steps: [
|
|
39
|
+
{ action: 'bootRuntime' },
|
|
40
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
41
|
+
{
|
|
42
|
+
action: 'createEntity',
|
|
43
|
+
modelName: model.name,
|
|
44
|
+
data: seedData,
|
|
45
|
+
bindAs: '{ id: _createId, data: created }',
|
|
46
|
+
uniqueField: display.name,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
action: 'expectEntityInList',
|
|
50
|
+
modelName: model.name,
|
|
51
|
+
displayExpr: `String(created[${JSON.stringify(display.name)}])`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return cases;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=create-reflects-in-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-reflects-in-list.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/create-reflects-in-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEnE,MAAM,CAAC,MAAM,OAAO,GAAG,yBAAyB,CAAC;AAEjD,MAAM,CAAC,MAAM,oBAAoB,GAAmB,CAAC,IAAoB,EAAc,EAAE;IACvF,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,MAAM,GAAG,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ;YAAE,SAAS,CAAC,kCAAkC;QAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,OAAO,YAAY,KAAK,QAAQ;YAAE,SAAS;QAE/C,KAAK,CAAC,IAAI,CAAC;YACT,MAAM,EAAE,OAAO;YACf,WAAW,EAAE,KAAK,CAAC,IAAI;YACvB,IAAI,EAAE,IAAI,OAAO,gBAAgB,KAAK,CAAC,IAAI,4BAA4B;YACvE,KAAK,EAAE;gBACL,EAAE,MAAM,EAAE,aAAa,EAAE;gBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;gBACpD;oBACE,MAAM,EAAE,cAAc;oBACtB,SAAS,EAAE,KAAK,CAAC,IAAI;oBACrB,IAAI,EAAE,QAAQ;oBACd,MAAM,EAAE,kCAAkC;oBAC1C,WAAW,EAAE,OAAO,CAAC,IAAI;iBAC1B;gBACD;oBACE,MAAM,EAAE,oBAAoB;oBAC5B,SAAS,EAAE,KAAK,CAAC,IAAI;oBACrB,WAAW,EAAE,kBAAkB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI;iBAChE;aACF;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: delete-reflects-in-list
|
|
3
|
+
*
|
|
4
|
+
* Mirror of create-reflects-in-list, for the delete path. Seeds an
|
|
5
|
+
* entity via backend POST, asserts it appears in the list, then
|
|
6
|
+
* deletes it via backend DELETE and asserts it disappears. The
|
|
7
|
+
* disappear-after-delete check exercises the full state-sync chain:
|
|
8
|
+
* backend publishes a Deleted event → WebSocket pushes to frontend
|
|
9
|
+
* → useEntitySync invalidates the ['entities', modelName] query key
|
|
10
|
+
* → React Query refetches → the list view re-renders without the
|
|
11
|
+
* deleted row.
|
|
12
|
+
*
|
|
13
|
+
* If any link in that chain is broken (e.g. the mutation hook
|
|
14
|
+
* forgets to invalidate, or the WebSocket bridge drops the event),
|
|
15
|
+
* this test is the one that catches it.
|
|
16
|
+
*
|
|
17
|
+
* One TestCase per seedable model.
|
|
18
|
+
*/
|
|
19
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
20
|
+
export declare const RULE_ID = "delete-reflects-in-list";
|
|
21
|
+
export declare const deleteReflectsInList: UiContractRule;
|
|
22
|
+
//# sourceMappingURL=delete-reflects-in-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-reflects-in-list.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/delete-reflects-in-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAGtF,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAEjD,eAAO,MAAM,oBAAoB,EAAE,cAwClC,CAAC"}
|