@anselmdk/feature-spec-md 0.5.0 → 0.6.0
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/README.md +59 -78
- package/SPEC_FORMAT.md +38 -78
- package/dist/cli.js +194 -86
- package/dist/cli.js.map +1 -1
- package/dist/featureSpecs.d.ts +1 -1
- package/dist/featureSpecs.d.ts.map +1 -1
- package/dist/featureSpecs.js +57 -10
- package/dist/featureSpecs.js.map +1 -1
- package/dist/githubActionDiffReport.d.ts +61 -0
- package/dist/githubActionDiffReport.d.ts.map +1 -1
- package/dist/githubActionDiffReport.js +343 -119
- package/dist/githubActionDiffReport.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/mockReports.d.ts +36 -0
- package/dist/mockReports.d.ts.map +1 -0
- package/dist/mockReports.js +250 -0
- package/dist/mockReports.js.map +1 -0
- package/dist/mocks/current/app/supportDesk.d.ts +12 -0
- package/dist/mocks/current/app/supportDesk.d.ts.map +1 -0
- package/dist/mocks/current/app/supportDesk.js +19 -0
- package/dist/mocks/current/app/supportDesk.js.map +1 -0
- package/dist/mocks/current/tests/supportDesk.spec.d.ts +3 -0
- package/dist/mocks/current/tests/supportDesk.spec.d.ts.map +1 -0
- package/dist/mocks/current/tests/supportDesk.spec.js +15 -0
- package/dist/mocks/current/tests/supportDesk.spec.js.map +1 -0
- package/dist/mocks/previous/app/supportDesk.d.ts +10 -0
- package/dist/mocks/previous/app/supportDesk.d.ts.map +1 -0
- package/dist/mocks/previous/app/supportDesk.js +9 -0
- package/dist/mocks/previous/app/supportDesk.js.map +1 -0
- package/dist/mocks/previous/tests/supportDesk.spec.d.ts +3 -0
- package/dist/mocks/previous/tests/supportDesk.spec.d.ts.map +1 -0
- package/dist/mocks/previous/tests/supportDesk.spec.js +15 -0
- package/dist/mocks/previous/tests/supportDesk.spec.js.map +1 -0
- package/dist/reportMetadata.d.ts +9 -0
- package/dist/reportMetadata.d.ts.map +1 -0
- package/dist/reportMetadata.js +28 -0
- package/dist/reportMetadata.js.map +1 -0
- package/dist/screenshots.d.ts +3 -1
- package/dist/screenshots.d.ts.map +1 -1
- package/dist/screenshots.js +23 -0
- package/dist/screenshots.js.map +1 -1
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/evidence-policy.md +63 -0
- package/docs/report-development.md +102 -0
- package/docs/spec-driven-flow.md +67 -8
- package/package.json +3 -1
- package/src/mocks/current/app/supportDesk.ts +27 -0
- package/src/mocks/current/screenshots/SUPPORT-INBOX-S001-line-27-current.svg +10 -0
- package/src/mocks/current/screenshots/SUPPORT-REPLY-S001-line-27-current.svg +10 -0
- package/src/mocks/current/screenshots/screenshots.json +18 -0
- package/src/mocks/current/specs/support-desk.model.md +29 -0
- package/src/mocks/current/specs/ticket-inbox.feature.md +27 -0
- package/src/mocks/current/specs/ticket-reply.feature.md +33 -0
- package/src/mocks/current/tests/supportDesk.spec.ts +17 -0
- package/src/mocks/previous/app/supportDesk.ts +17 -0
- package/src/mocks/previous/screenshots/SUPPORT-INBOX-S001-line-27-previous.svg +10 -0
- package/src/mocks/previous/screenshots/screenshots.json +11 -0
- package/src/mocks/previous/specs/support-desk.model.md +29 -0
- package/src/mocks/previous/specs/ticket-inbox.feature.md +26 -0
- package/src/mocks/previous/specs/ticket-reply.feature.md +26 -0
- package/src/mocks/previous/tests/supportDesk.spec.ts +17 -0
package/dist/screenshots.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { SpecScreenshot } from "./types.js";
|
|
1
|
+
import type { FeatureSpec, SpecScreenshot, ValidationIssue } from "./types.js";
|
|
2
2
|
/** Load screenshot evidence manifests and normalize entries for report rendering. */
|
|
3
3
|
export declare function collectSpecScreenshots(patterns: string[]): Promise<SpecScreenshot[]>;
|
|
4
|
+
/** Validate screenshot evidence manifests against scenario policies. */
|
|
5
|
+
export declare function validateScenarioScreenshots(specs: FeatureSpec[], screenshots: SpecScreenshot[]): ValidationIssue[];
|
|
4
6
|
export declare function screenshotKey(filePath: string, line: number): string;
|
|
5
7
|
export declare function normalizeFilePath(filePath: string): string;
|
|
6
8
|
//# sourceMappingURL=screenshots.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../src/screenshots.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../src/screenshots.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,qFAAqF;AACrF,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,6BAgB9D;AAED,wEAAwE;AACxE,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,WAAW,EAAE,EACpB,WAAW,EAAE,cAAc,EAAE,qBA0B9B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAE3D;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,UAEjD"}
|
package/dist/screenshots.js
CHANGED
|
@@ -22,6 +22,29 @@ export async function collectSpecScreenshots(patterns) {
|
|
|
22
22
|
}
|
|
23
23
|
return screenshots;
|
|
24
24
|
}
|
|
25
|
+
/** Validate screenshot evidence manifests against scenario policies. */
|
|
26
|
+
export function validateScenarioScreenshots(specs, screenshots) {
|
|
27
|
+
const screenshotKeys = new Set(screenshots.map((screenshot) => screenshotKey(screenshot.specPath, screenshot.line)));
|
|
28
|
+
const issues = [];
|
|
29
|
+
for (const spec of specs) {
|
|
30
|
+
for (const scenario of spec.scenarios) {
|
|
31
|
+
if (scenario.evidence.screenshots === "required") {
|
|
32
|
+
for (const step of scenario.steps) {
|
|
33
|
+
if (!screenshotKeys.has(screenshotKey(spec.filePath, step.line))) {
|
|
34
|
+
issues.push({
|
|
35
|
+
code: "missing-screenshot-evidence",
|
|
36
|
+
severity: "error",
|
|
37
|
+
filePath: spec.filePath,
|
|
38
|
+
line: step.line,
|
|
39
|
+
message: `Screenshot evidence is required for ${scenario.id} ${step.keyword} ${step.text}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return issues;
|
|
47
|
+
}
|
|
25
48
|
export function screenshotKey(filePath, line) {
|
|
26
49
|
return `${normalizeFilePath(filePath)}:${line}`;
|
|
27
50
|
}
|
package/dist/screenshots.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshots.js","sourceRoot":"","sources":["../src/screenshots.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,QAAkB;IAC7D,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,MAAM,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;QACnE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACnC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;gBACrD,CAAC,CAAC,MAAM,CAAC,WAAW;gBACpB,CAAC,CAAC,EAAE,CAAC;QAET,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,UAAU;gBAAE,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAY;IAC1D,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC;IAChD,IACE,OAAO,QAAQ,KAAK,QAAQ;QAC5B,OAAO,IAAI,KAAK,QAAQ;QACxB,OAAO,SAAS,KAAK,QAAQ,EAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC;QACrC,IAAI;QACJ,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAChE,QAAQ,EAAE,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC"}
|
|
1
|
+
{"version":3,"file":"screenshots.js","sourceRoot":"","sources":["../src/screenshots.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -14,8 +14,17 @@ export type ModelReferenceFrontmatter = SpecFrontmatter & {
|
|
|
14
14
|
model?: string;
|
|
15
15
|
models?: string[] | string;
|
|
16
16
|
};
|
|
17
|
+
export type ScenarioTestType = "unit" | "integration" | "playwright" | "manual" | "skip";
|
|
18
|
+
export type ScreenshotPolicy = "required" | "optional" | "skip";
|
|
19
|
+
export type ScenarioEvidencePolicy = {
|
|
20
|
+
test: ScenarioTestType;
|
|
21
|
+
screenshots: ScreenshotPolicy;
|
|
22
|
+
};
|
|
17
23
|
/** Frontmatter fields supported by `*.feature.md` files. */
|
|
18
|
-
export type FeatureFrontmatter = ModelReferenceFrontmatter
|
|
24
|
+
export type FeatureFrontmatter = ModelReferenceFrontmatter & {
|
|
25
|
+
test?: ScenarioTestType;
|
|
26
|
+
screenshots?: ScreenshotPolicy;
|
|
27
|
+
};
|
|
19
28
|
/** Frontmatter fields supported by `*.design.md` files. */
|
|
20
29
|
export type DesignFrontmatter = ModelReferenceFrontmatter;
|
|
21
30
|
/** Parsed contents of one `*.model.md` file. */
|
|
@@ -91,6 +100,7 @@ export type FeatureScenario = {
|
|
|
91
100
|
id: string;
|
|
92
101
|
title: string;
|
|
93
102
|
line: number;
|
|
103
|
+
evidence: ScenarioEvidencePolicy;
|
|
94
104
|
steps: FeatureStep[];
|
|
95
105
|
};
|
|
96
106
|
/** A single Given / When / Then style step inside a scenario. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,yBAAyB,GAAG,eAAe,GAAG;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,4DAA4D;AAC5D,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Scenario Test And Evidence Policy
|
|
2
|
+
|
|
3
|
+
Feature specs can declare how scenarios are expected to be tested and whether screenshot evidence is required.
|
|
4
|
+
|
|
5
|
+
Use feature frontmatter to set defaults for all scenarios in a feature:
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
---
|
|
9
|
+
id: KANBAN-CARD-AUTHORING
|
|
10
|
+
title: Card authoring
|
|
11
|
+
test: playwright
|
|
12
|
+
screenshots: required
|
|
13
|
+
---
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use scenario-level overrides directly below a scenario heading when one scenario needs different evidence:
|
|
17
|
+
|
|
18
|
+
```md
|
|
19
|
+
### KANBAN-CARD-AUTHORING-S002: Card title is normalized
|
|
20
|
+
Test: unit
|
|
21
|
+
Screenshots: skip
|
|
22
|
+
|
|
23
|
+
Given the raw card title contains leading whitespace
|
|
24
|
+
When the title is normalized
|
|
25
|
+
Then the stored title has no leading whitespace
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `test` values
|
|
29
|
+
|
|
30
|
+
- `unit`: behavior is expected to be covered by unit tests.
|
|
31
|
+
- `integration`: behavior is expected to be covered by integration tests.
|
|
32
|
+
- `playwright`: behavior is expected to be covered by Playwright or another UI/browser test.
|
|
33
|
+
- `manual`: behavior is intentionally verified manually.
|
|
34
|
+
- `skip`: no executable test is expected for this scenario.
|
|
35
|
+
|
|
36
|
+
## `screenshots` values
|
|
37
|
+
|
|
38
|
+
- `required`: every scenario step should have screenshot evidence in the report manifest.
|
|
39
|
+
- `optional`: screenshot evidence may be included when useful, but missing screenshots do not fail validation.
|
|
40
|
+
- `skip`: screenshots are not expected and should not be treated as missing evidence.
|
|
41
|
+
|
|
42
|
+
`screenshots: none` is accepted as an alias for `screenshots: skip`.
|
|
43
|
+
|
|
44
|
+
## Defaults
|
|
45
|
+
|
|
46
|
+
- If `test` is omitted, scenarios default to `unit`.
|
|
47
|
+
- If `screenshots` is omitted and `test` resolves to `playwright`, screenshots default to `required`.
|
|
48
|
+
- If `screenshots` is omitted and `test` resolves to `unit`, `integration`, `manual`, or `skip`, screenshots default to `skip`.
|
|
49
|
+
|
|
50
|
+
This lets UI scenarios fail CI when screenshot evidence is missing, while unit-only domain behavior does not need screenshots.
|
|
51
|
+
|
|
52
|
+
## CI enforcement
|
|
53
|
+
|
|
54
|
+
Use report evidence enforcement in CI when declared screenshot evidence should block a build:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx feature-spec-md report \
|
|
58
|
+
--screenshots "test-results/spec-report/screenshots-*.json" \
|
|
59
|
+
--enforce-evidence \
|
|
60
|
+
--out test-results/spec-report/index.html
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The gate only fails for scenarios whose resolved screenshot policy is `required`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Report development mock data
|
|
2
|
+
|
|
3
|
+
The report UI is easier to develop when it can be rendered without a real app build, Playwright run, FTP server, or multiple published builds.
|
|
4
|
+
|
|
5
|
+
The mock setup keeps the maintainable source material in `src/mocks/`:
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
src/mocks/
|
|
9
|
+
├── current/
|
|
10
|
+
│ ├── app/
|
|
11
|
+
│ ├── specs/
|
|
12
|
+
│ ├── tests/
|
|
13
|
+
│ └── screenshots/
|
|
14
|
+
└── previous/
|
|
15
|
+
├── app/
|
|
16
|
+
├── specs/
|
|
17
|
+
├── tests/
|
|
18
|
+
└── screenshots/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The feature reports are generated by the normal library pipeline. The mock command loads the Markdown specs, scans the test files for coverage references, loads the screenshot manifests, and then renders the report HTML.
|
|
22
|
+
|
|
23
|
+
The diff report is generated by the same implementation used by the PR diff workflow. `src/mockReports.ts` prepares previous/current published-report directories, then calls `renderLocalDiffReport` from `src/githubActionDiffReport.ts`. That means the mock report exercises the real diff extraction, file comparison, screenshot grouping, and diff renderer instead of maintaining a separate mock-only diff renderer.
|
|
24
|
+
|
|
25
|
+
Run the library-owned mock setup from this package:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run report:mock
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It writes a deterministic set of report fixtures to `test-results/mock-reports/`:
|
|
32
|
+
|
|
33
|
+
```txt
|
|
34
|
+
test-results/mock-reports/
|
|
35
|
+
├── feature-spec-report/index.html
|
|
36
|
+
├── previous-feature-spec-report/index.html
|
|
37
|
+
└── diff-report/index.html
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The fixtures intentionally include the states that are otherwise cumbersome to reproduce by hand:
|
|
41
|
+
|
|
42
|
+
- covered and missing model/rule/scenario coverage
|
|
43
|
+
- current and previous screenshot evidence
|
|
44
|
+
- report metadata for branch, build, commit, and pull request
|
|
45
|
+
- a PR diff report with changed spec text, added spec text, changed screenshots, and added screenshots
|
|
46
|
+
|
|
47
|
+
Use `--out` to write the mock reports somewhere else:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm run report:mock -- --out /tmp/feature-spec-md-reports
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## FTP publishing
|
|
54
|
+
|
|
55
|
+
The `.github/workflows/mock-reports.yml` workflow publishes the generated mock reports on every push and on manual workflow dispatch.
|
|
56
|
+
|
|
57
|
+
It uses the same publishing command that consuming projects use:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node dist/cli.js github-report --publish ftp --report-dir test-results/mock-reports --name mock-feature-spec-reports
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Before publishing, the workflow adds a root `test-results/mock-reports/index.html` that links to:
|
|
64
|
+
|
|
65
|
+
- `feature-spec-report/`
|
|
66
|
+
- `previous-feature-spec-report/`
|
|
67
|
+
- `diff-report/`
|
|
68
|
+
|
|
69
|
+
The FTP publisher then uploads the whole `test-results/mock-reports` directory to `build/<github.run_number>/` and updates the build index.
|
|
70
|
+
|
|
71
|
+
Configure these repository settings before enabling the workflow:
|
|
72
|
+
|
|
73
|
+
| Name | Source |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `FEATURE_SPEC_FTP_HOST` | Repository variable |
|
|
76
|
+
| `FEATURE_SPEC_FTP_USER` | Repository variable |
|
|
77
|
+
| `FEATURE_SPEC_FTP_PASSWORD` | Repository secret |
|
|
78
|
+
| `FEATURE_SPEC_REPORT_BASE_URL` | Repository variable |
|
|
79
|
+
| `FEATURE_SPEC_FTP_REMOTE_DIR` | Repository variable |
|
|
80
|
+
|
|
81
|
+
Optional settings:
|
|
82
|
+
|
|
83
|
+
| Name | Source |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `FEATURE_SPEC_FTP_PORT` | Repository variable |
|
|
86
|
+
| `FEATURE_SPEC_FTP_SECURE` | Repository variable |
|
|
87
|
+
|
|
88
|
+
Use a mock-specific base URL and remote directory value so these reports do not overwrite the demo or any consumer report. For example, use a public base URL ending in `/mocks/` and an FTP remote directory ending in `/mocks`.
|
|
89
|
+
|
|
90
|
+
The same data is available from the library for custom dev servers or visual tests:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import {
|
|
94
|
+
loadMockReportData,
|
|
95
|
+
renderLocalDiffReport,
|
|
96
|
+
renderMockDiffReport,
|
|
97
|
+
renderMockFeatureSpecReport,
|
|
98
|
+
writeMockReports,
|
|
99
|
+
} from "@anselmdk/feature-spec-md";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Keep new reusable report states in `src/mocks/` where possible, and only put report-generation glue in `src/mockReports.ts`. That keeps the fixtures readable as normal specs, tests, manifests, and app code instead of duplicating parsed TypeScript objects.
|
package/docs/spec-driven-flow.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
```txt
|
|
6
6
|
AI drafts specs
|
|
7
|
-
-> humans review intent
|
|
7
|
+
-> humans review intent and evidence policy
|
|
8
8
|
-> AI writes tests from stable IDs
|
|
9
|
-
-> tooling checks coverage
|
|
9
|
+
-> tooling checks coverage and declared evidence
|
|
10
10
|
-> implementation follows failing tests
|
|
11
11
|
-> reports show what is implemented
|
|
12
12
|
```
|
|
@@ -54,7 +54,9 @@ Useful instruction:
|
|
|
54
54
|
```txt
|
|
55
55
|
Create feature-spec-md documents for this change. Keep the documents small.
|
|
56
56
|
Use stable uppercase IDs. Put durable behavior in rules. Put testable examples
|
|
57
|
-
in scenarios.
|
|
57
|
+
in scenarios. For each feature or scenario, declare whether the expected test
|
|
58
|
+
is unit, integration, playwright, manual, or skip. Require screenshots only for
|
|
59
|
+
UI flows where visual evidence should be part of the review.
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
Run:
|
|
@@ -74,9 +76,34 @@ Check that:
|
|
|
74
76
|
- model terms are clear enough for test authors
|
|
75
77
|
- feature rules are durable product truths
|
|
76
78
|
- scenarios are concrete enough to become executable tests
|
|
79
|
+
- each scenario has the right test policy
|
|
80
|
+
- screenshots are required only for UI/browser behavior where visual evidence is valuable
|
|
77
81
|
- stack choices are specific enough to guide implementation
|
|
78
82
|
- design direction covers the visible behavior users will judge
|
|
79
83
|
|
|
84
|
+
Example feature-level policy for UI behavior:
|
|
85
|
+
|
|
86
|
+
```md
|
|
87
|
+
---
|
|
88
|
+
id: ACCOUNT-ACCESS
|
|
89
|
+
title: Account access
|
|
90
|
+
test: playwright
|
|
91
|
+
screenshots: required
|
|
92
|
+
---
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Example scenario-level override for unit-only behavior:
|
|
96
|
+
|
|
97
|
+
```md
|
|
98
|
+
### ACCOUNT-ACCESS-S002: Link expiry is calculated
|
|
99
|
+
Test: unit
|
|
100
|
+
Screenshots: skip
|
|
101
|
+
|
|
102
|
+
Given a sign-in link was created 31 minutes ago
|
|
103
|
+
When expiry is calculated
|
|
104
|
+
Then the link is expired
|
|
105
|
+
```
|
|
106
|
+
|
|
80
107
|
Then run:
|
|
81
108
|
|
|
82
109
|
```bash
|
|
@@ -87,14 +114,17 @@ This keeps the spec set valid while allowing missing tests.
|
|
|
87
114
|
|
|
88
115
|
## 3. Ask AI To Write Tests From The Specs
|
|
89
116
|
|
|
90
|
-
Give the AI the relevant spec files and tell it to write executable tests that preserve spec IDs in the test source.
|
|
117
|
+
Give the AI the relevant spec files and tell it to write executable tests that preserve spec IDs in the test source and match the declared test policy.
|
|
91
118
|
|
|
92
119
|
Useful instruction:
|
|
93
120
|
|
|
94
121
|
```txt
|
|
95
122
|
Write tests from these feature-spec-md specs. Each scenario test must include
|
|
96
123
|
the scenario ID in the test title. Add comments or annotations for covered
|
|
97
|
-
rule IDs and model item IDs.
|
|
124
|
+
rule IDs and model item IDs. Use Playwright and the screenshot evidence helper
|
|
125
|
+
for scenarios declared as test: playwright with screenshots: required. Use unit
|
|
126
|
+
or integration tests for scenarios declared that way. Do not invent IDs that are
|
|
127
|
+
not in the specs.
|
|
98
128
|
```
|
|
99
129
|
|
|
100
130
|
Example:
|
|
@@ -137,7 +167,7 @@ Run:
|
|
|
137
167
|
npx feature-spec-md report --out test-results/feature-spec-report/index.html
|
|
138
168
|
```
|
|
139
169
|
|
|
140
|
-
The report is useful as a PR artifact because it shows specs, coverage state, validation issues, and optional screenshot evidence.
|
|
170
|
+
The report is useful as a PR artifact because it shows specs, coverage state, validation issues, declared evidence policy, and optional screenshot evidence.
|
|
141
171
|
|
|
142
172
|
If your tests produce screenshot manifests, include them:
|
|
143
173
|
|
|
@@ -147,6 +177,17 @@ npx feature-spec-md report \
|
|
|
147
177
|
--out test-results/feature-spec-report/index.html
|
|
148
178
|
```
|
|
149
179
|
|
|
180
|
+
When missing declared screenshot evidence should block the build, enforce evidence policy:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx feature-spec-md report \
|
|
184
|
+
--screenshots "test-results/spec-report/screenshots-*.json" \
|
|
185
|
+
--enforce-evidence \
|
|
186
|
+
--out test-results/feature-spec-report/index.html
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The gate only fails for scenarios whose resolved screenshot policy is `required`.
|
|
190
|
+
|
|
150
191
|
Screenshot manifest shape:
|
|
151
192
|
|
|
152
193
|
```json
|
|
@@ -162,14 +203,32 @@ Screenshot manifest shape:
|
|
|
162
203
|
}
|
|
163
204
|
```
|
|
164
205
|
|
|
165
|
-
## 6.
|
|
206
|
+
## 6. Publish The Report From CI
|
|
207
|
+
|
|
208
|
+
A typical CI sequence is:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npm run build
|
|
212
|
+
npx feature-spec-md check --require-rule-coverage --require-model-coverage
|
|
213
|
+
npx feature-spec-md coverage --fail-on-missing
|
|
214
|
+
npm test
|
|
215
|
+
npx feature-spec-md report \
|
|
216
|
+
--screenshots "test-results/spec-report/screenshots-*.json" \
|
|
217
|
+
--enforce-evidence \
|
|
218
|
+
--out test-results/spec-report/index.html
|
|
219
|
+
npx feature-spec-md github-report --publish ftp --report-dir test-results/spec-report
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Use `--enforce-evidence` when the project wants missing required screenshots to fail the build. Leave it out when the report should show screenshot state without blocking.
|
|
223
|
+
|
|
224
|
+
## 7. Keep The Loop Honest
|
|
166
225
|
|
|
167
226
|
When behavior changes, update specs first, then regenerate or update tests from the changed specs.
|
|
168
227
|
|
|
169
228
|
The expected loop is:
|
|
170
229
|
|
|
171
230
|
```txt
|
|
172
|
-
spec change -> validation -> AI test update -> coverage -> implementation -> report
|
|
231
|
+
spec change -> validation -> AI test update -> coverage -> implementation -> evidence report
|
|
173
232
|
```
|
|
174
233
|
|
|
175
234
|
That keeps the AI-generated work anchored to a small, reviewable contract instead of a loose conversation history.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anselmdk/feature-spec-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Markdown specs for AI-assisted, testable spec driven development across model, feature, stack, and design documents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
+
"src/mocks",
|
|
29
30
|
"README.md",
|
|
30
31
|
"SPEC_FORMAT.md",
|
|
31
32
|
"docs",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"spec": "tsx src/cli.ts check",
|
|
44
45
|
"spec:init": "tsx src/cli.ts init",
|
|
45
46
|
"spec:report": "tsx src/cli.ts report",
|
|
47
|
+
"report:mock": "tsx src/mockReports.ts",
|
|
46
48
|
"format": "prettier --write .",
|
|
47
49
|
"check:example": "tsx src/cli.ts check --specs examples/**/*.feature.md --require-scenario-coverage=false",
|
|
48
50
|
"report:example": "tsx src/cli.ts report --specs examples/**/*.feature.md --tests tests/**/*.test.ts --out test-results/feature-spec-report/index.html",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Ticket = {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
status: "open" | "closed";
|
|
5
|
+
priority: "low" | "normal" | "high";
|
|
6
|
+
createdAt: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function visibleTickets(tickets: Ticket[]) {
|
|
10
|
+
return tickets
|
|
11
|
+
.filter((ticket) => ticket.status === "open")
|
|
12
|
+
.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function priorityBadge(ticket: Ticket) {
|
|
16
|
+
if (ticket.priority === "high") return "High priority";
|
|
17
|
+
if (ticket.priority === "low") return "Low priority";
|
|
18
|
+
return "Normal priority";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function draftReply(existingDraft: string, nextText: string) {
|
|
22
|
+
return nextText.trim() ? nextText : existingDraft;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function applySavedReply(template: string) {
|
|
26
|
+
return template.trim();
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-label="Inbox with priority badges">
|
|
2
|
+
<rect width="960" height="540" fill="#f6f8fa"/>
|
|
3
|
+
<rect x="72" y="64" width="816" height="412" rx="24" fill="white" stroke="#d0d7de"/>
|
|
4
|
+
<rect x="112" y="112" width="736" height="72" rx="14" fill="#ddf4ff" stroke="#54aeff"/>
|
|
5
|
+
<text x="144" y="158" font-family="system-ui, sans-serif" font-size="28" font-weight="700" fill="#1f2328">Inbox with priority badges</text>
|
|
6
|
+
<rect x="112" y="224" width="520" height="44" rx="10" fill="#f6f8fa"/>
|
|
7
|
+
<rect x="112" y="292" width="660" height="44" rx="10" fill="#f6f8fa"/>
|
|
8
|
+
<rect x="112" y="360" width="420" height="44" rx="10" fill="#f6f8fa"/>
|
|
9
|
+
<circle cx="812" cy="384" r="34" fill="#ddf4ff" stroke="#54aeff"/>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-label="Reply composer with suggested answer">
|
|
2
|
+
<rect width="960" height="540" fill="#f6f8fa"/>
|
|
3
|
+
<rect x="72" y="64" width="816" height="412" rx="24" fill="white" stroke="#d0d7de"/>
|
|
4
|
+
<rect x="112" y="112" width="736" height="72" rx="14" fill="#ddf4ff" stroke="#54aeff"/>
|
|
5
|
+
<text x="144" y="158" font-family="system-ui, sans-serif" font-size="28" font-weight="700" fill="#1f2328">Reply composer with suggested answer</text>
|
|
6
|
+
<rect x="112" y="224" width="680" height="44" rx="10" fill="#f6f8fa"/>
|
|
7
|
+
<rect x="112" y="292" width="620" height="44" rx="10" fill="#f6f8fa"/>
|
|
8
|
+
<rect x="112" y="360" width="300" height="44" rx="10" fill="#ddf4ff" stroke="#54aeff"/>
|
|
9
|
+
<circle cx="812" cy="384" r="34" fill="#ddf4ff" stroke="#54aeff"/>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"screenshots": [
|
|
3
|
+
{
|
|
4
|
+
"specPath": "src/mocks/current/specs/ticket-inbox.feature.md",
|
|
5
|
+
"line": 27,
|
|
6
|
+
"path": "screenshots/SUPPORT-INBOX-S001-line-27-current.svg",
|
|
7
|
+
"title": "Inbox with priority badges",
|
|
8
|
+
"testPath": "src/mocks/current/tests/supportDesk.spec.ts"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"specPath": "src/mocks/current/specs/ticket-reply.feature.md",
|
|
12
|
+
"line": 27,
|
|
13
|
+
"path": "screenshots/SUPPORT-REPLY-S001-line-27-current.svg",
|
|
14
|
+
"title": "Reply composer with suggested answer",
|
|
15
|
+
"testPath": "src/mocks/current/tests/supportDesk.spec.ts"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SUPPORT
|
|
3
|
+
title: Support desk
|
|
4
|
+
status: active
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Support desk
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Define the shared vocabulary for a small customer support workflow.
|
|
12
|
+
|
|
13
|
+
## Model
|
|
14
|
+
|
|
15
|
+
### SUPPORT-M001: Ticket
|
|
16
|
+
|
|
17
|
+
A customer request that needs a response from an agent.
|
|
18
|
+
|
|
19
|
+
### SUPPORT-M002: Agent
|
|
20
|
+
|
|
21
|
+
A team member who reviews tickets and sends replies.
|
|
22
|
+
|
|
23
|
+
### SUPPORT-M003: Priority
|
|
24
|
+
|
|
25
|
+
A visible urgency label used to sort the queue.
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- SUPPORT-M-R001: Tickets MUST keep a stable public reference.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SUPPORT-INBOX
|
|
3
|
+
title: Ticket inbox
|
|
4
|
+
status: active
|
|
5
|
+
model: SUPPORT
|
|
6
|
+
test: playwright
|
|
7
|
+
screenshots: optional
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ticket inbox
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
Let agents find the next ticket to work on without losing context.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- SUPPORT-INBOX-R001: The inbox MUST show open tickets first.
|
|
19
|
+
- SUPPORT-INBOX-R002: The inbox SHOULD highlight high priority tickets.
|
|
20
|
+
|
|
21
|
+
## Scenarios
|
|
22
|
+
|
|
23
|
+
### SUPPORT-INBOX-S001: Agent reviews the queue
|
|
24
|
+
|
|
25
|
+
Given an agent has open tickets
|
|
26
|
+
When they open the inbox
|
|
27
|
+
Then open tickets are listed by age with priority badges
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SUPPORT-REPLY
|
|
3
|
+
title: Ticket replies
|
|
4
|
+
status: active
|
|
5
|
+
model: SUPPORT
|
|
6
|
+
test: playwright
|
|
7
|
+
screenshots: optional
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ticket replies
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
Help agents answer a customer without leaving the ticket.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- SUPPORT-REPLY-R001: The reply composer MUST preserve unsent text.
|
|
19
|
+
- SUPPORT-REPLY-R002: Saved replies SHOULD be reusable across tickets.
|
|
20
|
+
|
|
21
|
+
## Scenarios
|
|
22
|
+
|
|
23
|
+
### SUPPORT-REPLY-S001: Agent drafts a reply
|
|
24
|
+
|
|
25
|
+
Given an agent has selected a ticket
|
|
26
|
+
When they write a reply
|
|
27
|
+
Then the draft remains visible
|
|
28
|
+
|
|
29
|
+
### SUPPORT-REPLY-S002: Agent sends a saved reply
|
|
30
|
+
|
|
31
|
+
Given an agent has selected a ticket
|
|
32
|
+
When they choose a saved reply
|
|
33
|
+
Then the composer is filled with reusable text
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { draftReply, priorityBadge, visibleTickets } from "../app/supportDesk.js";
|
|
2
|
+
|
|
3
|
+
const tickets = [
|
|
4
|
+
{ id: "T-2", title: "Newer", status: "open" as const, priority: "high" as const, createdAt: "2026-01-02T10:00:00Z" },
|
|
5
|
+
{ id: "T-1", title: "Older", status: "open" as const, priority: "normal" as const, createdAt: "2026-01-01T10:00:00Z" },
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function verifiesTicketInboxMock() {
|
|
9
|
+
// Covers SUPPORT-M001, SUPPORT-M002, SUPPORT-M003 and SUPPORT-M-R001.
|
|
10
|
+
// Covers SUPPORT-INBOX-R001, SUPPORT-INBOX-R002 and SUPPORT-INBOX-S001.
|
|
11
|
+
return visibleTickets(tickets).map((ticket) => priorityBadge(ticket));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function verifiesReplyDraftMock() {
|
|
15
|
+
// Covers SUPPORT-REPLY-R001 and SUPPORT-REPLY-S001.
|
|
16
|
+
return draftReply("Existing draft", "Updated draft");
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type Ticket = {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
status: "open" | "closed";
|
|
5
|
+
priority: "low" | "normal" | "high";
|
|
6
|
+
createdAt: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function visibleTickets(tickets: Ticket[]) {
|
|
10
|
+
return tickets
|
|
11
|
+
.filter((ticket) => ticket.status === "open")
|
|
12
|
+
.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function draftReply(existingDraft: string, nextText: string) {
|
|
16
|
+
return nextText.trim() ? nextText : existingDraft;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-label="Inbox before priority badges">
|
|
2
|
+
<rect width="960" height="540" fill="#f6f8fa"/>
|
|
3
|
+
<rect x="72" y="64" width="816" height="412" rx="24" fill="white" stroke="#d0d7de"/>
|
|
4
|
+
<rect x="112" y="112" width="736" height="72" rx="14" fill="#fff8c5" stroke="#d4a72c"/>
|
|
5
|
+
<text x="144" y="158" font-family="system-ui, sans-serif" font-size="28" font-weight="700" fill="#1f2328">Inbox before priority badges</text>
|
|
6
|
+
<rect x="112" y="224" width="520" height="44" rx="10" fill="#f6f8fa"/>
|
|
7
|
+
<rect x="112" y="292" width="660" height="44" rx="10" fill="#f6f8fa"/>
|
|
8
|
+
<rect x="112" y="360" width="420" height="44" rx="10" fill="#f6f8fa"/>
|
|
9
|
+
<circle cx="812" cy="384" r="34" fill="#fff8c5" stroke="#d4a72c"/>
|
|
10
|
+
</svg>
|