@specverse/engines 4.1.22 → 4.1.23
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 +48 -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 +50 -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 +53 -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 +126 -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 +127 -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/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/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,50 @@
|
|
|
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 { pickDisplayAttribute, buildSeedData } from './_shared.js';
|
|
20
|
+
export const RULE_ID = 'delete-reflects-in-list';
|
|
21
|
+
export const deleteReflectsInList = (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-delete-${model.name}`;
|
|
28
|
+
const seedData = buildSeedData(model, marker);
|
|
29
|
+
if (!seedData)
|
|
30
|
+
continue;
|
|
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}] deleting a ${model.name} removes it from its list view`,
|
|
38
|
+
steps: [
|
|
39
|
+
{ action: 'bootRuntime' },
|
|
40
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
41
|
+
{ action: 'createEntity', modelName: model.name, data: seedData, bindAs: 'id' },
|
|
42
|
+
{ action: 'expectEntityInList', modelName: model.name, displayValue },
|
|
43
|
+
{ action: 'deleteEntity', modelName: model.name, idExpr: 'id' },
|
|
44
|
+
{ action: 'expectEntityAbsent', modelName: model.name, displayValue },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return cases;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=delete-reflects-in-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-reflects-in-list.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/delete-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;QACxB,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,gCAAgC;YAC3E,KAAK,EAAE;gBACL,EAAE,MAAM,EAAE,aAAa,EAAE;gBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;gBACpD,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;gBAC/E,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE;gBACrE,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;gBAC/D,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE;aACtE;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: detail-view-renders
|
|
3
|
+
*
|
|
4
|
+
* For every model, boot the runtime, navigate to the model, and verify
|
|
5
|
+
* no error boundary is shown even when the model has zero instances
|
|
6
|
+
* (the empty state is still a render that can crash).
|
|
7
|
+
*
|
|
8
|
+
* Distinct from list-view-renders because v1's navigateToModel lands
|
|
9
|
+
* on the list by default, not the detail. In practice both rules
|
|
10
|
+
* currently exercise the same nav-click + no-error assertion; when
|
|
11
|
+
* detail views get their own dedicated entry point in the DevShell,
|
|
12
|
+
* this rule will diverge to target that path specifically.
|
|
13
|
+
*
|
|
14
|
+
* Kept as its own rule for two reasons:
|
|
15
|
+
* 1. Failure attribution is clearer — a broken detail template
|
|
16
|
+
* fails this rule but not `list-view-renders`.
|
|
17
|
+
* 2. The rule stays stable across harness evolution — when we add
|
|
18
|
+
* a `navigateToDetail` helper, only this rule's step changes,
|
|
19
|
+
* not the entire test surface.
|
|
20
|
+
*/
|
|
21
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
22
|
+
export declare const RULE_ID = "detail-view-renders";
|
|
23
|
+
export declare const detailViewRenders: UiContractRule;
|
|
24
|
+
//# sourceMappingURL=detail-view-renders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detail-view-renders.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/detail-view-renders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,wBAAwB,CAAC;AAE7C,eAAO,MAAM,iBAAiB,EAAE,cAW/B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: detail-view-renders
|
|
3
|
+
*
|
|
4
|
+
* For every model, boot the runtime, navigate to the model, and verify
|
|
5
|
+
* no error boundary is shown even when the model has zero instances
|
|
6
|
+
* (the empty state is still a render that can crash).
|
|
7
|
+
*
|
|
8
|
+
* Distinct from list-view-renders because v1's navigateToModel lands
|
|
9
|
+
* on the list by default, not the detail. In practice both rules
|
|
10
|
+
* currently exercise the same nav-click + no-error assertion; when
|
|
11
|
+
* detail views get their own dedicated entry point in the DevShell,
|
|
12
|
+
* this rule will diverge to target that path specifically.
|
|
13
|
+
*
|
|
14
|
+
* Kept as its own rule for two reasons:
|
|
15
|
+
* 1. Failure attribution is clearer — a broken detail template
|
|
16
|
+
* fails this rule but not `list-view-renders`.
|
|
17
|
+
* 2. The rule stays stable across harness evolution — when we add
|
|
18
|
+
* a `navigateToDetail` helper, only this rule's step changes,
|
|
19
|
+
* not the entire test surface.
|
|
20
|
+
*/
|
|
21
|
+
export const RULE_ID = 'detail-view-renders';
|
|
22
|
+
export const detailViewRenders = (spec) => {
|
|
23
|
+
return spec.models.map(model => ({
|
|
24
|
+
ruleId: RULE_ID,
|
|
25
|
+
specElement: model.name,
|
|
26
|
+
name: `[${RULE_ID}] ${model.name} detail view renders without errors (empty state ok)`,
|
|
27
|
+
steps: [
|
|
28
|
+
{ action: 'bootRuntime' },
|
|
29
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
30
|
+
{ action: 'expectViewRenders' },
|
|
31
|
+
],
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=detail-view-renders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detail-view-renders.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/detail-view-renders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAE7C,MAAM,CAAC,MAAM,iBAAiB,GAAmB,CAAC,IAAoB,EAAc,EAAE;IACpF,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,sDAAsD;QACtF,KAAK,EAAE;YACL,EAAE,MAAM,EAAE,aAAa,EAAE;YACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;YACpD,EAAE,MAAM,EAAE,mBAAmB,EAAE;SAChC;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: evolve-reflects-in-list
|
|
3
|
+
*
|
|
4
|
+
* For every model with a lifecycle, seed an entity in the initial
|
|
5
|
+
* state, transition it to the next state via backend PATCH, and
|
|
6
|
+
* assert the list view reflects the new state. This exercises the
|
|
7
|
+
* lifecycle mutation path through the same state-sync contract as
|
|
8
|
+
* create/delete.
|
|
9
|
+
*
|
|
10
|
+
* Uses the first → second state transition from the lifecycle flow.
|
|
11
|
+
* If the lifecycle has fewer than two states, the rule produces no
|
|
12
|
+
* test case for that model.
|
|
13
|
+
*
|
|
14
|
+
* One TestCase per (seedable, lifecycled) model. For poll-app: Poll
|
|
15
|
+
* has a lifecycle with states draft → open → closed, so a single
|
|
16
|
+
* test covers the draft → open transition.
|
|
17
|
+
*/
|
|
18
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
19
|
+
export declare const RULE_ID = "evolve-reflects-in-list";
|
|
20
|
+
export declare const evolveReflectsInList: UiContractRule;
|
|
21
|
+
//# sourceMappingURL=evolve-reflects-in-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evolve-reflects-in-list.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/evolve-reflects-in-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAGtF,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAEjD,eAAO,MAAM,oBAAoB,EAAE,cA8BlC,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: evolve-reflects-in-list
|
|
3
|
+
*
|
|
4
|
+
* For every model with a lifecycle, seed an entity in the initial
|
|
5
|
+
* state, transition it to the next state via backend PATCH, and
|
|
6
|
+
* assert the list view reflects the new state. This exercises the
|
|
7
|
+
* lifecycle mutation path through the same state-sync contract as
|
|
8
|
+
* create/delete.
|
|
9
|
+
*
|
|
10
|
+
* Uses the first → second state transition from the lifecycle flow.
|
|
11
|
+
* If the lifecycle has fewer than two states, the rule produces no
|
|
12
|
+
* test case for that model.
|
|
13
|
+
*
|
|
14
|
+
* One TestCase per (seedable, lifecycled) model. For poll-app: Poll
|
|
15
|
+
* has a lifecycle with states draft → open → closed, so a single
|
|
16
|
+
* test covers the draft → open transition.
|
|
17
|
+
*/
|
|
18
|
+
import { pickDisplayAttribute, buildSeedData, getLifecycleStates } from './_shared.js';
|
|
19
|
+
export const RULE_ID = 'evolve-reflects-in-list';
|
|
20
|
+
export const evolveReflectsInList = (spec) => {
|
|
21
|
+
const cases = [];
|
|
22
|
+
for (const model of spec.models) {
|
|
23
|
+
const states = getLifecycleStates(model);
|
|
24
|
+
if (!states || states.length < 2)
|
|
25
|
+
continue;
|
|
26
|
+
const display = pickDisplayAttribute(model);
|
|
27
|
+
if (!display)
|
|
28
|
+
continue;
|
|
29
|
+
const marker = `ct-evolve-${model.name}`;
|
|
30
|
+
const seedData = buildSeedData(model, marker);
|
|
31
|
+
if (!seedData)
|
|
32
|
+
continue;
|
|
33
|
+
const displayValue = seedData[display.name];
|
|
34
|
+
if (typeof displayValue !== 'string')
|
|
35
|
+
continue;
|
|
36
|
+
const [fromState, toState] = states;
|
|
37
|
+
cases.push({
|
|
38
|
+
ruleId: RULE_ID,
|
|
39
|
+
specElement: `${model.name} ${fromState}→${toState}`,
|
|
40
|
+
name: `[${RULE_ID}] evolving a ${model.name} from '${fromState}' to '${toState}' updates the list view`,
|
|
41
|
+
steps: [
|
|
42
|
+
{ action: 'bootRuntime' },
|
|
43
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
44
|
+
{ action: 'createEntity', modelName: model.name, data: seedData, bindAs: 'id' },
|
|
45
|
+
{ action: 'expectEntityInList', modelName: model.name, displayValue },
|
|
46
|
+
{ action: 'evolveEntity', modelName: model.name, idExpr: 'id', targetState: toState },
|
|
47
|
+
{ action: 'expectLifecycleState', modelName: model.name, stateName: toState },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return cases;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=evolve-reflects-in-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evolve-reflects-in-list.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/evolve-reflects-in-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvF,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,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC3C,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;QACxB,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,OAAO,YAAY,KAAK,QAAQ;YAAE,SAAS;QAE/C,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;QAEpC,KAAK,CAAC,IAAI,CAAC;YACT,MAAM,EAAE,OAAO;YACf,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,SAAS,IAAI,OAAO,EAAE;YACpD,IAAI,EAAE,IAAI,OAAO,gBAAgB,KAAK,CAAC,IAAI,UAAU,SAAS,SAAS,OAAO,yBAAyB;YACvG,KAAK,EAAE;gBACL,EAAE,MAAM,EAAE,aAAa,EAAE;gBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;gBACpD,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;gBAC/E,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE;gBACrE,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE;gBACrF,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE;aAC9E;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: form-shows-required-indicators
|
|
3
|
+
*
|
|
4
|
+
* For every attribute marked `required`, the model's form view
|
|
5
|
+
* should expose a visible input or field label. v1 uses a loose
|
|
6
|
+
* text-presence check; the harness helper can be tightened later
|
|
7
|
+
* to verify a specific required marker (asterisk, aria-required,
|
|
8
|
+
* "required" text, etc.).
|
|
9
|
+
*
|
|
10
|
+
* One TestCase per (model, required-attribute) pair.
|
|
11
|
+
*/
|
|
12
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
13
|
+
export declare const RULE_ID = "form-shows-required-indicators";
|
|
14
|
+
export declare const formShowsRequiredIndicators: UiContractRule;
|
|
15
|
+
//# sourceMappingURL=form-shows-required-indicators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-shows-required-indicators.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/form-shows-required-indicators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,mCAAmC,CAAC;AAExD,eAAO,MAAM,2BAA2B,EAAE,cAsBzC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: form-shows-required-indicators
|
|
3
|
+
*
|
|
4
|
+
* For every attribute marked `required`, the model's form view
|
|
5
|
+
* should expose a visible input or field label. v1 uses a loose
|
|
6
|
+
* text-presence check; the harness helper can be tightened later
|
|
7
|
+
* to verify a specific required marker (asterisk, aria-required,
|
|
8
|
+
* "required" text, etc.).
|
|
9
|
+
*
|
|
10
|
+
* One TestCase per (model, required-attribute) pair.
|
|
11
|
+
*/
|
|
12
|
+
export const RULE_ID = 'form-shows-required-indicators';
|
|
13
|
+
export const formShowsRequiredIndicators = (spec) => {
|
|
14
|
+
const cases = [];
|
|
15
|
+
for (const model of spec.models) {
|
|
16
|
+
const attrs = model.attributes || [];
|
|
17
|
+
for (const attr of attrs) {
|
|
18
|
+
if (!attr.required)
|
|
19
|
+
continue;
|
|
20
|
+
// Skip metadata attributes — they're system-managed (id, createdAt)
|
|
21
|
+
// and shouldn't appear in a user-facing form.
|
|
22
|
+
if (attr.category === 'metadata')
|
|
23
|
+
continue;
|
|
24
|
+
cases.push({
|
|
25
|
+
ruleId: RULE_ID,
|
|
26
|
+
specElement: `${model.name}.${attr.name}`,
|
|
27
|
+
name: `[${RULE_ID}] ${model.name}.${attr.name} is shown as a required input`,
|
|
28
|
+
steps: [
|
|
29
|
+
{ action: 'bootRuntime' },
|
|
30
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
31
|
+
{ action: 'expectFormInput', modelName: model.name, attributeName: attr.name, required: true },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return cases;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=form-shows-required-indicators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-shows-required-indicators.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/form-shows-required-indicators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,gCAAgC,CAAC;AAExD,MAAM,CAAC,MAAM,2BAA2B,GAAmB,CAAC,IAAoB,EAAc,EAAE;IAC9F,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAC7B,oEAAoE;YACpE,8CAA8C;YAC9C,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;gBAAE,SAAS;YAC3C,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,OAAO;gBACf,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;gBACzC,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,+BAA+B;gBAC5E,KAAK,EAAE;oBACL,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;oBACpD,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;iBAC/F;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: hasMany-shows-children-in-detail
|
|
3
|
+
*
|
|
4
|
+
* For every `hasMany` relationship on a model, the parent model's
|
|
5
|
+
* detail view should surface the child collection (section,
|
|
6
|
+
* collapsible, link, card — any visible reference will do for v1).
|
|
7
|
+
*
|
|
8
|
+
* Relationship shape can be either an explicit object or a shorthand
|
|
9
|
+
* string: `posts: hasMany Post cascade`. The parser normalizes to
|
|
10
|
+
* `{ name: 'posts', type: 'hasMany', target: 'Post' }`.
|
|
11
|
+
*
|
|
12
|
+
* One TestCase per (parent-model, hasMany-relationship) pair.
|
|
13
|
+
*/
|
|
14
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
15
|
+
export declare const RULE_ID = "hasMany-shows-children-in-detail";
|
|
16
|
+
export declare const hasManyShowsChildrenInDetail: UiContractRule;
|
|
17
|
+
//# sourceMappingURL=hasmany-shows-children-in-detail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasmany-shows-children-in-detail.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/hasmany-shows-children-in-detail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,qCAAqC,CAAC;AAE1D,eAAO,MAAM,4BAA4B,EAAE,cAsB1C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: hasMany-shows-children-in-detail
|
|
3
|
+
*
|
|
4
|
+
* For every `hasMany` relationship on a model, the parent model's
|
|
5
|
+
* detail view should surface the child collection (section,
|
|
6
|
+
* collapsible, link, card — any visible reference will do for v1).
|
|
7
|
+
*
|
|
8
|
+
* Relationship shape can be either an explicit object or a shorthand
|
|
9
|
+
* string: `posts: hasMany Post cascade`. The parser normalizes to
|
|
10
|
+
* `{ name: 'posts', type: 'hasMany', target: 'Post' }`.
|
|
11
|
+
*
|
|
12
|
+
* One TestCase per (parent-model, hasMany-relationship) pair.
|
|
13
|
+
*/
|
|
14
|
+
export const RULE_ID = 'hasMany-shows-children-in-detail';
|
|
15
|
+
export const hasManyShowsChildrenInDetail = (spec) => {
|
|
16
|
+
const cases = [];
|
|
17
|
+
for (const model of spec.models) {
|
|
18
|
+
const rels = model.relationships || [];
|
|
19
|
+
for (const rel of rels) {
|
|
20
|
+
// Type may contain modifiers (e.g. 'hasMany cascade'); look
|
|
21
|
+
// for the substring.
|
|
22
|
+
const typeStr = String(rel.type || '');
|
|
23
|
+
if (!/hasMany/i.test(typeStr))
|
|
24
|
+
continue;
|
|
25
|
+
cases.push({
|
|
26
|
+
ruleId: RULE_ID,
|
|
27
|
+
specElement: `${model.name}.${rel.name}`,
|
|
28
|
+
name: `[${RULE_ID}] ${model.name}.${rel.name} relationship is visible in the detail view`,
|
|
29
|
+
steps: [
|
|
30
|
+
{ action: 'bootRuntime' },
|
|
31
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
32
|
+
{ action: 'expectRelationshipSection', modelName: model.name, relationshipName: rel.name },
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return cases;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=hasmany-shows-children-in-detail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasmany-shows-children-in-detail.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/hasmany-shows-children-in-detail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,kCAAkC,CAAC;AAE1D,MAAM,CAAC,MAAM,4BAA4B,GAAmB,CAAC,IAAoB,EAAc,EAAE;IAC/F,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,4DAA4D;YAC5D,qBAAqB;YACrB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACvC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,SAAS;YACxC,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,OAAO;gBACf,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE;gBACxC,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,6CAA6C;gBACzF,KAAK,EAAE;oBACL,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;oBACpD,EAAE,MAAM,EAAE,2BAA2B,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,CAAC,IAAI,EAAE;iBAC3F;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: lifecycle-state-visible-in-detail
|
|
3
|
+
*
|
|
4
|
+
* For every model with a lifecycle, the detail view should surface
|
|
5
|
+
* the lifecycle's state names. v1 checks the *first* (initial)
|
|
6
|
+
* state — typically what a fresh instance shows — because that's
|
|
7
|
+
* the one guaranteed to exist without creating data.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle shape in the spec:
|
|
10
|
+
* lifecycles:
|
|
11
|
+
* status:
|
|
12
|
+
* flow: draft -> open -> closed
|
|
13
|
+
*
|
|
14
|
+
* We parse the `flow` string to extract states. If the model uses a
|
|
15
|
+
* different shape (e.g. an explicit `states: [...]`), the parser
|
|
16
|
+
* should normalize both into the same structure.
|
|
17
|
+
*
|
|
18
|
+
* One TestCase per (model, lifecycle) pair — not per state, since
|
|
19
|
+
* testing every state requires creating instances and evolving them,
|
|
20
|
+
* which belongs to the state-sync rules.
|
|
21
|
+
*/
|
|
22
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
23
|
+
export declare const RULE_ID = "lifecycle-state-visible-in-detail";
|
|
24
|
+
export declare const lifecycleStateVisibleInDetail: UiContractRule;
|
|
25
|
+
//# sourceMappingURL=lifecycle-state-visible-in-detail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-state-visible-in-detail.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAuB3D,eAAO,MAAM,6BAA6B,EAAE,cAqB3C,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: lifecycle-state-visible-in-detail
|
|
3
|
+
*
|
|
4
|
+
* For every model with a lifecycle, the detail view should surface
|
|
5
|
+
* the lifecycle's state names. v1 checks the *first* (initial)
|
|
6
|
+
* state — typically what a fresh instance shows — because that's
|
|
7
|
+
* the one guaranteed to exist without creating data.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle shape in the spec:
|
|
10
|
+
* lifecycles:
|
|
11
|
+
* status:
|
|
12
|
+
* flow: draft -> open -> closed
|
|
13
|
+
*
|
|
14
|
+
* We parse the `flow` string to extract states. If the model uses a
|
|
15
|
+
* different shape (e.g. an explicit `states: [...]`), the parser
|
|
16
|
+
* should normalize both into the same structure.
|
|
17
|
+
*
|
|
18
|
+
* One TestCase per (model, lifecycle) pair — not per state, since
|
|
19
|
+
* testing every state requires creating instances and evolving them,
|
|
20
|
+
* which belongs to the state-sync rules.
|
|
21
|
+
*/
|
|
22
|
+
export const RULE_ID = 'lifecycle-state-visible-in-detail';
|
|
23
|
+
function extractInitialState(lifecycleDef) {
|
|
24
|
+
if (!lifecycleDef)
|
|
25
|
+
return null;
|
|
26
|
+
// Form 1: { flow: 'draft -> open -> closed' }
|
|
27
|
+
if (typeof lifecycleDef.flow === 'string') {
|
|
28
|
+
const first = lifecycleDef.flow.split('->')[0]?.trim();
|
|
29
|
+
if (first)
|
|
30
|
+
return first;
|
|
31
|
+
}
|
|
32
|
+
// Form 2: { states: ['draft', 'open', 'closed'] }
|
|
33
|
+
if (Array.isArray(lifecycleDef.states) && lifecycleDef.states.length > 0) {
|
|
34
|
+
const first = lifecycleDef.states[0];
|
|
35
|
+
return typeof first === 'string' ? first : first?.name || null;
|
|
36
|
+
}
|
|
37
|
+
// Form 3: { initial: 'draft', states: {...} }
|
|
38
|
+
if (typeof lifecycleDef.initial === 'string')
|
|
39
|
+
return lifecycleDef.initial;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
export const lifecycleStateVisibleInDetail = (spec) => {
|
|
43
|
+
const cases = [];
|
|
44
|
+
for (const model of spec.models) {
|
|
45
|
+
const lifecycles = model.lifecycles;
|
|
46
|
+
if (!lifecycles)
|
|
47
|
+
continue;
|
|
48
|
+
for (const [lcName, lcDef] of Object.entries(lifecycles)) {
|
|
49
|
+
const initial = extractInitialState(lcDef);
|
|
50
|
+
if (!initial)
|
|
51
|
+
continue;
|
|
52
|
+
cases.push({
|
|
53
|
+
ruleId: RULE_ID,
|
|
54
|
+
specElement: `${model.name}.${lcName}`,
|
|
55
|
+
name: `[${RULE_ID}] ${model.name}.${lcName} initial state '${initial}' visible`,
|
|
56
|
+
steps: [
|
|
57
|
+
{ action: 'bootRuntime' },
|
|
58
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
59
|
+
{ action: 'expectLifecycleState', modelName: model.name, stateName: initial },
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return cases;
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=lifecycle-state-visible-in-detail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-state-visible-in-detail.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/lifecycle-state-visible-in-detail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAE3D,SAAS,mBAAmB,CAAC,YAAiB;IAC5C,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,8CAA8C;IAC9C,IAAI,OAAO,YAAY,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACvD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IAED,kDAAkD;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,CAAC;IACjE,CAAC;IAED,8CAA8C;IAC9C,IAAI,OAAO,YAAY,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,OAAO,CAAC;IAE1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,6BAA6B,GAAmB,CAAC,IAAoB,EAAc,EAAE;IAChG,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,KAAK,CAAC,UAA6C,CAAC;QACvE,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACzD,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,OAAO;gBACf,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,EAAE;gBACtC,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,MAAM,mBAAmB,OAAO,WAAW;gBAC/E,KAAK,EAAE;oBACL,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;oBACpD,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE;iBAC9E;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: list-shows-business-columns
|
|
3
|
+
*
|
|
4
|
+
* For each attribute marked `category: business` on a model, the
|
|
5
|
+
* model's list view should expose a column for it. Metadata and
|
|
6
|
+
* relationship attributes are excluded — metadata is system-managed
|
|
7
|
+
* and relationship FKs are handled by the relationship rule.
|
|
8
|
+
*
|
|
9
|
+
* One TestCase per (model, business-attribute) pair. A 3-attribute
|
|
10
|
+
* business model with 5 other metadata attributes produces 3 cases,
|
|
11
|
+
* not 8. That's the point of spec inference: test cases track the
|
|
12
|
+
* shape that matters.
|
|
13
|
+
*/
|
|
14
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
15
|
+
export declare const RULE_ID = "list-shows-business-columns";
|
|
16
|
+
export declare const listShowsBusinessColumns: UiContractRule;
|
|
17
|
+
//# sourceMappingURL=list-shows-business-columns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-shows-business-columns.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/list-shows-business-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,gCAAgC,CAAC;AAErD,eAAO,MAAM,wBAAwB,EAAE,cAsBtC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: list-shows-business-columns
|
|
3
|
+
*
|
|
4
|
+
* For each attribute marked `category: business` on a model, the
|
|
5
|
+
* model's list view should expose a column for it. Metadata and
|
|
6
|
+
* relationship attributes are excluded — metadata is system-managed
|
|
7
|
+
* and relationship FKs are handled by the relationship rule.
|
|
8
|
+
*
|
|
9
|
+
* One TestCase per (model, business-attribute) pair. A 3-attribute
|
|
10
|
+
* business model with 5 other metadata attributes produces 3 cases,
|
|
11
|
+
* not 8. That's the point of spec inference: test cases track the
|
|
12
|
+
* shape that matters.
|
|
13
|
+
*/
|
|
14
|
+
export const RULE_ID = 'list-shows-business-columns';
|
|
15
|
+
export const listShowsBusinessColumns = (spec) => {
|
|
16
|
+
const cases = [];
|
|
17
|
+
for (const model of spec.models) {
|
|
18
|
+
const attrs = model.attributes || [];
|
|
19
|
+
for (const attr of attrs) {
|
|
20
|
+
// v1: treat missing category as 'business' so older specs that
|
|
21
|
+
// don't tag categories still produce useful rules. Skip
|
|
22
|
+
// metadata + relationship explicitly.
|
|
23
|
+
if (attr.category === 'metadata' || attr.category === 'relationship')
|
|
24
|
+
continue;
|
|
25
|
+
cases.push({
|
|
26
|
+
ruleId: RULE_ID,
|
|
27
|
+
specElement: `${model.name}.${attr.name}`,
|
|
28
|
+
name: `[${RULE_ID}] ${model.name}.${attr.name} column is visible in the list view`,
|
|
29
|
+
steps: [
|
|
30
|
+
{ action: 'bootRuntime' },
|
|
31
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
32
|
+
{ action: 'expectListColumn', modelName: model.name, attributeName: attr.name },
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return cases;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=list-shows-business-columns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-shows-business-columns.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/list-shows-business-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AAErD,MAAM,CAAC,MAAM,wBAAwB,GAAmB,CAAC,IAAoB,EAAc,EAAE;IAC3F,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,+DAA+D;YAC/D,wDAAwD;YACxD,sCAAsC;YACtC,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,KAAK,cAAc;gBAAE,SAAS;YAC/E,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,OAAO;gBACf,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;gBACzC,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,qCAAqC;gBAClF,KAAK,EAAE;oBACL,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;oBACpD,EAAE,MAAM,EAAE,kBAAkB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE;iBAChF;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: list-view-renders
|
|
3
|
+
*
|
|
4
|
+
* For every model in the spec, boot the runtime, click through to the
|
|
5
|
+
* model's primary view (typically the list), and assert nothing threw
|
|
6
|
+
* an error boundary or bailed with an empty DOM.
|
|
7
|
+
*
|
|
8
|
+
* Catches: generator regressions that produce broken list components,
|
|
9
|
+
* runtime component crashes on specific field types, missing column
|
|
10
|
+
* renderers, etc. The dual guard (error-boundary + non-empty content)
|
|
11
|
+
* catches both obvious explosions and silent "return null" bugs.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately doesn't check for specific content — column-level
|
|
14
|
+
* assertions come from `list-shows-business-columns` (Phase 3).
|
|
15
|
+
*/
|
|
16
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
17
|
+
export declare const RULE_ID = "list-view-renders";
|
|
18
|
+
export declare const listViewRenders: UiContractRule;
|
|
19
|
+
//# sourceMappingURL=list-view-renders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-view-renders.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/list-view-renders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,sBAAsB,CAAC;AAE3C,eAAO,MAAM,eAAe,EAAE,cAW7B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: list-view-renders
|
|
3
|
+
*
|
|
4
|
+
* For every model in the spec, boot the runtime, click through to the
|
|
5
|
+
* model's primary view (typically the list), and assert nothing threw
|
|
6
|
+
* an error boundary or bailed with an empty DOM.
|
|
7
|
+
*
|
|
8
|
+
* Catches: generator regressions that produce broken list components,
|
|
9
|
+
* runtime component crashes on specific field types, missing column
|
|
10
|
+
* renderers, etc. The dual guard (error-boundary + non-empty content)
|
|
11
|
+
* catches both obvious explosions and silent "return null" bugs.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately doesn't check for specific content — column-level
|
|
14
|
+
* assertions come from `list-shows-business-columns` (Phase 3).
|
|
15
|
+
*/
|
|
16
|
+
export const RULE_ID = 'list-view-renders';
|
|
17
|
+
export const listViewRenders = (spec) => {
|
|
18
|
+
return spec.models.map(model => ({
|
|
19
|
+
ruleId: RULE_ID,
|
|
20
|
+
specElement: model.name,
|
|
21
|
+
name: `[${RULE_ID}] ${model.name} list view renders without errors`,
|
|
22
|
+
steps: [
|
|
23
|
+
{ action: 'bootRuntime' },
|
|
24
|
+
{ action: 'navigateToModel', modelName: model.name },
|
|
25
|
+
{ action: 'expectViewRenders' },
|
|
26
|
+
],
|
|
27
|
+
}));
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=list-view-renders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-view-renders.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/list-view-renders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,mBAAmB,CAAC;AAE3C,MAAM,CAAC,MAAM,eAAe,GAAmB,CAAC,IAAoB,EAAc,EAAE;IAClF,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,mCAAmC;QACnE,KAAK,EAAE;YACL,EAAE,MAAM,EAAE,aAAa,EAAE;YACzB,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;YACpD,EAAE,MAAM,EAAE,mBAAmB,EAAE;SAChC;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: nav-has-model-entries
|
|
3
|
+
*
|
|
4
|
+
* For every model declared in the spec, assert that the runtime UI
|
|
5
|
+
* exposes a visible reference to the model (nav entry, sidebar link,
|
|
6
|
+
* dashboard card — any of them count). v1 uses a loose "text appears
|
|
7
|
+
* on the page" assertion; a stricter ARIA-role variant can come
|
|
8
|
+
* later once the runtime standardizes its nav component's semantics.
|
|
9
|
+
*
|
|
10
|
+
* One TestCase per model.
|
|
11
|
+
*
|
|
12
|
+
* This is the most important rule: if the runtime can't even show
|
|
13
|
+
* the model name after initial render, everything else downstream is
|
|
14
|
+
* certainly broken. It's also the cheapest — one navigation, one
|
|
15
|
+
* text query per model.
|
|
16
|
+
*/
|
|
17
|
+
import type { UiContractRule } from '../test-case-types.js';
|
|
18
|
+
export declare const RULE_ID = "nav-has-model-entries";
|
|
19
|
+
export declare const navHasModelEntries: UiContractRule;
|
|
20
|
+
//# sourceMappingURL=nav-has-model-entries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-has-model-entries.d.ts","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/nav-has-model-entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAEtF,eAAO,MAAM,OAAO,0BAA0B,CAAC;AAE/C,eAAO,MAAM,kBAAkB,EAAE,cAUhC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: nav-has-model-entries
|
|
3
|
+
*
|
|
4
|
+
* For every model declared in the spec, assert that the runtime UI
|
|
5
|
+
* exposes a visible reference to the model (nav entry, sidebar link,
|
|
6
|
+
* dashboard card — any of them count). v1 uses a loose "text appears
|
|
7
|
+
* on the page" assertion; a stricter ARIA-role variant can come
|
|
8
|
+
* later once the runtime standardizes its nav component's semantics.
|
|
9
|
+
*
|
|
10
|
+
* One TestCase per model.
|
|
11
|
+
*
|
|
12
|
+
* This is the most important rule: if the runtime can't even show
|
|
13
|
+
* the model name after initial render, everything else downstream is
|
|
14
|
+
* certainly broken. It's also the cheapest — one navigation, one
|
|
15
|
+
* text query per model.
|
|
16
|
+
*/
|
|
17
|
+
export const RULE_ID = 'nav-has-model-entries';
|
|
18
|
+
export const navHasModelEntries = (spec) => {
|
|
19
|
+
return spec.models.map(model => ({
|
|
20
|
+
ruleId: RULE_ID,
|
|
21
|
+
specElement: model.name,
|
|
22
|
+
name: `[${RULE_ID}] ${model.name} is visible in the nav`,
|
|
23
|
+
steps: [
|
|
24
|
+
{ action: 'bootRuntime' },
|
|
25
|
+
{ action: 'expectNavHasModel', modelName: model.name },
|
|
26
|
+
],
|
|
27
|
+
}));
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=nav-has-model-entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-has-model-entries.js","sourceRoot":"","sources":["../../../../src/inference/ui-contracts/rules/nav-has-model-entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,CAAC,MAAM,OAAO,GAAG,uBAAuB,CAAC;AAE/C,MAAM,CAAC,MAAM,kBAAkB,GAAmB,CAAC,IAAoB,EAAc,EAAE;IACrF,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,OAAO;QACf,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,IAAI,EAAE,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,wBAAwB;QACxD,KAAK,EAAE;YACL,EAAE,MAAM,EAAE,aAAa,EAAE;YACzB,EAAE,MAAM,EAAE,mBAAmB,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;SACvD;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC"}
|