@anselmdk/feature-spec-md 0.8.2 → 0.9.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/SPEC_FORMAT.md +89 -11
- package/dist/cli.js +16 -8
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +97 -0
- package/dist/config.js.map +1 -0
- package/dist/featureSpecs.d.ts +2 -0
- package/dist/featureSpecs.d.ts.map +1 -1
- package/dist/featureSpecs.js +86 -1
- package/dist/featureSpecs.js.map +1 -1
- package/dist/githubActionDiffReport.d.ts +5 -0
- package/dist/githubActionDiffReport.d.ts.map +1 -1
- package/dist/githubActionDiffReport.js +154 -46
- package/dist/githubActionDiffReport.js.map +1 -1
- package/dist/githubActionFtp.d.ts.map +1 -1
- package/dist/githubActionFtp.js +14 -2
- package/dist/githubActionFtp.js.map +1 -1
- package/dist/githubActionOutput.d.ts.map +1 -1
- package/dist/githubActionOutput.js.map +1 -1
- package/dist/githubReportMetadata.js +14 -5
- package/dist/githubReportMetadata.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mockReports.d.ts.map +1 -1
- package/dist/mockReports.js +41 -8
- package/dist/mockReports.js.map +1 -1
- package/dist/mocks/current/tests/supportDesk.spec.d.ts.map +1 -1
- package/dist/mocks/current/tests/supportDesk.spec.js +15 -3
- package/dist/mocks/current/tests/supportDesk.spec.js.map +1 -1
- package/dist/mocks/previous/tests/supportDesk.spec.d.ts.map +1 -1
- package/dist/mocks/previous/tests/supportDesk.spec.js +14 -2
- package/dist/mocks/previous/tests/supportDesk.spec.js.map +1 -1
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +3 -1
- package/dist/playwright.js.map +1 -1
- package/dist/reportArtifacts.d.ts.map +1 -1
- package/dist/reportArtifacts.js.map +1 -1
- package/dist/reportHtml.d.ts.map +1 -1
- package/dist/reportHtml.js +75 -2
- package/dist/reportHtml.js.map +1 -1
- package/dist/reportTemplate.d.ts +4 -1
- package/dist/reportTemplate.d.ts.map +1 -1
- package/dist/reportTemplate.js +290 -53
- package/dist/reportTemplate.js.map +1 -1
- package/dist/reportTemplates/shared.d.ts.map +1 -1
- package/dist/reportTemplates/shared.js.map +1 -1
- package/dist/screenshots.d.ts.map +1 -1
- package/dist/screenshots.js +3 -1
- package/dist/screenshots.js.map +1 -1
- package/dist/specDocuments.d.ts +3 -1
- package/dist/specDocuments.d.ts.map +1 -1
- package/dist/specDocuments.js +12 -1
- package/dist/specDocuments.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/evidence-policy.md +1 -0
- package/docs/report-development.md +9 -9
- package/docs/spec-driven-flow.md +1 -0
- package/package.json +1 -1
- package/src/mocks/current/specs/support-desk.design.md +1 -0
- package/src/mocks/current/specs/support-desk.model.md +1 -0
- package/src/mocks/current/specs/support-platform.stack.md +1 -0
- package/src/mocks/current/specs/ticket-inbox.feature.md +1 -0
- package/src/mocks/current/specs/ticket-reply.feature.md +1 -0
- package/src/mocks/current/tests/supportDesk.spec.ts +19 -3
- package/src/mocks/previous/specs/support-desk.model.md +1 -0
- package/src/mocks/previous/specs/ticket-inbox.feature.md +1 -0
- package/src/mocks/previous/specs/ticket-reply.feature.md +1 -0
- package/src/mocks/previous/tests/supportDesk.spec.ts +14 -2
package/README.md
CHANGED
|
@@ -253,6 +253,53 @@ npx feature-spec-md report \
|
|
|
253
253
|
|
|
254
254
|
This gate only fails for scenarios whose resolved screenshot policy is `required`. In GitHub Actions, the report also includes source links and report metadata derived from the repository, ref, SHA, run, build number, and pull request context when available.
|
|
255
255
|
|
|
256
|
+
Reports include a persistent light/dark theme toggle. Screenshot evidence is
|
|
257
|
+
clickable and opens at its native size in a scrollable full-screen viewer. PR
|
|
258
|
+
diff reports keep before and after images side by side, with red and green
|
|
259
|
+
borders respectively.
|
|
260
|
+
|
|
261
|
+
Projects can group model, stack, design, and feature documents into collapsible
|
|
262
|
+
report layers. Add a `layer` to each document's frontmatter and define the
|
|
263
|
+
ordered layer catalogue in `package.json`:
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"featureSpecMd": {
|
|
268
|
+
"report": {
|
|
269
|
+
"layers": [
|
|
270
|
+
{
|
|
271
|
+
"id": "context",
|
|
272
|
+
"title": "Context",
|
|
273
|
+
"description": "Architecture, design, and domain vocabulary"
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"id": "capability",
|
|
277
|
+
"title": "Capabilities",
|
|
278
|
+
"description": "Authoritative business behavior"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"id": "surface",
|
|
282
|
+
"title": "Delivery surfaces",
|
|
283
|
+
"description": "Applications, hosting, and documentation"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"id": "journey",
|
|
287
|
+
"title": "Journeys",
|
|
288
|
+
"description": "Cross-system release paths"
|
|
289
|
+
}
|
|
290
|
+
],
|
|
291
|
+
"layersDefaultOpen": false,
|
|
292
|
+
"documentsDefaultOpen": false
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
When layers are configured, unassigned documents remain visible in an `Other`
|
|
299
|
+
layer and produce a warning; unknown layer ids are errors. Deep links open all
|
|
300
|
+
containing layers and documents automatically. Projects without layer
|
|
301
|
+
configuration retain the flat report layout.
|
|
302
|
+
|
|
256
303
|
### `github-report`
|
|
257
304
|
|
|
258
305
|
Writes a GitHub Actions job summary and prepares the generated report for either artifact upload or FTP publishing.
|
|
@@ -283,7 +330,7 @@ npx feature-spec-md github-diff-report \
|
|
|
283
330
|
```
|
|
284
331
|
|
|
285
332
|
The diff report lists changed report assets, extracts changed spec sections,
|
|
286
|
-
groups screenshot changes by spec/scenario, writes a GitHub Actions summary,
|
|
333
|
+
groups configured projects by layer and screenshot changes by spec/scenario, writes a GitHub Actions summary,
|
|
287
334
|
and exposes a `diff-comment-body` output. The reusable PR workflow maintains
|
|
288
335
|
one compact PR comment linking to the latest full spec report and diff report.
|
|
289
336
|
Earlier build links remain available in a collapsed history below the current
|
package/SPEC_FORMAT.md
CHANGED
|
@@ -44,6 +44,7 @@ Optional fields:
|
|
|
44
44
|
|
|
45
45
|
- `status`: `draft`, `active`, or `deprecated`
|
|
46
46
|
- `owner`
|
|
47
|
+
- `layer`: a project-defined report layer id
|
|
47
48
|
- `model`: a single referenced model id, for feature and design files
|
|
48
49
|
- `models`: comma-separated referenced model ids, for feature and design files
|
|
49
50
|
- `test`: default scenario test type for feature files
|
|
@@ -51,6 +52,19 @@ Optional fields:
|
|
|
51
52
|
|
|
52
53
|
IDs use uppercase words separated by hyphens. IDs are the contract between specs, tests, reports, and implementation work.
|
|
53
54
|
|
|
55
|
+
### Report Layers
|
|
56
|
+
|
|
57
|
+
Any model, feature, stack, or design document MAY declare a project-defined
|
|
58
|
+
`layer` in frontmatter. Layer definitions belong in the consuming project's
|
|
59
|
+
`package.json` under `featureSpecMd.report.layers`; the configuration controls
|
|
60
|
+
their titles, descriptions, and order. `layersDefaultOpen` and
|
|
61
|
+
`documentsDefaultOpen` control the initial nested report state.
|
|
62
|
+
|
|
63
|
+
Layer ids MUST use lowercase words separated by hyphens. When a layer catalogue
|
|
64
|
+
is configured, an unknown layer is an error. A document without a layer remains
|
|
65
|
+
visible under `Other` and produces a warning so report content is never silently
|
|
66
|
+
discarded.
|
|
67
|
+
|
|
54
68
|
## Purpose
|
|
55
69
|
|
|
56
70
|
Every model, feature, stack, and design file MUST include a short `## Purpose` section.
|
|
@@ -75,6 +89,7 @@ Optional sections:
|
|
|
75
89
|
|
|
76
90
|
```md
|
|
77
91
|
## Rules
|
|
92
|
+
|
|
78
93
|
## Model Diagram
|
|
79
94
|
```
|
|
80
95
|
|
|
@@ -165,6 +180,7 @@ Scenario-level overrides go directly below a scenario heading. The Given / When
|
|
|
165
180
|
|
|
166
181
|
````md
|
|
167
182
|
### KANBAN-CARD-AUTHORING-S002: Card title is normalized
|
|
183
|
+
|
|
168
184
|
Test: unit
|
|
169
185
|
Screenshots: skip
|
|
170
186
|
|
|
@@ -187,6 +203,66 @@ Defaults:
|
|
|
187
203
|
|
|
188
204
|
See [Scenario Test And Evidence Policy](docs/evidence-policy.md) for details and CI examples.
|
|
189
205
|
|
|
206
|
+
### End-to-end journey metadata
|
|
207
|
+
|
|
208
|
+
Scenarios that cross multiple product or system boundaries can be identified as
|
|
209
|
+
end-to-end journeys. When most or all scenarios in a feature share the same
|
|
210
|
+
journey metadata, declare the defaults in frontmatter:
|
|
211
|
+
|
|
212
|
+
```md
|
|
213
|
+
---
|
|
214
|
+
id: CHECKOUT
|
|
215
|
+
title: Checkout
|
|
216
|
+
test: playwright
|
|
217
|
+
screenshots: required
|
|
218
|
+
journey: end-to-end
|
|
219
|
+
path: happy
|
|
220
|
+
critical: true
|
|
221
|
+
---
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Scenarios inherit those values and only need to declare their systems:
|
|
225
|
+
|
|
226
|
+
````md
|
|
227
|
+
### CHECKOUT-S001: Attendee buys access and books
|
|
228
|
+
|
|
229
|
+
Systems: consumer UI, API, payment provider, email
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
Given a new attendee is viewing a public class
|
|
233
|
+
When they purchase access and complete the booking
|
|
234
|
+
Then the booking appears in their account
|
|
235
|
+
```
|
|
236
|
+
````
|
|
237
|
+
|
|
238
|
+
Scenario-level metadata overrides feature defaults, so an exceptional failure
|
|
239
|
+
path can remain explicit:
|
|
240
|
+
|
|
241
|
+
````md
|
|
242
|
+
### CHECKOUT-S002: Payment is declined
|
|
243
|
+
|
|
244
|
+
Path: failure
|
|
245
|
+
Critical: false
|
|
246
|
+
Systems: consumer UI, API, payment provider, email
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
Given an attendee is checking out
|
|
250
|
+
When the payment provider declines the payment
|
|
251
|
+
Then access is not granted
|
|
252
|
+
```
|
|
253
|
+
````
|
|
254
|
+
|
|
255
|
+
`Path` is either `happy` or `failure`. `Critical` marks release-gating
|
|
256
|
+
journeys, and `Systems` is a comma-separated list shown in the report. Journey
|
|
257
|
+
scenarios appear in a dedicated report overview and otherwise retain normal
|
|
258
|
+
scenario coverage and evidence requirements. CI can enforce critical journeys
|
|
259
|
+
even when general scenario coverage is disabled by passing
|
|
260
|
+
`--require-critical-journey-coverage`.
|
|
261
|
+
|
|
262
|
+
For a mixed feature, `Journey: end-to-end`, `Path`, and `Critical` may still be
|
|
263
|
+
declared per scenario. Feature defaults are optional; `path` and `critical`
|
|
264
|
+
frontmatter require `journey: end-to-end`.
|
|
265
|
+
|
|
190
266
|
## Stack Files
|
|
191
267
|
|
|
192
268
|
Stack files use the `.stack.md` suffix and define technical platform choices and the reasoning behind them.
|
|
@@ -258,10 +334,10 @@ Use `## API Contract` for API-first features and services. This section SHOULD d
|
|
|
258
334
|
```md
|
|
259
335
|
## API Contract
|
|
260
336
|
|
|
261
|
-
| Method | Path
|
|
262
|
-
|
|
|
263
|
-
| POST
|
|
264
|
-
| GET
|
|
337
|
+
| Method | Path | Purpose | Auth |
|
|
338
|
+
| ------ | ----------- | ------------------- | -------------- |
|
|
339
|
+
| POST | /api/events | Create an event | administrator |
|
|
340
|
+
| GET | /api/events | List visible events | account member |
|
|
265
341
|
```
|
|
266
342
|
|
|
267
343
|
API contract details can be covered by integration tests that reference the relevant scenario and rule IDs.
|
|
@@ -273,10 +349,10 @@ Use `## Permissions` for role, group, and tenant capability matrices.
|
|
|
273
349
|
```md
|
|
274
350
|
## Permissions
|
|
275
351
|
|
|
276
|
-
| Capability
|
|
277
|
-
|
|
|
278
|
-
| Manage payment connection | yes
|
|
279
|
-
| Move own class instance
|
|
352
|
+
| Capability | Owner | Administrator | Teacher | Attendee |
|
|
353
|
+
| ------------------------- | ----- | ------------- | ------- | -------- |
|
|
354
|
+
| Manage payment connection | yes | no | no | no |
|
|
355
|
+
| Move own class instance | yes | yes | yes | no |
|
|
280
356
|
```
|
|
281
357
|
|
|
282
358
|
Permissions that must be enforced by the implementation SHOULD also be written as rules so coverage can track them.
|
|
@@ -285,7 +361,7 @@ Permissions that must be enforced by the implementation SHOULD also be written a
|
|
|
285
361
|
|
|
286
362
|
Use `## Lifecycle` for state machines and transition rules for entities such as bookings, payments, subscriptions, passes, and recurring event instances.
|
|
287
363
|
|
|
288
|
-
|
|
364
|
+
````md
|
|
289
365
|
## Lifecycle
|
|
290
366
|
|
|
291
367
|
Payment states:
|
|
@@ -295,9 +371,11 @@ pending -> paid
|
|
|
295
371
|
pending -> failed
|
|
296
372
|
paid -> refunded
|
|
297
373
|
```
|
|
374
|
+
````
|
|
298
375
|
|
|
299
376
|
Invalid transitions MUST be rejected by the API.
|
|
300
|
-
|
|
377
|
+
|
|
378
|
+
````
|
|
301
379
|
|
|
302
380
|
Lifecycle behavior that must be executable SHOULD be represented by rules and scenarios in the same feature file.
|
|
303
381
|
|
|
@@ -311,7 +389,7 @@ Use `## Test Environment` for mocks, seed data, fixed-time behavior, provider ad
|
|
|
311
389
|
- Email delivery is mocked and exposes the latest magic link to tests.
|
|
312
390
|
- MobilePay runs through a deterministic mock in CI.
|
|
313
391
|
- Time is frozen during recurring-event generation tests.
|
|
314
|
-
|
|
392
|
+
````
|
|
315
393
|
|
|
316
394
|
This section is especially useful for SaaS apps with external dependencies such as payment providers, email, calendars, or third-party APIs.
|
|
317
395
|
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import { collectSpecScreenshots, insertReportMetadata, renderHtmlReport, validateScenarioScreenshots, writeTextFile, } from "./index.js";
|
|
8
|
+
import { collectSpecScreenshots, insertReportMetadata, loadProjectConfiguration, renderHtmlReport, validateScenarioScreenshots, writeTextFile, } from "./index.js";
|
|
9
9
|
import { githubReportMetadata } from "./githubReportMetadata.js";
|
|
10
10
|
import { publishGithubActionDiffReport } from "./githubActionDiffReport.js";
|
|
11
11
|
import { publishGithubActionReport } from "./githubActionReport.js";
|
|
@@ -22,12 +22,13 @@ main().catch((error) => {
|
|
|
22
22
|
async function main() {
|
|
23
23
|
const [command = "help", ...args] = process.argv.slice(2);
|
|
24
24
|
const options = parseArgs(args);
|
|
25
|
+
const configuration = await loadProjectConfiguration();
|
|
25
26
|
if (command === "check")
|
|
26
|
-
return runCheck(options);
|
|
27
|
+
return runCheck(options, configuration);
|
|
27
28
|
if (command === "report")
|
|
28
|
-
return runReport(options);
|
|
29
|
+
return runReport(options, configuration);
|
|
29
30
|
if (command === "coverage")
|
|
30
|
-
return runCoverage(options);
|
|
31
|
+
return runCoverage(options, configuration);
|
|
31
32
|
if (command === "github-report")
|
|
32
33
|
return publishGithubActionReport(options);
|
|
33
34
|
if (command === "github-diff-report")
|
|
@@ -37,13 +38,15 @@ async function main() {
|
|
|
37
38
|
printHelp();
|
|
38
39
|
process.exit(command === "help" || command === "--help" || command === "-h" ? 0 : 1);
|
|
39
40
|
}
|
|
40
|
-
async function runCheck(options) {
|
|
41
|
+
async function runCheck(options, configuration) {
|
|
41
42
|
const result = await checkSpecDocuments({
|
|
42
43
|
specs: optionList(options.specs, defaultSpecPattern),
|
|
43
44
|
tests: options.tests === "" ? [] : optionList(options.tests, defaultTestPattern),
|
|
44
45
|
requireModelCoverage: booleanOption(options, "require-model-coverage", true),
|
|
45
46
|
requireRuleCoverage: booleanOption(options, "require-rule-coverage", true),
|
|
46
47
|
requireScenarioCoverage: booleanOption(options, "require-scenario-coverage", true),
|
|
48
|
+
requireCriticalJourneyCoverage: booleanOption(options, "require-critical-journey-coverage", false),
|
|
49
|
+
layers: configuration.report?.layers,
|
|
47
50
|
});
|
|
48
51
|
printIssues([...result.validationIssues, ...result.coverageIssues]);
|
|
49
52
|
if (!result.ok)
|
|
@@ -53,13 +56,14 @@ async function runCheck(options) {
|
|
|
53
56
|
const scenarioCount = result.features.reduce((sum, spec) => sum + spec.scenarios.length, 0);
|
|
54
57
|
console.log(`Spec check passed: ${result.models.length} model(s), ${result.features.length} feature(s), ${result.stacks.length} stack(s), ${result.designs.length} design(s), ${modelItemCount} model item(s), ${ruleCount} rule(s), ${scenarioCount} scenario(s).`);
|
|
55
58
|
}
|
|
56
|
-
async function runCoverage(options) {
|
|
59
|
+
async function runCoverage(options, configuration) {
|
|
57
60
|
const result = await checkSpecDocuments({
|
|
58
61
|
specs: optionList(options.specs, defaultSpecPattern),
|
|
59
62
|
tests: options.tests === "" ? [] : optionList(options.tests, defaultTestPattern),
|
|
60
63
|
requireModelCoverage: false,
|
|
61
64
|
requireRuleCoverage: false,
|
|
62
65
|
requireScenarioCoverage: false,
|
|
66
|
+
layers: configuration.report?.layers,
|
|
63
67
|
});
|
|
64
68
|
printIssues(result.coverageIssues);
|
|
65
69
|
if (!result.coverage) {
|
|
@@ -70,16 +74,18 @@ async function runCoverage(options) {
|
|
|
70
74
|
}
|
|
71
75
|
const report = buildSpecImplementationReport(result.features, result.coverage, result.models);
|
|
72
76
|
console.log(formatSpecImplementationReport(report));
|
|
73
|
-
if (booleanOption(options, "fail-on-missing", true) &&
|
|
77
|
+
if (booleanOption(options, "fail-on-missing", true) &&
|
|
78
|
+
hasMissingCoverage(report)) {
|
|
74
79
|
process.exit(1);
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
|
-
async function runReport(options) {
|
|
82
|
+
async function runReport(options, configuration) {
|
|
78
83
|
const result = await checkSpecDocuments({
|
|
79
84
|
specs: optionList(options.specs, defaultSpecPattern),
|
|
80
85
|
tests: options.tests === "" ? [] : optionList(options.tests, defaultTestPattern),
|
|
81
86
|
requireRuleCoverage: booleanOption(options, "require-rule-coverage", false),
|
|
82
87
|
requireScenarioCoverage: false,
|
|
88
|
+
layers: configuration.report?.layers,
|
|
83
89
|
});
|
|
84
90
|
const out = options.out ?? defaultReportPath;
|
|
85
91
|
const screenshots = options.screenshots === ""
|
|
@@ -104,6 +110,7 @@ async function runReport(options) {
|
|
|
104
110
|
coverage: result.coverage,
|
|
105
111
|
screenshots,
|
|
106
112
|
validationIssues,
|
|
113
|
+
...configuration.report,
|
|
107
114
|
...githubSourceLinkOptions(),
|
|
108
115
|
});
|
|
109
116
|
await writeTextFile(out, insertReportMetadata(report, await githubReportMetadata({ includeGitFallbacks: true })));
|
|
@@ -226,6 +233,7 @@ Options:
|
|
|
226
233
|
--require-model-coverage Defaults to true for check. Use --require-model-coverage=false to disable.
|
|
227
234
|
--require-rule-coverage Defaults to true for check. Use --require-rule-coverage=false to disable.
|
|
228
235
|
--require-scenario-coverage Defaults to true for check. Use --require-scenario-coverage=false to disable.
|
|
236
|
+
--require-critical-journey-coverage Require every critical end-to-end journey to have a test reference.
|
|
229
237
|
--enforce-evidence Defaults to true for report. Use --enforce-evidence=false to disable.
|
|
230
238
|
--require-declared-evidence Alias for --enforce-evidence.
|
|
231
239
|
--require-screenshots Deprecated alias for --enforce-evidence.
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,GAEd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,kBAAkB,GACtB,oFAAoF,CAAC;AACvF,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAC3C,MAAM,wBAAwB,GAAG,6CAA6C,CAAC;AAC/E,MAAM,iBAAiB,GAAG,qCAAqC,CAAC;AAEhE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,GAEd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,kBAAkB,GACtB,oFAAoF,CAAC;AACvF,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAC3C,MAAM,wBAAwB,GAAG,6CAA6C,CAAC;AAC/E,MAAM,iBAAiB,GAAG,qCAAqC,CAAC;AAEhE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,aAAa,GAAG,MAAM,wBAAwB,EAAE,CAAC;IAEvD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACjE,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACnE,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACvE,IAAI,OAAO,KAAK,eAAe;QAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC3E,IAAI,OAAO,KAAK,oBAAoB;QAClC,OAAO,6BAA6B,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhD,SAAS,EAAE,CAAC;IACZ,OAAO,CAAC,IAAI,CACV,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACvE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,OAAmB,EACnB,aAAmE;IAEnE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;QACtC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QACpD,KAAK,EACH,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QAC3E,oBAAoB,EAAE,aAAa,CACjC,OAAO,EACP,wBAAwB,EACxB,IAAI,CACL;QACD,mBAAmB,EAAE,aAAa,CAAC,OAAO,EAAE,uBAAuB,EAAE,IAAI,CAAC;QAC1E,uBAAuB,EAAE,aAAa,CACpC,OAAO,EACP,2BAA2B,EAC3B,IAAI,CACL;QACD,8BAA8B,EAAE,aAAa,CAC3C,OAAO,EACP,mCAAmC,EACnC,KAAK,CACN;QACD,MAAM,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM;KACrC,CAAC,CAAC;IAEH,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,gBAAgB,EAAE,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CACzC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAC3C,CAAC,CACF,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CACvC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EACtC,CAAC,CACF,CAAC;IACF,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAC1C,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAC1C,CAAC,CACF,CAAC;IACF,OAAO,CAAC,GAAG,CACT,sBAAsB,MAAM,CAAC,MAAM,CAAC,MAAM,cAAc,MAAM,CAAC,QAAQ,CAAC,MAAM,gBAAgB,MAAM,CAAC,MAAM,CAAC,MAAM,cAAc,MAAM,CAAC,OAAO,CAAC,MAAM,eAAe,cAAc,mBAAmB,SAAS,aAAa,aAAa,eAAe,CACxP,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,OAAmB,EACnB,aAAmE;IAEnE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;QACtC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QACpD,KAAK,EACH,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QAC3E,oBAAoB,EAAE,KAAK;QAC3B,mBAAmB,EAAE,KAAK;QAC1B,uBAAuB,EAAE,KAAK;QAC9B,MAAM,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM;KACrC,CAAC,CAAC;IAEH,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAEnC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CACT,0EAA0E,kBAAkB,WAAW,CACxG,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,6BAA6B,CAC1C,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,MAAM,CACd,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC,CAAC;IAEpD,IACE,aAAa,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,CAAC;QAC/C,kBAAkB,CAAC,MAAM,CAAC,EAC1B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,OAAmB,EACnB,aAAmE;IAEnE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;QACtC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QACpD,KAAK,EACH,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC;QAC3E,mBAAmB,EAAE,aAAa,CAAC,OAAO,EAAE,uBAAuB,EAAE,KAAK,CAAC;QAC3E,uBAAuB,EAAE,KAAK;QAC9B,MAAM,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM;KACrC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,iBAAiB,CAAC;IAC7C,MAAM,WAAW,GACf,OAAO,CAAC,WAAW,KAAK,EAAE;QACxB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,sBAAsB,CAC1B,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAC1D,CAAC;IACR,MAAM,eAAe,GACnB,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,CAAC;QAChD,OAAO,CAAC,2BAA2B,CAAC,KAAK,MAAM;QAC/C,OAAO,CAAC,qBAAqB,CAAC,KAAK,MAAM,CAAC;IAC5C,MAAM,gBAAgB,GAAG,eAAe;QACtC,CAAC,CAAC,2BAA2B,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC;QAC3D,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,gBAAgB,GAAG;QACvB,GAAG,MAAM,CAAC,gBAAgB;QAC1B,GAAG,MAAM,CAAC,cAAc;QACxB,GAAG,gBAAgB;KACpB,CAAC;IAEF,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,QAAQ,EAAE;QAC/C,KAAK,EAAE,MAAM,WAAW,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,WAAW;QACX,gBAAgB;QAChB,GAAG,aAAa,CAAC,MAAM;QACvB,GAAG,uBAAuB,EAAE;KAC7B,CAAC,CAAC;IAEH,MAAM,aAAa,CACjB,GAAG,EACH,oBAAoB,CAClB,MAAM,EACN,MAAM,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAC1D,CACF,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IAErD,WAAW,CAAC,gBAAgB,CAAC,CAAC;IAC9B,IAAI,gBAAgB,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,OAAmB;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC;AAID,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACpC,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACtD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YACpB,KAAK,IAAI,CAAC,CAAC;YACX,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACxB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,KAAyB,EAAE,QAAgB;IAC7D,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC;SACvB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,aAAa,CACpB,OAAmB,EACnB,GAAW,EACX,YAAqB;IAErB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC;IAC7C,OAAO,KAAK,KAAK,OAAO,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW,CAAC,MAAyB;IAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ;YAC7B,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YAC1D,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,CAAC,KAAK,CACX,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,CACnG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAwD;IAExD,OAAO,CACL,MAAM,CAAC,gBAAgB,GAAG,CAAC;QAC3B,MAAM,CAAC,YAAY,GAAG,CAAC;QACvB,MAAM,CAAC,iBAAiB,GAAG,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW;IACxB,MAAM,WAAW,GAAG,MAAM,eAAe,EAAE,CAAC;IAC5C,OAAO,WAAW;QAChB,CAAC,CAAC,2BAA2B,WAAW,EAAE;QAC1C,CAAC,CAAC,qBAAqB,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,OAAO,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE;YAC1D,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE;YACpB,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACjD,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,aAAa,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,oBAAoB,IAAI,UAAU,EAAE,CAAC;IAC/F,OAAO;QACL,aAAa;QACb,SAAS;QACT,aAAa,EAAE,aAAa;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAC1D,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACnC,OAAO,GAAG,EAAE,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;oCAIsB,kBAAkB,gBAAgB,kBAAkB;uCACjD,kBAAkB,gBAAgB,kBAAkB;qCACtD,kBAAkB,gBAAgB,kBAAkB,sBAAsB,wBAAwB,wCAAwC,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B/L,CAAC,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,kBAAkB,CAAC;IAChD,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,kBAAkB,CAAC;IAChD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,mBAAmB,CAAC;IAClD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO;;;;;;;;;;;;;;;;;;;;;CAqBV,CAAC;IACA,CAAC;IACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO;;;;;;;;;;;;;;;;;;CAkBV,CAAC;IACA,CAAC;IACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;;;;;;;;;;;;;;;;CAgBV,CAAC;IACA,CAAC;IACD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwBR,CAAC;AACF,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FeatureSpecMdConfiguration, ReportLayer, SpecDocument, ValidationIssue } from "./types.js";
|
|
2
|
+
export declare function loadProjectConfiguration(cwd?: string): Promise<FeatureSpecMdConfiguration>;
|
|
3
|
+
export declare function normalizeProjectConfiguration(value: unknown): FeatureSpecMdConfiguration;
|
|
4
|
+
export declare function validateDocumentLayers(documents: SpecDocument[], layers?: ReportLayer[]): ValidationIssue[];
|
|
5
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,0BAA0B,EAC1B,WAAW,EACX,YAAY,EACZ,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,wBAAsB,wBAAwB,CAC5C,GAAG,SAAgB,GAClB,OAAO,CAAC,0BAA0B,CAAC,CAMrC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,0BAA0B,CA0B5B;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,YAAY,EAAE,EACzB,MAAM,GAAE,WAAW,EAAO,GACzB,eAAe,EAAE,CAyCnB"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function loadProjectConfiguration(cwd = process.cwd()) {
|
|
4
|
+
const packagePath = path.join(cwd, "package.json");
|
|
5
|
+
const packageJson = JSON.parse(await readFile(packagePath, "utf8"));
|
|
6
|
+
return normalizeProjectConfiguration(packageJson.featureSpecMd);
|
|
7
|
+
}
|
|
8
|
+
export function normalizeProjectConfiguration(value) {
|
|
9
|
+
if (value === undefined)
|
|
10
|
+
return {};
|
|
11
|
+
if (!isRecord(value))
|
|
12
|
+
throw new Error("package.json featureSpecMd must be an object.");
|
|
13
|
+
const report = value.report;
|
|
14
|
+
if (report === undefined)
|
|
15
|
+
return {};
|
|
16
|
+
if (!isRecord(report))
|
|
17
|
+
throw new Error("package.json featureSpecMd.report must be an object.");
|
|
18
|
+
const layers = report.layers;
|
|
19
|
+
if (layers !== undefined && !Array.isArray(layers))
|
|
20
|
+
throw new Error("featureSpecMd.report.layers must be an array.");
|
|
21
|
+
return {
|
|
22
|
+
report: {
|
|
23
|
+
layers: layers?.map(normalizeLayer),
|
|
24
|
+
layersDefaultOpen: optionalBoolean(report.layersDefaultOpen, "featureSpecMd.report.layersDefaultOpen"),
|
|
25
|
+
documentsDefaultOpen: optionalBoolean(report.documentsDefaultOpen, "featureSpecMd.report.documentsDefaultOpen"),
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function validateDocumentLayers(documents, layers = []) {
|
|
30
|
+
if (!layers.length)
|
|
31
|
+
return [];
|
|
32
|
+
const issues = [];
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
for (const layer of layers) {
|
|
35
|
+
if (!/^[a-z][a-z0-9-]*$/.test(layer.id)) {
|
|
36
|
+
issues.push({
|
|
37
|
+
code: "invalid-report-layer-id",
|
|
38
|
+
severity: "error",
|
|
39
|
+
message: `Report layer id "${layer.id}" must use lowercase words separated by hyphens.`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (seen.has(layer.id)) {
|
|
43
|
+
issues.push({
|
|
44
|
+
code: "duplicate-report-layer-id",
|
|
45
|
+
severity: "error",
|
|
46
|
+
message: `Report layer id "${layer.id}" is configured more than once.`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
seen.add(layer.id);
|
|
50
|
+
}
|
|
51
|
+
for (const document of documents) {
|
|
52
|
+
const layer = document.frontmatter.layer;
|
|
53
|
+
if (!layer) {
|
|
54
|
+
issues.push({
|
|
55
|
+
code: "missing-report-layer",
|
|
56
|
+
severity: "warning",
|
|
57
|
+
message: `Spec "${document.frontmatter.id}" has no report layer and will appear under Other.`,
|
|
58
|
+
filePath: document.filePath,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (!seen.has(layer)) {
|
|
62
|
+
issues.push({
|
|
63
|
+
code: "unknown-report-layer",
|
|
64
|
+
severity: "error",
|
|
65
|
+
message: `Spec "${document.frontmatter.id}" references unknown report layer "${layer}".`,
|
|
66
|
+
filePath: document.filePath,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return issues;
|
|
71
|
+
}
|
|
72
|
+
function normalizeLayer(value, index) {
|
|
73
|
+
if (!isRecord(value))
|
|
74
|
+
throw new Error(`featureSpecMd.report.layers[${index}] must be an object.`);
|
|
75
|
+
if (typeof value.id !== "string" || !value.id.trim())
|
|
76
|
+
throw new Error(`featureSpecMd.report.layers[${index}].id is required.`);
|
|
77
|
+
if (typeof value.title !== "string" || !value.title.trim())
|
|
78
|
+
throw new Error(`featureSpecMd.report.layers[${index}].title is required.`);
|
|
79
|
+
if (value.description !== undefined && typeof value.description !== "string")
|
|
80
|
+
throw new Error(`featureSpecMd.report.layers[${index}].description must be a string.`);
|
|
81
|
+
return {
|
|
82
|
+
id: value.id.trim(),
|
|
83
|
+
title: value.title.trim(),
|
|
84
|
+
description: value.description?.trim(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function optionalBoolean(value, name) {
|
|
88
|
+
if (value === undefined)
|
|
89
|
+
return undefined;
|
|
90
|
+
if (typeof value !== "boolean")
|
|
91
|
+
throw new Error(`${name} must be boolean.`);
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function isRecord(value) {
|
|
95
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAEjE,CAAC;IACF,OAAO,6BAA6B,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,KAAc;IAEd,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAE1E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAEnE,OAAO;QACL,MAAM,EAAE;YACN,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC;YACnC,iBAAiB,EAAE,eAAe,CAChC,MAAM,CAAC,iBAAiB,EACxB,wCAAwC,CACzC;YACD,oBAAoB,EAAE,eAAe,CACnC,MAAM,CAAC,oBAAoB,EAC3B,2CAA2C,CAC5C;SACF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,SAAyB,EACzB,SAAwB,EAAE;IAE1B,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,yBAAyB;gBAC/B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,oBAAoB,KAAK,CAAC,EAAE,kDAAkD;aACxF,CAAC,CAAC;QACL,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,2BAA2B;gBACjC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,oBAAoB,KAAK,CAAC,EAAE,iCAAiC;aACvE,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,SAAS,QAAQ,CAAC,WAAW,CAAC,EAAE,oDAAoD;gBAC7F,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC5B,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,SAAS,QAAQ,CAAC,WAAW,CAAC,EAAE,sCAAsC,KAAK,IAAI;gBACxF,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC5B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc,EAAE,KAAa;IACnD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,sBAAsB,CAAC,CAAC;IAC9E,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAClD,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,mBAAmB,CAAC,CAAC;IAC3E,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;QACxD,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,sBAAsB,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QAC1E,MAAM,IAAI,KAAK,CACb,+BAA+B,KAAK,iCAAiC,CACtE,CAAC;IACJ,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QACnB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;QACzB,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE;KACvC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAc,EAAE,IAAY;IACnD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mBAAmB,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
|
package/dist/featureSpecs.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare function buildCoverageSummary(specs: FeatureSpec[], references: T
|
|
|
15
15
|
export declare function validateCoverage(coverage: CoverageSummary, options?: {
|
|
16
16
|
requireRuleCoverage?: boolean;
|
|
17
17
|
requireScenarioCoverage?: boolean;
|
|
18
|
+
criticalJourneyIds?: string[];
|
|
18
19
|
}): ValidationIssue[];
|
|
19
20
|
/** Load and parse all feature specs matching the provided globs. */
|
|
20
21
|
export declare function loadFeatureSpecs(patterns: string[]): Promise<FeatureSpec[]>;
|
|
@@ -24,6 +25,7 @@ export declare function checkFeatureSpecs(options: {
|
|
|
24
25
|
tests?: string[];
|
|
25
26
|
requireRuleCoverage?: boolean;
|
|
26
27
|
requireScenarioCoverage?: boolean;
|
|
28
|
+
requireCriticalJourneyCoverage?: boolean;
|
|
27
29
|
}): Promise<{
|
|
28
30
|
specs: FeatureSpec[];
|
|
29
31
|
validationIssues: ValidationIssue[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureSpecs.d.ts","sourceRoot":"","sources":["../src/featureSpecs.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAEV,eAAe,EAGf,WAAW,
|
|
1
|
+
{"version":3,"file":"featureSpecs.d.ts","sourceRoot":"","sources":["../src/featureSpecs.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAEV,eAAe,EAGf,WAAW,EAMX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAYpB,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,WAAW,CAcb;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CA6GxE;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,SAAa,GACpB,aAAa,EAAE,CAyBjB;AAED,qFAAqF;AACrF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,4BAM7D;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,aAAa,EAAE,GAC1B,eAAe,CAkCjB;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,EACzB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,qBAkEP;AAED,oEAAoE;AACpE,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,0BAQxD;AAED,6EAA6E;AAC7E,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;;;;;;GA4BA"}
|
package/dist/featureSpecs.js
CHANGED
|
@@ -13,6 +13,7 @@ const scenarioTestTypes = [
|
|
|
13
13
|
"skip",
|
|
14
14
|
];
|
|
15
15
|
const screenshotPolicies = ["required", "optional", "skip"];
|
|
16
|
+
const journeyPaths = ["happy", "failure"];
|
|
16
17
|
/** Parse one feature spec Markdown document into structured metadata, rules, and scenarios. */
|
|
17
18
|
export function parseFeatureSpec(source, options = {}) {
|
|
18
19
|
const filePath = options.filePath ?? "<inline>";
|
|
@@ -137,6 +138,18 @@ export function validateCoverage(coverage, options = {}) {
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
}
|
|
141
|
+
for (const id of options.criticalJourneyIds ?? []) {
|
|
142
|
+
const item = coverage.scenarioCoverage.find((candidate) => candidate.id === id);
|
|
143
|
+
if (item && !item.covered) {
|
|
144
|
+
issues.push({
|
|
145
|
+
code: "missing-critical-journey-coverage",
|
|
146
|
+
severity: "error",
|
|
147
|
+
message: `Critical end-to-end journey "${item.id}" has no matching test reference.`,
|
|
148
|
+
filePath: item.filePath,
|
|
149
|
+
line: item.line,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
140
153
|
for (const ref of coverage.orphanScenarioReferences) {
|
|
141
154
|
issues.push({
|
|
142
155
|
code: "orphan-scenario-reference",
|
|
@@ -176,6 +189,12 @@ export async function checkFeatureSpecs(options) {
|
|
|
176
189
|
? validateCoverage(coverage, {
|
|
177
190
|
requireRuleCoverage: options.requireRuleCoverage,
|
|
178
191
|
requireScenarioCoverage: options.requireScenarioCoverage,
|
|
192
|
+
criticalJourneyIds: options.requireCriticalJourneyCoverage
|
|
193
|
+
? specs
|
|
194
|
+
.flatMap((spec) => spec.scenarios)
|
|
195
|
+
.filter((scenario) => scenario.journey?.critical)
|
|
196
|
+
.map((scenario) => scenario.id)
|
|
197
|
+
: [],
|
|
179
198
|
})
|
|
180
199
|
: [];
|
|
181
200
|
return {
|
|
@@ -194,6 +213,11 @@ function parseScenarios(lines, bodyStartLine, frontmatter) {
|
|
|
194
213
|
continue;
|
|
195
214
|
const steps = [];
|
|
196
215
|
const overrides = {};
|
|
216
|
+
const journey = {
|
|
217
|
+
scope: normalizeJourneyScope(frontmatter.journey),
|
|
218
|
+
path: normalizeJourneyPath(frontmatter.path),
|
|
219
|
+
critical: normalizeBoolean(frontmatter.critical),
|
|
220
|
+
};
|
|
197
221
|
for (let j = i + 1; j < lines.length; j += 1) {
|
|
198
222
|
if (/^##?#\s+/.test(lines[j]))
|
|
199
223
|
break;
|
|
@@ -203,6 +227,21 @@ function parseScenarios(lines, bodyStartLine, frontmatter) {
|
|
|
203
227
|
const screenshotsMatch = lines[j].trim().match(/^Screenshots:\s*(.+)$/i);
|
|
204
228
|
if (screenshotsMatch)
|
|
205
229
|
overrides.screenshots = normalizeScreenshotPolicy(screenshotsMatch[1]);
|
|
230
|
+
const journeyMatch = lines[j].trim().match(/^Journey:\s*(.+)$/i);
|
|
231
|
+
if (journeyMatch && journeyMatch[1].trim().toLowerCase() === "end-to-end")
|
|
232
|
+
journey.scope = "end-to-end";
|
|
233
|
+
const pathMatch = lines[j].trim().match(/^Path:\s*(.+)$/i);
|
|
234
|
+
if (pathMatch)
|
|
235
|
+
journey.path = normalizeJourneyPath(pathMatch[1]);
|
|
236
|
+
const criticalMatch = lines[j].trim().match(/^Critical:\s*(.+)$/i);
|
|
237
|
+
if (criticalMatch)
|
|
238
|
+
journey.critical = normalizeBoolean(criticalMatch[1]);
|
|
239
|
+
const systemsMatch = lines[j].trim().match(/^Systems:\s*(.+)$/i);
|
|
240
|
+
if (systemsMatch)
|
|
241
|
+
journey.systems = systemsMatch[1]
|
|
242
|
+
.split(",")
|
|
243
|
+
.map((item) => item.trim())
|
|
244
|
+
.filter(Boolean);
|
|
206
245
|
const step = lines[j].trim().match(/^(Given|When|Then|And|But)\s+(.+)$/);
|
|
207
246
|
if (step)
|
|
208
247
|
steps.push({
|
|
@@ -216,6 +255,14 @@ function parseScenarios(lines, bodyStartLine, frontmatter) {
|
|
|
216
255
|
title: match[2].trim(),
|
|
217
256
|
line: bodyStartLine + i,
|
|
218
257
|
evidence: resolveEvidencePolicy(frontmatter, overrides),
|
|
258
|
+
journey: journey.scope === "end-to-end"
|
|
259
|
+
? {
|
|
260
|
+
scope: journey.scope,
|
|
261
|
+
path: journey.path ?? "happy",
|
|
262
|
+
critical: journey.critical ?? false,
|
|
263
|
+
systems: journey.systems ?? [],
|
|
264
|
+
}
|
|
265
|
+
: undefined,
|
|
219
266
|
steps,
|
|
220
267
|
});
|
|
221
268
|
}
|
|
@@ -242,14 +289,52 @@ function normalizeScreenshotPolicy(value) {
|
|
|
242
289
|
return "skip";
|
|
243
290
|
return screenshotPolicies.find((candidate) => candidate === normalized);
|
|
244
291
|
}
|
|
292
|
+
function normalizeJourneyScope(value) {
|
|
293
|
+
if (typeof value !== "string")
|
|
294
|
+
return undefined;
|
|
295
|
+
return value.trim().toLowerCase() === "end-to-end" ? "end-to-end" : undefined;
|
|
296
|
+
}
|
|
297
|
+
function normalizeJourneyPath(value) {
|
|
298
|
+
if (typeof value !== "string")
|
|
299
|
+
return undefined;
|
|
300
|
+
const normalized = value.trim().toLowerCase();
|
|
301
|
+
return journeyPaths.find((candidate) => candidate === normalized);
|
|
302
|
+
}
|
|
303
|
+
function normalizeBoolean(value) {
|
|
304
|
+
if (typeof value === "boolean")
|
|
305
|
+
return value;
|
|
306
|
+
if (typeof value !== "string")
|
|
307
|
+
return undefined;
|
|
308
|
+
const normalized = value.trim().toLowerCase();
|
|
309
|
+
if (normalized === "true")
|
|
310
|
+
return true;
|
|
311
|
+
if (normalized === "false")
|
|
312
|
+
return false;
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
245
315
|
function validateEvidencePolicy(spec, frontmatter) {
|
|
246
316
|
const issues = [];
|
|
247
317
|
if (frontmatter.test && !normalizeTestType(frontmatter.test)) {
|
|
248
318
|
issues.push(issue(spec, "invalid-test-type", "error", `Feature test policy must be one of ${scenarioTestTypes.join(", ")}.`));
|
|
249
319
|
}
|
|
250
|
-
if (frontmatter.screenshots &&
|
|
320
|
+
if (frontmatter.screenshots &&
|
|
321
|
+
!normalizeScreenshotPolicy(frontmatter.screenshots)) {
|
|
251
322
|
issues.push(issue(spec, "invalid-screenshot-policy", "error", `Feature screenshots policy must be one of ${screenshotPolicies.join(", ")} or none.`));
|
|
252
323
|
}
|
|
324
|
+
if (frontmatter.journey && !normalizeJourneyScope(frontmatter.journey)) {
|
|
325
|
+
issues.push(issue(spec, "invalid-journey-scope", "error", 'Feature journey policy must be "end-to-end".'));
|
|
326
|
+
}
|
|
327
|
+
if (frontmatter.path && !normalizeJourneyPath(frontmatter.path)) {
|
|
328
|
+
issues.push(issue(spec, "invalid-journey-path", "error", `Feature journey path must be one of ${journeyPaths.join(", ")}.`));
|
|
329
|
+
}
|
|
330
|
+
if (frontmatter.critical !== undefined &&
|
|
331
|
+
normalizeBoolean(frontmatter.critical) === undefined) {
|
|
332
|
+
issues.push(issue(spec, "invalid-journey-criticality", "error", "Feature journey critical policy must be true or false."));
|
|
333
|
+
}
|
|
334
|
+
if (!normalizeJourneyScope(frontmatter.journey) &&
|
|
335
|
+
(frontmatter.path !== undefined || frontmatter.critical !== undefined)) {
|
|
336
|
+
issues.push(issue(spec, "journey-default-without-scope", "error", "Feature path and critical defaults require journey: end-to-end."));
|
|
337
|
+
}
|
|
253
338
|
return issues;
|
|
254
339
|
}
|
|
255
340
|
function issue(spec, code, severity, message, line) {
|