@sun-asterisk/sungen 3.2.16-beta.7 → 3.2.16-beta.9
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/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +17 -3
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/gates.js +6 -1
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +3 -0
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/wording.d.ts +10 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +52 -0
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +11 -2
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/blindspot.d.ts.map +1 -1
- package/dist/harness/blindspot.js +2 -1
- package/dist/harness/blindspot.js.map +1 -1
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +3 -2
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/feedback.d.ts.map +1 -1
- package/dist/harness/feedback.js +3 -2
- package/dist/harness/feedback.js.map +1 -1
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +2 -1
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/flow-plan.d.ts.map +1 -1
- package/dist/harness/flow-plan.js +3 -2
- package/dist/harness/flow-plan.js.map +1 -1
- package/dist/harness/intent.d.ts.map +1 -1
- package/dist/harness/intent.js +2 -1
- package/dist/harness/intent.js.map +1 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +3 -2
- package/dist/harness/journey.js.map +1 -1
- package/dist/harness/ledger.d.ts.map +1 -1
- package/dist/harness/ledger.js +3 -2
- package/dist/harness/ledger.js.map +1 -1
- package/dist/harness/manifest.d.ts.map +1 -1
- package/dist/harness/manifest.js +4 -3
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +16 -3
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +2 -1
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/read-text.d.ts +22 -0
- package/dist/harness/read-text.d.ts.map +1 -0
- package/dist/harness/read-text.js +64 -0
- package/dist/harness/read-text.js.map +1 -0
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +3 -2
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +2 -10
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts +5 -0
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +17 -7
- package/dist/harness/spec-coverage.js.map +1 -1
- package/dist/harness/trace.d.ts.map +1 -1
- package/dist/harness/trace.js +4 -3
- package/dist/harness/trace.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +2 -1
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +9 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +5 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +22 -0
- package/package.json +4 -4
- package/src/exporters/matrix/build.ts +24 -4
- package/src/exporters/matrix/gates.ts +6 -1
- package/src/exporters/matrix/types.ts +3 -0
- package/src/exporters/matrix/wording.ts +47 -0
- package/src/harness/audit.ts +11 -2
- package/src/harness/blindspot.ts +2 -1
- package/src/harness/capability-plan.ts +3 -2
- package/src/harness/feedback.ts +3 -2
- package/src/harness/flow-check.ts +2 -1
- package/src/harness/flow-plan.ts +3 -2
- package/src/harness/intent.ts +2 -1
- package/src/harness/journey.ts +3 -2
- package/src/harness/ledger.ts +3 -2
- package/src/harness/manifest.ts +4 -3
- package/src/harness/parse.ts +17 -3
- package/src/harness/quality-gates.ts +2 -1
- package/src/harness/read-text.ts +28 -0
- package/src/harness/script-check.ts +3 -2
- package/src/harness/sensors.ts +2 -1
- package/src/harness/spec-coverage.ts +22 -7
- package/src/harness/trace.ts +4 -3
- package/src/harness/viewpoint-ledger.ts +2 -1
- package/src/orchestrator/templates/ai-src/commands/create-test.md +9 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +5 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +22 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-coverage.js","sourceRoot":"","sources":["../../src/harness/spec-coverage.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"spec-coverage.js","sourceRoot":"","sources":["../../src/harness/spec-coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/harness/trace.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/harness/trace.ts"],"names":[],"mappings":"AAmBA,UAAU,UAAU;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAiCzD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACtG,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAqE7E"}
|
package/dist/harness/trace.js
CHANGED
|
@@ -51,9 +51,10 @@ const fs = __importStar(require("fs"));
|
|
|
51
51
|
const path = __importStar(require("path"));
|
|
52
52
|
const unit_paths_1 = require("./unit-paths");
|
|
53
53
|
const ledger_1 = require("./ledger");
|
|
54
|
+
const read_text_1 = require("./read-text");
|
|
54
55
|
function readJson(p) {
|
|
55
56
|
try {
|
|
56
|
-
return fs.existsSync(p) ? JSON.parse(
|
|
57
|
+
return fs.existsSync(p) ? JSON.parse((0, read_text_1.readTextFile)(p)) : null;
|
|
57
58
|
}
|
|
58
59
|
catch {
|
|
59
60
|
return null;
|
|
@@ -63,7 +64,7 @@ function readLedger(screen) {
|
|
|
63
64
|
const p = path.join(process.cwd(), '.sungen', 'ledger', `${(0, unit_paths_1.reportSlug)(screen)}.jsonl`);
|
|
64
65
|
if (!fs.existsSync(p))
|
|
65
66
|
return [];
|
|
66
|
-
return
|
|
67
|
+
return (0, read_text_1.readTextFile)(p).split('\n').filter(Boolean).map((l) => { try {
|
|
67
68
|
return JSON.parse(l);
|
|
68
69
|
}
|
|
69
70
|
catch {
|
|
@@ -74,7 +75,7 @@ function readLedger(screen) {
|
|
|
74
75
|
function parseManual(featurePath) {
|
|
75
76
|
if (!fs.existsSync(featurePath))
|
|
76
77
|
return [];
|
|
77
|
-
const lines =
|
|
78
|
+
const lines = (0, read_text_1.readTextFile)(featurePath).split('\n');
|
|
78
79
|
const out = [];
|
|
79
80
|
for (let i = 0; i < lines.length; i++) {
|
|
80
81
|
const m = lines[i].match(/^\s*Scenario:\s*(.+)$/);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/harness/trace.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/harness/trace.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewpoint-ledger.d.ts","sourceRoot":"","sources":["../../src/harness/viewpoint-ledger.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"viewpoint-ledger.d.ts","sourceRoot":"","sources":["../../src/harness/viewpoint-ledger.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,WAAW,UAAU;IAAG,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAE3E,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAKD,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CA4B1F;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAsBnH"}
|
|
@@ -46,13 +46,14 @@ exports.viewpointLedger = viewpointLedger;
|
|
|
46
46
|
* gaps that inflate a "looks-covered" score; it does not fail the gate.
|
|
47
47
|
*/
|
|
48
48
|
const fs = __importStar(require("fs"));
|
|
49
|
+
const read_text_1 = require("./read-text");
|
|
49
50
|
const ID_RE = /\b([A-Z]{1,5}\d{0,2}(?:[.\-][A-Za-z0-9]+)*-?\d{0,3})\b/; // VP0.Title, VP7-002, MS-HP-001, TV-01
|
|
50
51
|
const GENERIC = new Set(['display', 'shown', 'value', 'field', 'input', 'page', 'screen', 'button', 'link', 'text', 'check', 'verify', 'should', 'with', 'when', 'then', 'user', 'this', 'that', 'each', 'item', 'items']);
|
|
51
52
|
/** Extract atomic checklist items from a viewpoint file (format-tolerant). */
|
|
52
53
|
function parseViewpointItems(viewpointPath) {
|
|
53
54
|
if (!fs.existsSync(viewpointPath))
|
|
54
55
|
return [];
|
|
55
|
-
const lines =
|
|
56
|
+
const lines = (0, read_text_1.readTextFile)(viewpointPath).split('\n');
|
|
56
57
|
const items = [];
|
|
57
58
|
let inFence = false;
|
|
58
59
|
for (const raw of lines) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewpoint-ledger.js","sourceRoot":"","sources":["../../src/harness/viewpoint-ledger.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"viewpoint-ledger.js","sourceRoot":"","sources":["../../src/harness/viewpoint-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -68,6 +68,15 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
|
|
|
68
68
|
If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the design loop differs — **no visual capture, no selectors**; the contract is the named-endpoint catalog. **Follow the `sungen-api-design` + `sungen-api-coverage-model` skills end-to-end** instead of the screen/flow steps: `sungen context --area <name>` (discover endpoints + `fields:`) → **enumerate the Tier-1 case list per endpoint from the coverage model** (contract + not-found/required-matrix + auth + idempotency, expanded mechanically from `fields:`) → generate `@api`/`@cases`/flow/`@concurrent`/`@query` scenarios with **strict assertions** (prove the effect, never status-only) → **`sungen audit --area <name>` gate + reviewer + repair loop to businessDepth ≥ 0.7** → record + trace. Then recommend `/sungen-run-test <name>`. The capture / viewpoint-group / selector steps do **not** apply.
|
|
69
69
|
{{/cap}}
|
|
70
70
|
|
|
71
|
+
## Requirement traceability (before you finish)
|
|
72
|
+
|
|
73
|
+
Cross-check `requirements/spec.md` against the scenarios you wrote: every `FR-`/`TR-`/`NFR-` id
|
|
74
|
+
must either carry a `@spec:<id>` tag on the scenario that proves it, or be a conscious
|
|
75
|
+
out-of-scope decision you state in the summary. `sungen audit` reports `SPEC-TRACE-IMPLICIT` for
|
|
76
|
+
requirements it could only match by keyword — treat that list as a to-do: add the tag to the
|
|
77
|
+
proving scenario, do not leave the link to inference. Delivery's requirement table follows the
|
|
78
|
+
tag only, so an untagged requirement is later reported as an uncovered gap.
|
|
79
|
+
|
|
71
80
|
## Steps
|
|
72
81
|
|
|
73
82
|
{{#cap parallel-subagents}}
|
|
@@ -89,6 +89,11 @@ target × category grid with explicit `—` gaps, dispositions, manifest. CSV mi
|
|
|
89
89
|
flat with a `Level` column (`item`/`variant`) + a requirement-coverage appendix.
|
|
90
90
|
`delivery_item_count` ≠ progress — variants are the execution metric.
|
|
91
91
|
|
|
92
|
+
**Runtime-captured values**: a scenario may produce its own values — `User remember [X] text as
|
|
93
|
+
{{v}}` (compared later) or a capability binding (`@query:`/`@api:`/`@dataFactory:`). These never
|
|
94
|
+
exist in test-data, so Gate D does not ask for them: the capture step renders as an instruction
|
|
95
|
+
(`Note the X text for later (as "v")`) and later references read `the captured v`.
|
|
96
|
+
|
|
92
97
|
**Data fidelity**: invisible test data is made visible, never normalized — `''` → `(empty)`,
|
|
93
98
|
whitespace-only → `(5 spaces)`, padded → `" value "` quoted verbatim. A trim/collapse here would
|
|
94
99
|
silently break the whitespace tests it describes.
|
|
@@ -288,6 +288,7 @@ Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `colu
|
|
|
288
288
|
|
|
289
289
|
| Tag | Effect |
|
|
290
290
|
|---|---|
|
|
291
|
+
| `@spec:<id>` | **Traceability**: this scenario proves requirement `<id>` from `requirements/spec.md` (`@spec:FR-003`). Repeatable — a scenario may prove several. Read by `sungen audit` (requirement coverage) and by delivery (the Coverage sheet's requirement table). Without it the link is only inferred from wording and cannot survive an edit. |
|
|
291
292
|
| `@manual` | Skip in generation |
|
|
292
293
|
| `@auth:role` | Use auth storage state for role |
|
|
293
294
|
| `@no-auth` | Disable inherited auth |
|
|
@@ -4,6 +4,28 @@ description: 'Use when create-test needs to translate spec.md/Figma/UI into .fea
|
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
## Traceability — tag the requirement each scenario proves
|
|
9
|
+
|
|
10
|
+
Every requirement id in `requirements/spec.md` (`FR-`/`TR-`/`NFR-`) must be reachable from a
|
|
11
|
+
scenario, and the link must be **written down, not inferred**:
|
|
12
|
+
|
|
13
|
+
```gherkin
|
|
14
|
+
@high @spec:FR-003
|
|
15
|
+
Scenario: VP-VAL-002 An email in an invalid format shows the format error
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Put `@spec:<id>` on the scenario that actually proves the requirement; repeat the tag when one
|
|
19
|
+
scenario proves several (`@spec:FR-003 @spec:FR-004`).
|
|
20
|
+
- One requirement may be proved by several scenarios — tag each of them.
|
|
21
|
+
- A requirement you deliberately do not cover here (performance budget, i18n suite, server-side
|
|
22
|
+
concern) needs no tag; record it later in the delivery map's `requirements:` section with a
|
|
23
|
+
status and reason.
|
|
24
|
+
- Why it matters: `sungen audit` also counts a requirement as covered when its keywords merely
|
|
25
|
+
resemble a scenario title, and reports `SPEC-TRACE-IMPLICIT` for those. Delivery's requirement
|
|
26
|
+
table follows **only** the explicit tag — an untagged requirement reads as an uncovered gap in
|
|
27
|
+
the customer-facing coverage sheet even when a scenario does prove it.
|
|
28
|
+
|
|
7
29
|
## ⚠️ Gotchas — read before generating
|
|
8
30
|
|
|
9
31
|
- **Field-level test-data follows the Data Factory standard — don't hand-invent values.** When the unit has input fields, the field values (valid / boundary / invalid + error codes) come from the **`sungen-data-factory`** catalog via a field-map + `sungen data gen` (create-test step 5.3), not free-form guessing. Weave the standardized boundary/invalid sets into `@cases` with `CHK-*` trace. This skill still owns scenario structure, viewpoints, and non-field oracles.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.16-beta.
|
|
3
|
+
"version": "3.2.16-beta.9",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && mkdir -p dist/generators/test-generator/adapters/appium/templates/steps && cp -r src/generators/test-generator/adapters/appium/templates/*.hbs dist/generators/test-generator/adapters/appium/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/appium/templates/steps dist/generators/test-generator/adapters/appium/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
|
|
13
13
|
"build:dashboard": "cd ../../dashboard && npm install --silent && npm run build && cd - && cp ../../dashboard/dist/index.html src/dashboard/templates/index.html",
|
|
14
14
|
"dev": "tsx src/cli/index.ts",
|
|
15
|
-
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/delivery-matrix.run.ts && tsx tests/exporter/delivery-scope-strictness.run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/exporter/delivery-prerun-overview.run.ts && tsx tests/exporter/delivery-locale-env-discovery.run.ts && tsx tests/exporter/manual-testcase-testdata.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/codegen/steps-base-not-compiled.run.ts && tsx tests/codegen/manual-oracle-gate.run.ts && tsx tests/codegen/locator-value-qualified-assertions.run.ts && tsx tests/codegen/table-match-data-column-mapping.run.ts && tsx tests/codegen/param-binding-native-type.run.ts && tsx tests/codegen/table-row-scope-redeclaration.run.ts && tsx tests/codegen/table-row-label-resolution.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/runtime/test-data-array-projection.run.ts && tsx tests/runtime/env-secret-interpolation.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/cosmos-engine.run.ts && tsx tests/db-runtime/mongodb-engine.run.ts && tsx tests/db-runtime/dynamodb-engine.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts && tsx tests/db-runtime/mongodb-integration.opt-in.run.ts && tsx tests/db-runtime/dynamodb-integration.opt-in.run.ts",
|
|
15
|
+
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/delivery-matrix.run.ts && tsx tests/exporter/delivery-scope-strictness.run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/exporter/delivery-prerun-overview.run.ts && tsx tests/exporter/delivery-locale-env-discovery.run.ts && tsx tests/exporter/manual-testcase-testdata.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/codegen/steps-base-not-compiled.run.ts && tsx tests/codegen/manual-oracle-gate.run.ts && tsx tests/codegen/locator-value-qualified-assertions.run.ts && tsx tests/codegen/table-match-data-column-mapping.run.ts && tsx tests/codegen/param-binding-native-type.run.ts && tsx tests/codegen/table-row-scope-redeclaration.run.ts && tsx tests/codegen/table-row-label-resolution.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/runtime/test-data-array-projection.run.ts && tsx tests/runtime/env-secret-interpolation.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/harness/spec-trace-crlf.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/cosmos-engine.run.ts && tsx tests/db-runtime/mongodb-engine.run.ts && tsx tests/db-runtime/dynamodb-engine.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts && tsx tests/db-runtime/mongodb-integration.opt-in.run.ts && tsx tests/db-runtime/dynamodb-integration.opt-in.run.ts",
|
|
16
16
|
"test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update && tsx tests/ingest/run.ts --update",
|
|
17
17
|
"prepublishOnly": "npm run build:dashboard && npm run build"
|
|
18
18
|
},
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@babel/types": "^7.28.5",
|
|
40
40
|
"@cucumber/gherkin": "^37.0.0",
|
|
41
41
|
"@cucumber/messages": "^31.0.0",
|
|
42
|
-
"@sungen/driver-data-factory": "3.2.16-beta.
|
|
43
|
-
"@sungen/driver-ui": "3.2.16-beta.
|
|
42
|
+
"@sungen/driver-data-factory": "3.2.16-beta.9",
|
|
43
|
+
"@sungen/driver-ui": "3.2.16-beta.9",
|
|
44
44
|
"chalk": "^5.6.2",
|
|
45
45
|
"commander": "^14.0.2",
|
|
46
46
|
"dotenv": "^17.2.3",
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
splitVpAndName,
|
|
15
15
|
} from '../feature-parser';
|
|
16
16
|
import { getCasesDatasetRows, resolveResultVariants } from '../result-variants';
|
|
17
|
-
import { classifyManualComments, renderAction, renderExpected, renderPrecondition, renderSetupInstruction } from './wording';
|
|
17
|
+
import { classifyManualComments, renderAction, renderExpected, renderPrecondition, renderSetupInstruction, runtimeVarsOf } from './wording';
|
|
18
18
|
import { scenarioFingerprint, combinedFingerprint, mapContentFingerprint } from './fingerprint';
|
|
19
19
|
import {
|
|
20
20
|
CoverageVariant,
|
|
@@ -129,6 +129,14 @@ export function deriveVariants(inputs: Pick<BuildInputs, 'feature' | 'merged' |
|
|
|
129
129
|
? (manual.expected.length > 0 ? manual.expected : m.feature.rawThenSteps)
|
|
130
130
|
: m.resolvedExpected.filter((s) => s.bucket === 'then').map((s) => s.text);
|
|
131
131
|
|
|
132
|
+
// Values the scenario itself produces during the run — captured from the page
|
|
133
|
+
// or bound by a capability precondition. The compiler registers these as
|
|
134
|
+
// "captured" and skips YAML validation; the matrix does the same.
|
|
135
|
+
const runtimeVars = runtimeVarsOf(
|
|
136
|
+
[...m.resolvedSteps.map((st) => st.text), ...m.resolvedExpected.map((st) => st.text)],
|
|
137
|
+
tags,
|
|
138
|
+
);
|
|
139
|
+
|
|
132
140
|
const preconditionProfile = [
|
|
133
141
|
authRole ?? '-',
|
|
134
142
|
m.feature.extendsName ?? '-',
|
|
@@ -153,6 +161,7 @@ export function deriveVariants(inputs: Pick<BuildInputs, 'feature' | 'merged' |
|
|
|
153
161
|
manualReason: manualReason(tags),
|
|
154
162
|
layers: deriveLayers(tags),
|
|
155
163
|
traces: tags.filter((t) => t.startsWith('@spec:')).map((t) => t.slice('@spec:'.length)),
|
|
164
|
+
runtimeVars: [...runtimeVars],
|
|
156
165
|
triggerShape: rawTrigger.map(normalizeShape),
|
|
157
166
|
oracleShape: rawOracle.map(normalizeShape),
|
|
158
167
|
preconditionProfile,
|
|
@@ -163,7 +172,7 @@ export function deriveVariants(inputs: Pick<BuildInputs, 'feature' | 'merged' |
|
|
|
163
172
|
|
|
164
173
|
/** Render the display cells for one data context (vars resolved first). */
|
|
165
174
|
const renderCells = (vars: Record<string, string>): { trigger: string[]; oracle: string[]; precondition: string[]; verification: string[] } => {
|
|
166
|
-
const sub = (s: string): string => substituteDisplayVars(s, vars);
|
|
175
|
+
const sub = (s: string): string => substituteDisplayVars(s, vars, runtimeVars);
|
|
167
176
|
const preconditionOut = precondition.map(sub);
|
|
168
177
|
const verificationOut = base.verification.map(sub);
|
|
169
178
|
if (procedureProfile === 'sequence' && !manual) {
|
|
@@ -245,8 +254,19 @@ export function deriveVariants(inputs: Pick<BuildInputs, 'feature' | 'merged' |
|
|
|
245
254
|
* empty field) — it renders as `(empty)` so the tester sees the intent and no
|
|
246
255
|
* `{{token}}` survives into the deliverable (Gate D would reject it).
|
|
247
256
|
*/
|
|
248
|
-
function substituteDisplayVars(
|
|
249
|
-
|
|
257
|
+
function substituteDisplayVars(
|
|
258
|
+
text: string,
|
|
259
|
+
vars: Record<string, string>,
|
|
260
|
+
runtimeVars: Set<string> = new Set(),
|
|
261
|
+
): string {
|
|
262
|
+
// `remember … as {{v}}` DECLARES v — leave that token for renderAction, which
|
|
263
|
+
// turns it into a quoted name ("note it as …"), not a value to look up.
|
|
264
|
+
const isCapture = /\bremember\b/i.test(text);
|
|
265
|
+
return text.replace(/\{\{\s*([^}\s]+)\s*\}\}/g, (m, key: string, offset: number) => {
|
|
266
|
+
if (isCapture && /\bas\s+$/i.test(text.slice(0, offset))) return m;
|
|
267
|
+
// A value the test produces at run time has no static form — name it so the
|
|
268
|
+
// tester knows to compare against what the earlier step captured.
|
|
269
|
+
if (runtimeVars.has(key) || runtimeVars.has(key.split(/[.[]/)[0])) return `the captured ${key}`;
|
|
250
270
|
if (!(key in vars)) return m; // unknown stays literal → Gate D flags it
|
|
251
271
|
return displayValue(vars[key]);
|
|
252
272
|
});
|
|
@@ -264,9 +264,14 @@ function gateDExecutability(ctx: GateContext, findings: MatrixFinding[]): void {
|
|
|
264
264
|
for (const [vpId, vs] of variantsByVp) {
|
|
265
265
|
const scenario = ctx.inputs.merged.find((m) => splitVpAndName(m.feature.name).vpId === vpId);
|
|
266
266
|
if (!scenario) continue;
|
|
267
|
+
// Values produced during the run (`remember … as {{v}}`, capability bindings)
|
|
268
|
+
// are provided by the test itself — demanding a test-data entry for them
|
|
269
|
+
// blocked 14 scenarios of a real suite that compiles and runs fine.
|
|
270
|
+
const runtime = new Set(vs.flatMap((v) => v.runtimeVars));
|
|
267
271
|
for (const varName of scenario.feature.referencedVars) {
|
|
268
272
|
const inRows = vs.some((v) => v.caseLabel !== undefined); // dataset rows carry their own columns
|
|
269
|
-
|
|
273
|
+
const atRuntime = runtime.has(varName) || runtime.has(varName.split(/[.[]/)[0]);
|
|
274
|
+
if (!inRows && !atRuntime && (!testData || !(varName in testData))) {
|
|
270
275
|
findings.push({
|
|
271
276
|
gate: 'D', severity: 'error', ref: vpId,
|
|
272
277
|
message: `${vpId}: {{${varName}}} does not resolve in test-data — the variant is not reproducible`,
|
|
@@ -95,6 +95,9 @@ export interface CoverageVariant {
|
|
|
95
95
|
layers: MatrixLayer[];
|
|
96
96
|
/** @spec:* requirement traces. */
|
|
97
97
|
traces: string[];
|
|
98
|
+
/** Variables this scenario produces at RUN TIME (`remember … as {{v}}`, capability
|
|
99
|
+
* bindings) — they never exist in test-data, so Gate D must not demand them. */
|
|
100
|
+
runtimeVars: string[];
|
|
98
101
|
/** Condition summary: differentiating dimension shown on the sub-row. */
|
|
99
102
|
condition: string;
|
|
100
103
|
/** Resolved data pairs (test-data values / dataset-row columns), `key: value` lines. */
|
|
@@ -13,6 +13,32 @@
|
|
|
13
13
|
* Action → action, Observable → expected, Oracle → verification method.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
/** A step that CAPTURES a runtime value: `User remember [X] text as {{var}}`. */
|
|
17
|
+
export const RUNTIME_CAPTURE = /\bremember\b/i;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Variable names a scenario defines at RUN TIME rather than reading from
|
|
21
|
+
* test-data: values captured from the page (`remember … as {{v}}`) and values
|
|
22
|
+
* bound by a capability precondition tag (`@query:`, `@api:`, `@dataFactory:`).
|
|
23
|
+
* The compiler already treats these as "captured" (DataResolver.registerCaptured);
|
|
24
|
+
* the delivery matrix must agree or it demands a static value that cannot exist.
|
|
25
|
+
*/
|
|
26
|
+
export function runtimeVarsOf(stepTexts: string[], tags: string[]): Set<string> {
|
|
27
|
+
const out = new Set<string>();
|
|
28
|
+
for (const text of stepTexts) {
|
|
29
|
+
if (!RUNTIME_CAPTURE.test(text)) continue;
|
|
30
|
+
const m = text.match(/\bas\s+\{\{\s*([^}]+?)\s*\}\}/i);
|
|
31
|
+
if (m) out.add(m[1].trim());
|
|
32
|
+
}
|
|
33
|
+
// A capability precondition binds its result under the endpoint/query/recipe
|
|
34
|
+
// name, so `{{<name>}}` and `{{<name>.field}}` exist only during the run.
|
|
35
|
+
for (const t of tags) {
|
|
36
|
+
const m = t.match(/^@(?:query|api|dataFactory):([^(\s]+)/);
|
|
37
|
+
if (m) out.add(m[1]);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
// `[Email] field` → `Email field` (the visible label + its element type).
|
|
17
43
|
function deRef(text: string): string {
|
|
18
44
|
return text.replace(/\[([^\]]+)\]/g, '$1');
|
|
@@ -36,8 +62,15 @@ function sentence(text: string): string {
|
|
|
36
62
|
*/
|
|
37
63
|
export function renderAction(raw: string): string {
|
|
38
64
|
let s = raw.trim().replace(/^(User|The user)\s+/i, '');
|
|
65
|
+
// `remember … as {{v}}` DECLARES a runtime variable — the trailing token is a
|
|
66
|
+
// name, not a value to resolve. Quote it so no `{{brace}}` reaches the sheet
|
|
67
|
+
// whatever rule matches below.
|
|
68
|
+
if (RUNTIME_CAPTURE.test(s)) s = s.replace(/\bas\s+\{\{\s*([^}]+?)\s*\}\}/i, 'as "$1"');
|
|
39
69
|
|
|
40
70
|
const rules: Array<[RegExp, (m: RegExpMatchArray) => string]> = [
|
|
71
|
+
// remember [X] (text|value) as "v" — capture a value for a later comparison
|
|
72
|
+
[/^remembers? \[([^\]]+)\]\s*(text|value)?\s*as "([^"]+)"$/i,
|
|
73
|
+
(m) => `Note the ${m[1]} ${m[2] || 'text'} for later (as "${m[3]}")`],
|
|
41
74
|
// fill [X] field with V
|
|
42
75
|
[/^fills? \[([^\]]+)\][a-z ]* with (.+)$/i, (m) => `Enter ${m[2]} in the ${m[1]} field`],
|
|
43
76
|
// clear [X] field
|
|
@@ -75,9 +108,23 @@ export function renderAction(raw: string): string {
|
|
|
75
108
|
* `should`, no DSL `User see`).
|
|
76
109
|
*/
|
|
77
110
|
export function renderExpected(raw: string): string {
|
|
111
|
+
// A capture step is an INSTRUCTION wherever it sits — Gherkin often chains
|
|
112
|
+
// `remember … / remember … / expect a is not b` inside one Then block. Render
|
|
113
|
+
// it as the note-it-down action, never as an assertion (and never leaving the
|
|
114
|
+
// declared `{{name}}` token behind).
|
|
115
|
+
if (RUNTIME_CAPTURE.test(raw) && /\bas\s+\{\{/.test(raw)) return renderAction(raw);
|
|
78
116
|
let s = raw.trim().replace(/^(User|The user)\s+/i, '');
|
|
79
117
|
|
|
80
118
|
const rules: Array<[RegExp, (m: RegExpMatchArray) => string]> = [
|
|
119
|
+
// expect A is (not) B — comparing two captured values (order matters: "is not" first)
|
|
120
|
+
[/^expects? (.+?) is not (.+)$/i, (m) => `${m[1]} differs from ${m[2]}`],
|
|
121
|
+
[/^expects? (.+?) is (.+)$/i, (m) => `${m[1]} equals ${m[2]}`],
|
|
122
|
+
// see all [X] <type> contain(s) V — every matching element, not just the first
|
|
123
|
+
[/^sees? all \[([^\]]+)\]\s*(\w+)? (?:contains?|match(?:es)?|belongs?) (.+)$/i,
|
|
124
|
+
(m) => `Every ${m[1]}${m[2] ? ` ${m[2]}` : ''} contains ${m[3]}`],
|
|
125
|
+
// see all [X] <type> contain [Child] <type> — structural per-item proof
|
|
126
|
+
[/^sees? all \[([^\]]+)\]\s*(\w+)? (?:contains?|includes?) \[([^\]]+)\]\s*(\w+)?$/i,
|
|
127
|
+
(m) => `Every ${m[1]}${m[2] ? ` ${m[2]}` : ''} contains a ${m[3]}${m[4] ? ` ${m[4]}` : ''}`],
|
|
81
128
|
// see [X] page
|
|
82
129
|
[/^sees? \[([^\]]+)\] page$/i, (m) => `The ${m[1]} page is displayed`],
|
|
83
130
|
// see [X] <type> with V
|
package/src/harness/audit.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { viewpointLedger, parseViewpointItems, LedgerResult } from './viewpoint-
|
|
|
27
27
|
import { capabilityRegistry } from '../capabilities/registry';
|
|
28
28
|
import { discoverAndRegisterCapabilities } from '../capabilities/discover';
|
|
29
29
|
import { contextRouter } from '../capabilities/context-router';
|
|
30
|
+
import { readTextFile } from './read-text';
|
|
30
31
|
|
|
31
32
|
export interface AuditReport {
|
|
32
33
|
screen: string;
|
|
@@ -118,7 +119,7 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
|
|
|
118
119
|
const catalogScreenName = catalogIdFromScreenDir(screenDir);
|
|
119
120
|
|
|
120
121
|
const specPath = path.join(screenDir, 'requirements', 'spec.md');
|
|
121
|
-
const featureText = fs.existsSync(featurePath) ?
|
|
122
|
+
const featureText = fs.existsSync(featurePath) ? readTextFile(featurePath) : '';
|
|
122
123
|
|
|
123
124
|
const scenarios: ScenarioInfo[] = loadScenarios(featurePath);
|
|
124
125
|
const viewpoints: ViewpointEntry[] = parseViewpointOverview(viewpointPath);
|
|
@@ -225,7 +226,7 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
|
|
|
225
226
|
findings.push(`COVERED-VIA-FLOW: critical theme "${c.theme}" is not covered on this screen but is deeply covered by flow "${c.flow}" — cross-screen depth correctly owned by the flow, so it is credited (not a screen gap). Verify with \`sungen flow-check\`.`);
|
|
226
227
|
}
|
|
227
228
|
// Verification-scope (advisory): API/DB verification the project's recorded test type never asked for.
|
|
228
|
-
for (const v of verificationScopeFindings(featureText, fs.existsSync(viewpointPath) ?
|
|
229
|
+
for (const v of verificationScopeFindings(featureText, fs.existsSync(viewpointPath) ? readTextFile(viewpointPath) : '', capProfile)) {
|
|
229
230
|
findings.push(`VERIFICATION-OUT-OF-SCOPE (${v.layer}): this unit ${v.message}`);
|
|
230
231
|
}
|
|
231
232
|
for (const g of gate.gaps) {
|
|
@@ -289,6 +290,14 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
|
|
|
289
290
|
for (const u of spec.uncoveredMust) {
|
|
290
291
|
findings.push(`SPEC-UNCOVERED: ${u.id} (MUST) has no covering scenario — "${u.text}" → add a scenario or tag one @spec:${u.id}.`);
|
|
291
292
|
}
|
|
293
|
+
if (spec.inferredOnly.length > 0) {
|
|
294
|
+
// Covered, but only by keyword inference — no scenario cites the id. Delivery's
|
|
295
|
+
// requirement table (and any later refactor) cannot follow an inferred link, so
|
|
296
|
+
// the trace has to be written down while the author still knows which scenario
|
|
297
|
+
// proves what.
|
|
298
|
+
const shown = spec.inferredOnly.slice(0, 8).join(', ');
|
|
299
|
+
findings.push(`SPEC-TRACE-IMPLICIT: ${spec.inferredOnly.length} requirement(s) are covered by keyword inference only (${shown}${spec.inferredOnly.length > 8 ? ', …' : ''}) — add \`@spec:<id>\` to the scenario that proves each so the trace survives edits and delivery can report it.`);
|
|
300
|
+
}
|
|
292
301
|
if (taxonomyMismatch) {
|
|
293
302
|
findings.push(`VP-TAXONOMY-MISMATCH: only ${(trace.mappedRatio * 100).toFixed(0)}% of scenarios use the viewpoint IDs declared in test-viewpoint.md — scenarios invented a generic VP-<CAT> scheme. Re-tag to the project's viewpoint IDs so the coverage matrix is accurate.`);
|
|
294
303
|
}
|
package/src/harness/blindspot.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
|
+
import { readTextFile } from './read-text';
|
|
13
14
|
|
|
14
15
|
export interface Blindspot {
|
|
15
16
|
ts: string;
|
|
@@ -35,7 +36,7 @@ export function addBlindspot(entry: Omit<Blindspot, 'ts'> & { ts?: string }): st
|
|
|
35
36
|
export function listBlindspots(): Blindspot[] {
|
|
36
37
|
const p = storePath();
|
|
37
38
|
if (!fs.existsSync(p)) return [];
|
|
38
|
-
return
|
|
39
|
+
return readTextFile(p).split('\n').filter(Boolean).map((l) => {
|
|
39
40
|
try { return JSON.parse(l) as Blindspot; } catch { return null; }
|
|
40
41
|
}).filter(Boolean) as Blindspot[];
|
|
41
42
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { loadDriverCatalog } from './capability';
|
|
12
|
+
import { readTextFile } from './read-text';
|
|
12
13
|
|
|
13
14
|
export type ReasonClass = 'capability' | 'keep' | 'flow';
|
|
14
15
|
export interface ReasonDef { code: string; label: string; cls: ReasonClass; drivers: string[] }
|
|
@@ -61,7 +62,7 @@ interface ParsedScenario { name: string; tags: string[]; manual: boolean; reason
|
|
|
61
62
|
/** Parse scenarios with their tags + the reason comment line above (for @manual). */
|
|
62
63
|
export function parseScenarios(featurePath: string): ParsedScenario[] {
|
|
63
64
|
if (!fs.existsSync(featurePath)) return [];
|
|
64
|
-
const lines =
|
|
65
|
+
const lines = readTextFile(featurePath).split('\n');
|
|
65
66
|
const out: ParsedScenario[] = [];
|
|
66
67
|
for (let i = 0; i < lines.length; i++) {
|
|
67
68
|
const m = lines[i].match(/^\s*Scenario:\s*(.+)$/);
|
|
@@ -123,7 +124,7 @@ export interface ReasonMismatch { scenario: string; explicit: string; inferred:
|
|
|
123
124
|
*/
|
|
124
125
|
export function manualReasonMismatches(featurePath: string): ReasonMismatch[] {
|
|
125
126
|
if (!fs.existsSync(featurePath)) return [];
|
|
126
|
-
const lines =
|
|
127
|
+
const lines = readTextFile(featurePath).split('\n');
|
|
127
128
|
const out: ReasonMismatch[] = [];
|
|
128
129
|
for (let i = 0; i < lines.length; i++) {
|
|
129
130
|
const m = lines[i].match(/^\s*Scenario:\s*(.+)$/);
|
package/src/harness/feedback.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
|
+
import { readTextFile } from './read-text';
|
|
15
16
|
|
|
16
17
|
export type FeedbackType = 'test-design' | 'product' | 'other';
|
|
17
18
|
export type FeedbackDecision = 'accept' | 'reject' | 'edit' | 'add' | 'none';
|
|
@@ -36,7 +37,7 @@ function currentAuditScore(screen?: string): number | undefined {
|
|
|
36
37
|
if (!screen) return undefined;
|
|
37
38
|
const p = path.join(process.cwd(), '.sungen', 'reports', `${screen}-audit.json`);
|
|
38
39
|
if (!fs.existsSync(p)) return undefined;
|
|
39
|
-
try { return JSON.parse(
|
|
40
|
+
try { return JSON.parse(readTextFile(p))?.score?.overall; } catch { return undefined; }
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export function recordFeedback(entry: Omit<FeedbackEntry, 'ts' | 'source' | 'auditScore'> & { ts?: string; source?: string }): string {
|
|
@@ -60,7 +61,7 @@ export function recordFeedback(entry: Omit<FeedbackEntry, 'ts' | 'source' | 'aud
|
|
|
60
61
|
export function readFeedback(): FeedbackEntry[] {
|
|
61
62
|
const p = feedbackPath();
|
|
62
63
|
if (!fs.existsSync(p)) return [];
|
|
63
|
-
return
|
|
64
|
+
return readTextFile(p).split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export interface FeedbackSummary {
|
|
@@ -13,6 +13,7 @@ import * as fs from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import { loadScenarios } from './parse';
|
|
15
15
|
import { parseScenarios } from './capability-plan';
|
|
16
|
+
import { readTextFile } from './read-text';
|
|
16
17
|
|
|
17
18
|
const KNOWN_TARGETS = ['cart', 'category', 'brand', 'detail', 'checkout', 'wishlist', 'order', 'payment', 'login'];
|
|
18
19
|
|
|
@@ -33,7 +34,7 @@ function featurePath(base: string, kind: 'screens' | 'flows', name: string): str
|
|
|
33
34
|
}
|
|
34
35
|
function featureTags(fp: string): string[] {
|
|
35
36
|
if (!fs.existsSync(fp)) return [];
|
|
36
|
-
for (const line of
|
|
37
|
+
for (const line of readTextFile(fp).split('\n')) {
|
|
37
38
|
const t = line.trim();
|
|
38
39
|
if (t.startsWith('Feature:')) break;
|
|
39
40
|
if (t.startsWith('@')) return t.split(/\s+/).filter((x) => x.startsWith('@'));
|
package/src/harness/flow-plan.ts
CHANGED
|
@@ -12,6 +12,7 @@ import * as path from 'path';
|
|
|
12
12
|
import { parse as parseYaml } from 'yaml';
|
|
13
13
|
import { MANUAL_REASONS, inferReasonCode } from './capability-plan';
|
|
14
14
|
import { buildFlowCheck, Contract } from './flow-check';
|
|
15
|
+
import { readTextFile } from './read-text';
|
|
15
16
|
|
|
16
17
|
const REF_RE = /\[([A-Za-z][\w ]*?):([\w &.-]+?)\]/g;
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ interface FlowScenario { name: string; manual: boolean; reason: string; tags: st
|
|
|
19
20
|
|
|
20
21
|
function parseFlowScenarios(featurePath: string): FlowScenario[] {
|
|
21
22
|
if (!fs.existsSync(featurePath)) return [];
|
|
22
|
-
const lines =
|
|
23
|
+
const lines = readTextFile(featurePath).split('\n');
|
|
23
24
|
const out: FlowScenario[] = [];
|
|
24
25
|
const idx: number[] = [];
|
|
25
26
|
lines.forEach((l, i) => { if (/^\s*Scenario:/.test(l)) idx.push(i); });
|
|
@@ -50,7 +51,7 @@ function parseFlowScenarios(featurePath: string): FlowScenario[] {
|
|
|
50
51
|
function selectorKeys(selectorsPath: string): Set<string> {
|
|
51
52
|
if (!fs.existsSync(selectorsPath)) return new Set();
|
|
52
53
|
try {
|
|
53
|
-
const y = parseYaml(
|
|
54
|
+
const y = parseYaml(readTextFile(selectorsPath)) || {};
|
|
54
55
|
return new Set(Object.keys(y).map((k) => k.toLowerCase()));
|
|
55
56
|
} catch { return new Set(); }
|
|
56
57
|
}
|
package/src/harness/intent.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
|
+
import { readTextFile } from './read-text';
|
|
17
18
|
|
|
18
19
|
export type IntentFocus = 'functional' | 'e-commerce' | 'security' | 'smoke';
|
|
19
20
|
|
|
@@ -50,7 +51,7 @@ export function readIntent(projectRoot: string): IntentProfile {
|
|
|
50
51
|
const ctx = path.join(projectRoot, 'qa', 'context.md');
|
|
51
52
|
if (!fs.existsSync(ctx)) return DEFAULT_INTENT;
|
|
52
53
|
let text: string;
|
|
53
|
-
try { text =
|
|
54
|
+
try { text = readTextFile(ctx).toLowerCase(); } catch { return DEFAULT_INTENT; }
|
|
54
55
|
|
|
55
56
|
const grab = (key: string): string | undefined => {
|
|
56
57
|
const m = text.match(new RegExp(`(?:^|\\n)\\s*${key}\\s*:\\s*([a-z0-9-]+)`));
|