@sdods/core 0.7.1 → 0.8.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/dist/.tsbuildinfo +1 -1
- package/dist/analyze/index.d.ts +2 -0
- package/dist/analyze/index.js +2 -0
- package/dist/analyze/traceability-render.d.ts +5 -0
- package/dist/analyze/traceability-render.js +254 -0
- package/dist/analyze/traceability.d.ts +87 -0
- package/dist/analyze/traceability.js +559 -0
- package/dist/api/json-path.d.ts +11 -1
- package/dist/api/json-path.js +23 -1
- package/dist/config/emulation.d.ts +74 -0
- package/dist/config/emulation.js +154 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/config/runner.js +4 -1
- package/dist/config/tags.d.ts +1 -1
- package/dist/config/tags.js +7 -0
- package/dist/errors.d.ts +1 -1
- package/dist/fixtures/api-context.d.ts +19 -1
- package/dist/fixtures/api-context.js +28 -1
- package/dist/fixtures/test.js +36 -0
- package/dist/fixtures/types.d.ts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/lint/index.js +86 -0
- package/dist/load/index.d.ts +4 -0
- package/dist/load/index.js +4 -0
- package/dist/load/profile.d.ts +22 -0
- package/dist/load/profile.js +109 -0
- package/dist/load/run.d.ts +85 -0
- package/dist/load/run.js +255 -0
- package/dist/load/script.d.ts +27 -0
- package/dist/load/script.js +188 -0
- package/dist/mail/extract.d.ts +38 -0
- package/dist/mail/extract.js +106 -0
- package/dist/mail/index.d.ts +8 -0
- package/dist/mail/index.js +11 -0
- package/dist/mail/mailpit.d.ts +26 -0
- package/dist/mail/mailpit.js +124 -0
- package/dist/mail/types.d.ts +38 -0
- package/dist/mail/types.js +2 -0
- package/dist/matrix/index.d.ts +104 -0
- package/dist/matrix/index.js +486 -0
- package/dist/steps/api.steps.js +5 -7
- package/dist/steps/browser.steps.d.ts +12 -0
- package/dist/steps/browser.steps.js +154 -3
- package/dist/steps/canvas.steps.d.ts +9 -0
- package/dist/steps/canvas.steps.js +331 -0
- package/dist/steps/email.steps.d.ts +2 -0
- package/dist/steps/email.steps.js +182 -0
- package/dist/steps/hybrid.steps.js +2 -2
- package/dist/steps/index.d.ts +2 -0
- package/dist/steps/index.js +2 -0
- package/package.json +4 -3
package/dist/analyze/index.d.ts
CHANGED
|
@@ -4,4 +4,6 @@ export { proposeProject, slugify, type ProposeOptions } from './propose.js';
|
|
|
4
4
|
export { applyProposal, importPlaywrightSpecs, type ApplyOptions, type ApplyResult, } from './apply.js';
|
|
5
5
|
export { computeCoverage, parseFeatures, pomRouteSteps, type CoverageOptions } from './coverage.js';
|
|
6
6
|
export * from './detectors.js';
|
|
7
|
+
export { REQ_TAG, buildTraceabilityReport, latestRunFor, loadRequirements, projectRequirements, readRunManifest, readRunResults, requirementUrl, traceScenarios, type RequirementDef, type RunAttempt, type RunResults, type TraceabilityOptions, } from './traceability.js';
|
|
8
|
+
export { TRACEABILITY_FORMATS, renderTraceability, type TraceabilityFormat, } from './traceability-render.js';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/analyze/index.js
CHANGED
|
@@ -4,4 +4,6 @@ export { proposeProject, slugify } from './propose.js';
|
|
|
4
4
|
export { applyProposal, importPlaywrightSpecs, } from './apply.js';
|
|
5
5
|
export { computeCoverage, parseFeatures, pomRouteSteps } from './coverage.js';
|
|
6
6
|
export * from './detectors.js';
|
|
7
|
+
export { REQ_TAG, buildTraceabilityReport, latestRunFor, loadRequirements, projectRequirements, readRunManifest, readRunResults, requirementUrl, traceScenarios, } from './traceability.js';
|
|
8
|
+
export { TRACEABILITY_FORMATS, renderTraceability, } from './traceability-render.js';
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TraceabilityReport } from '@sdods/contracts';
|
|
2
|
+
export declare const TRACEABILITY_FORMATS: readonly ["json", "csv", "md", "html"];
|
|
3
|
+
export type TraceabilityFormat = (typeof TRACEABILITY_FORMATS)[number];
|
|
4
|
+
export declare function renderTraceability(report: TraceabilityReport, format: TraceabilityFormat): string;
|
|
5
|
+
//# sourceMappingURL=traceability-render.d.ts.map
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export const TRACEABILITY_FORMATS = ['json', 'csv', 'md', 'html'];
|
|
2
|
+
const SIGN_OFF_FIELDS = [
|
|
3
|
+
['signedBy', 'Signed by'],
|
|
4
|
+
['role', 'Role'],
|
|
5
|
+
['date', 'Date'],
|
|
6
|
+
['decision', 'Decision'],
|
|
7
|
+
['notes', 'Notes'],
|
|
8
|
+
];
|
|
9
|
+
export function renderTraceability(report, format) {
|
|
10
|
+
switch (format) {
|
|
11
|
+
case 'json':
|
|
12
|
+
return `${JSON.stringify(report, null, 2)}\n`;
|
|
13
|
+
case 'csv':
|
|
14
|
+
return renderCsv(report);
|
|
15
|
+
case 'md':
|
|
16
|
+
return renderMarkdown(report);
|
|
17
|
+
case 'html':
|
|
18
|
+
return renderHtml(report);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const resultLabel = (r) => `${r.runnerProject}${r.exampleIndex != null ? ` #${r.exampleIndex}` : ''}: ${r.status}${r.flaky ? ' (flaky)' : ''}`;
|
|
22
|
+
const duration = (ms) => (ms == null ? '' : `${(ms / 1000).toFixed(1)}s`);
|
|
23
|
+
function runLines(report) {
|
|
24
|
+
const run = report.run;
|
|
25
|
+
if (!run)
|
|
26
|
+
return [['Run', 'none (static matrix)']];
|
|
27
|
+
const git = run.git?.sha
|
|
28
|
+
? `${run.git.sha}${run.git.branch ? ` (${run.git.branch})` : ''}${run.git.dirty ? ', uncommitted changes' : ''}`
|
|
29
|
+
: '';
|
|
30
|
+
return [
|
|
31
|
+
['Run', run.id],
|
|
32
|
+
['Environment', run.env ?? ''],
|
|
33
|
+
['Started', run.startedAt ?? ''],
|
|
34
|
+
['Finished', run.finishedAt ?? ''],
|
|
35
|
+
['Exit code', run.exitCode == null ? '' : String(run.exitCode)],
|
|
36
|
+
['Git', git],
|
|
37
|
+
['SDODS version', run.sdodsVersion ?? ''],
|
|
38
|
+
['Tags', run.tagsExpr ?? ''],
|
|
39
|
+
['CI', run.ci?.url ?? ''],
|
|
40
|
+
['Results from', run.resultsSource],
|
|
41
|
+
].filter(([, v]) => v !== '');
|
|
42
|
+
}
|
|
43
|
+
function summaryLines(report) {
|
|
44
|
+
const s = report.summary;
|
|
45
|
+
return [
|
|
46
|
+
['Requirements', String(s.requirements)],
|
|
47
|
+
['Covered', String(s.covered)],
|
|
48
|
+
['Passed', String(s.passed)],
|
|
49
|
+
['Failed', String(s.failed)],
|
|
50
|
+
['Not run', String(s.notRun)],
|
|
51
|
+
['Not covered', s.notCovered == null ? 'unknown (no requirements file)' : String(s.notCovered)],
|
|
52
|
+
['Undeclared ids', String(s.undeclared)],
|
|
53
|
+
['Scenarios', `${s.scenarios} (${s.tracedScenarios} traced, ${s.untracedScenarios} untraced)`],
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
// ── Markdown ─────────────────────────────────────────────────────────────────
|
|
57
|
+
const md = (s) => s.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
|
|
58
|
+
function mdTable(head, rows) {
|
|
59
|
+
return [
|
|
60
|
+
`| ${head.join(' | ')} |`,
|
|
61
|
+
`| ${head.map(() => '---').join(' | ')} |`,
|
|
62
|
+
...rows.map((r) => `| ${r.map(md).join(' | ')} |`),
|
|
63
|
+
].join('\n');
|
|
64
|
+
}
|
|
65
|
+
function renderMarkdown(report) {
|
|
66
|
+
const out = [];
|
|
67
|
+
out.push(`# Traceability: ${md(report.projectName)} (${report.project})`, '');
|
|
68
|
+
out.push(`Generated ${report.generatedAt}.`, '');
|
|
69
|
+
out.push(mdTable(['Field', 'Value'], runLines(report)), '');
|
|
70
|
+
out.push('## Summary', '', mdTable(['Field', 'Value'], summaryLines(report)), '');
|
|
71
|
+
out.push('## Requirements', '');
|
|
72
|
+
out.push(report.requirements.length
|
|
73
|
+
? mdTable(['Requirement', 'Title', 'Status', 'Scenarios'], report.requirements.map((r) => [
|
|
74
|
+
r.url ? `[${r.id}](${r.url})` : r.id,
|
|
75
|
+
`${r.title ?? ''}${r.declared === false ? ' (not in requirements file)' : ''}`,
|
|
76
|
+
r.status,
|
|
77
|
+
String(r.scenarios.length),
|
|
78
|
+
]))
|
|
79
|
+
: 'No requirements: no scenario carries an `@req:<id>` tag and no requirements file is configured.', '');
|
|
80
|
+
for (const r of report.requirements) {
|
|
81
|
+
out.push(`### ${md(r.id)}${r.title ? `: ${md(r.title)}` : ''}`, '');
|
|
82
|
+
out.push(`Status: **${r.status}**`, '');
|
|
83
|
+
if (!r.scenarios.length) {
|
|
84
|
+
out.push('No scenario covers this requirement.', '');
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
out.push(scenarioTable(r.scenarios), '');
|
|
88
|
+
}
|
|
89
|
+
if (report.untraced.length) {
|
|
90
|
+
out.push('## Scenarios without a requirement', '');
|
|
91
|
+
out.push(mdTable(['Scenario', 'Location', 'Status'], report.untraced.map((s) => [s.name, `${s.feature}:${s.line}`, s.status])), '');
|
|
92
|
+
}
|
|
93
|
+
out.push('## Notes', '', ...report.notes.map((n) => `- ${n}`), '');
|
|
94
|
+
out.push('## Sign-off', '');
|
|
95
|
+
out.push('To be completed by a person. SDODS does not fill in this section.', '');
|
|
96
|
+
out.push(mdTable(['Field', 'Value'], SIGN_OFF_FIELDS.map(([, label]) => [label, ''])), '');
|
|
97
|
+
return out.join('\n');
|
|
98
|
+
}
|
|
99
|
+
function scenarioTable(scenarios) {
|
|
100
|
+
return mdTable(['Scenario', 'Location', 'Tags', 'Status', 'Results', 'Duration'], scenarios.map((s) => [
|
|
101
|
+
s.name,
|
|
102
|
+
`${s.feature}:${s.line}`,
|
|
103
|
+
s.tags.join(' '),
|
|
104
|
+
s.status,
|
|
105
|
+
s.results.map(resultLabel).join('; '),
|
|
106
|
+
duration(s.results.reduce((n, r) => n + (r.durationMs ?? 0), 0) || undefined),
|
|
107
|
+
]));
|
|
108
|
+
}
|
|
109
|
+
// ── CSV ──────────────────────────────────────────────────────────────────────
|
|
110
|
+
const csvCell = (v) => {
|
|
111
|
+
const s = v == null ? '' : String(v);
|
|
112
|
+
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
113
|
+
};
|
|
114
|
+
const csvRow = (cells) => cells.map(csvCell).join(',');
|
|
115
|
+
/**
|
|
116
|
+
* One row per requirement × scenario × result, so a spreadsheet can filter and pivot it. The
|
|
117
|
+
* sign-off block follows after an empty line, with its values left blank.
|
|
118
|
+
*/
|
|
119
|
+
function renderCsv(report) {
|
|
120
|
+
const run = report.run;
|
|
121
|
+
const head = [
|
|
122
|
+
'requirement_id',
|
|
123
|
+
'requirement_title',
|
|
124
|
+
'requirement_url',
|
|
125
|
+
'declared',
|
|
126
|
+
'requirement_status',
|
|
127
|
+
'feature',
|
|
128
|
+
'line',
|
|
129
|
+
'scenario',
|
|
130
|
+
'tags',
|
|
131
|
+
'scenario_status',
|
|
132
|
+
'runner_project',
|
|
133
|
+
'browser',
|
|
134
|
+
'example',
|
|
135
|
+
'result_status',
|
|
136
|
+
'attempts',
|
|
137
|
+
'flaky',
|
|
138
|
+
'duration_ms',
|
|
139
|
+
'finished_at',
|
|
140
|
+
'run_id',
|
|
141
|
+
'env',
|
|
142
|
+
'git_sha',
|
|
143
|
+
'sdods_version',
|
|
144
|
+
];
|
|
145
|
+
const runCells = [run?.id, run?.env, run?.git?.sha, run?.sdodsVersion];
|
|
146
|
+
const rows = [csvRow(head)];
|
|
147
|
+
for (const r of report.requirements) {
|
|
148
|
+
const reqCells = [r.id, r.title, r.url, r.declared == null ? '' : r.declared, r.status];
|
|
149
|
+
if (!r.scenarios.length) {
|
|
150
|
+
rows.push(csvRow([...reqCells, '', '', '', '', '', '', '', '', '', '', '', '', '', ...runCells]));
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
for (const s of r.scenarios) {
|
|
154
|
+
const scCells = [s.feature, s.line, s.name, s.tags.join(' '), s.status];
|
|
155
|
+
if (!s.results.length) {
|
|
156
|
+
rows.push(csvRow([...reqCells, ...scCells, '', '', '', '', '', '', '', '', ...runCells]));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
for (const res of s.results) {
|
|
160
|
+
rows.push(csvRow([
|
|
161
|
+
...reqCells,
|
|
162
|
+
...scCells,
|
|
163
|
+
res.runnerProject,
|
|
164
|
+
res.browser,
|
|
165
|
+
res.exampleIndex,
|
|
166
|
+
res.status,
|
|
167
|
+
res.attempts,
|
|
168
|
+
res.flaky,
|
|
169
|
+
res.durationMs,
|
|
170
|
+
res.finishedAt,
|
|
171
|
+
...runCells,
|
|
172
|
+
]));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
rows.push('', csvRow(['sign_off', 'value']));
|
|
177
|
+
for (const [key] of SIGN_OFF_FIELDS)
|
|
178
|
+
rows.push(csvRow([key, '']));
|
|
179
|
+
return `${rows.join('\n')}\n`;
|
|
180
|
+
}
|
|
181
|
+
// ── HTML ─────────────────────────────────────────────────────────────────────
|
|
182
|
+
const esc = (s) => String(s ?? '')
|
|
183
|
+
.replace(/&/g, '&')
|
|
184
|
+
.replace(/</g, '<')
|
|
185
|
+
.replace(/>/g, '>')
|
|
186
|
+
.replace(/"/g, '"')
|
|
187
|
+
.replace(/'/g, ''');
|
|
188
|
+
const safeHref = (url) => (/^https?:\/\//i.test(url) ? esc(url) : '');
|
|
189
|
+
const badge = (status) => `<span class="st st-${esc(status)}">${esc(status)}</span>`;
|
|
190
|
+
function renderHtml(report) {
|
|
191
|
+
const kv = (rows) => `<table class="kv">${rows.map(([k, v]) => `<tr><th>${esc(k)}</th><td>${esc(v)}</td></tr>`).join('')}</table>`;
|
|
192
|
+
const reqLink = (id, url) => url && safeHref(url) ? `<a href="${safeHref(url)}">${esc(id)}</a>` : esc(id);
|
|
193
|
+
const overview = report.requirements
|
|
194
|
+
.map((r) => `<tr><td>${reqLink(r.id, r.url)}</td><td>${esc(r.title)}${r.declared === false ? ' <em>(not in requirements file)</em>' : ''}</td><td>${badge(r.status)}</td><td>${r.scenarios.length}</td></tr>`)
|
|
195
|
+
.join('');
|
|
196
|
+
const details = report.requirements
|
|
197
|
+
.map((r) => {
|
|
198
|
+
const body = r.scenarios.length
|
|
199
|
+
? `<table><thead><tr><th>Scenario</th><th>Location</th><th>Tags</th><th>Status</th><th>Results</th></tr></thead><tbody>${r.scenarios
|
|
200
|
+
.map((s) => `<tr><td>${esc(s.name)}</td><td><code>${esc(`${s.feature}:${s.line}`)}</code></td><td>${esc(s.tags.join(' '))}</td><td>${badge(s.status)}</td><td>${s.results.length
|
|
201
|
+
? s.results
|
|
202
|
+
.map((x) => `${esc(resultLabel(x))}${x.durationMs != null ? ` <span class="dim">${esc(duration(x.durationMs))}</span>` : ''}`)
|
|
203
|
+
.join('<br>')
|
|
204
|
+
: '<span class="dim">no result</span>'}</td></tr>`)
|
|
205
|
+
.join('')}</tbody></table>`
|
|
206
|
+
: '<p class="dim">No scenario covers this requirement.</p>';
|
|
207
|
+
return `<section><h3>${reqLink(r.id, r.url)}${r.title ? `: ${esc(r.title)}` : ''} ${badge(r.status)}</h3>${body}</section>`;
|
|
208
|
+
})
|
|
209
|
+
.join('');
|
|
210
|
+
const untraced = report.untraced.length
|
|
211
|
+
? `<h2>Scenarios without a requirement</h2><table><thead><tr><th>Scenario</th><th>Location</th><th>Status</th></tr></thead><tbody>${report.untraced
|
|
212
|
+
.map((s) => `<tr><td>${esc(s.name)}</td><td><code>${esc(`${s.feature}:${s.line}`)}</code></td><td>${badge(s.status)}</td></tr>`)
|
|
213
|
+
.join('')}</tbody></table>`
|
|
214
|
+
: '';
|
|
215
|
+
const signOff = SIGN_OFF_FIELDS.map(([, label]) => `<tr><th>${esc(label)}</th><td class="blank"></td></tr>`).join('');
|
|
216
|
+
return `<!doctype html>
|
|
217
|
+
<html lang="en">
|
|
218
|
+
<head>
|
|
219
|
+
<meta charset="utf-8">
|
|
220
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
221
|
+
<title>Traceability: ${esc(report.projectName)}</title>
|
|
222
|
+
<style>
|
|
223
|
+
body{font:14px/1.5 system-ui,-apple-system,Segoe UI,sans-serif;color:#1a1a1a;background:#fff;margin:0;padding:24px 16px;max-width:1100px;margin-inline:auto}
|
|
224
|
+
h1{font-size:22px;margin:0 0 4px}h2{font-size:17px;margin:28px 0 8px}h3{font-size:15px;margin:20px 0 6px}
|
|
225
|
+
table{border-collapse:collapse;width:100%;margin:6px 0}th,td{border:1px solid #d9d9d9;padding:5px 8px;text-align:left;vertical-align:top}
|
|
226
|
+
thead th{background:#f4f4f4}table.kv{width:auto}table.kv th{background:#f8f8f8;font-weight:600}
|
|
227
|
+
code{font-size:12px}.dim{color:#6b6b6b}.wrap{overflow-x:auto}
|
|
228
|
+
.st{display:inline-block;padding:0 6px;border-radius:3px;font-size:12px;font-weight:600;border:1px solid}
|
|
229
|
+
.st-passed{color:#11633a;border-color:#11633a}.st-failed{color:#a1161c;border-color:#a1161c}
|
|
230
|
+
.st-not-run,.st-skipped{color:#6b5300;border-color:#6b5300}.st-not-covered{color:#5a2d91;border-color:#5a2d91}
|
|
231
|
+
td.blank{height:28px;min-width:320px}
|
|
232
|
+
@media print{body{padding:0}section{break-inside:avoid}}
|
|
233
|
+
</style>
|
|
234
|
+
</head>
|
|
235
|
+
<body>
|
|
236
|
+
<h1>Traceability: ${esc(report.projectName)} <span class="dim">(${esc(report.project)})</span></h1>
|
|
237
|
+
<p class="dim">Generated ${esc(report.generatedAt)}</p>
|
|
238
|
+
${kv(runLines(report))}
|
|
239
|
+
<h2>Summary</h2>
|
|
240
|
+
${kv(summaryLines(report))}
|
|
241
|
+
<h2>Requirements</h2>
|
|
242
|
+
<div class="wrap"><table><thead><tr><th>Requirement</th><th>Title</th><th>Status</th><th>Scenarios</th></tr></thead><tbody>${overview}</tbody></table></div>
|
|
243
|
+
<div class="wrap">${details}</div>
|
|
244
|
+
${untraced}
|
|
245
|
+
<h2>Notes</h2>
|
|
246
|
+
<ul>${report.notes.map((n) => `<li>${esc(n)}</li>`).join('')}</ul>
|
|
247
|
+
<h2>Sign-off</h2>
|
|
248
|
+
<p>To be completed by a person. SDODS does not fill in this section.</p>
|
|
249
|
+
<table class="kv">${signOff}</table>
|
|
250
|
+
</body>
|
|
251
|
+
</html>
|
|
252
|
+
`;
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=traceability-render.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type ProjectConfig, type RunManifest, type SuiteStatus, type TraceabilityReport, type TraceRunInfo } from '@sdods/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Requirement traceability: `@req:<id>` tags on scenarios → the scenarios' results in one run.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately read-only over files that already exist (feature files, the run directory). The
|
|
6
|
+
* requirement id is opaque — SDODS does not know or decide what it points at — and the sign-off
|
|
7
|
+
* block of the export is left empty for a person to fill in.
|
|
8
|
+
*/
|
|
9
|
+
export declare const REQ_TAG = "req";
|
|
10
|
+
export interface RequirementDef {
|
|
11
|
+
id: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
}
|
|
14
|
+
type Project = ProjectConfig & {
|
|
15
|
+
root: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Read a requirements file. YAML/JSON accepts a list (`- id: REQ-1` / `title:` or plain strings),
|
|
19
|
+
* a map (`REQ-1: Title`), or either under a top-level `requirements:` key. CSV needs an `id`
|
|
20
|
+
* column and may have a `title` column.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadRequirements(file: string): RequirementDef[];
|
|
23
|
+
/** The requirements declared by the project, or null when it configures no requirements file. */
|
|
24
|
+
export declare function projectRequirements(project: Project): RequirementDef[] | null;
|
|
25
|
+
export declare function requirementUrl(project: ProjectConfig, id: string): string | undefined;
|
|
26
|
+
interface StaticScenario {
|
|
27
|
+
feature: string;
|
|
28
|
+
featureName: string;
|
|
29
|
+
line: number;
|
|
30
|
+
name: string;
|
|
31
|
+
tags: string[];
|
|
32
|
+
requirements: string[];
|
|
33
|
+
}
|
|
34
|
+
/** Every scenario of the project with its effective tags (Feature and Rule tags inherited). */
|
|
35
|
+
export declare function traceScenarios(project: Project, files?: string[]): StaticScenario[];
|
|
36
|
+
/** One attempt of one scenario in one runner project, as recorded in the run directory. */
|
|
37
|
+
export interface RunAttempt {
|
|
38
|
+
runnerProject: string;
|
|
39
|
+
/** feature path as the run recorded it (repo- or project-relative) */
|
|
40
|
+
uri: string;
|
|
41
|
+
/** line of the Scenario / Scenario Outline keyword, when known */
|
|
42
|
+
line?: number;
|
|
43
|
+
name: string;
|
|
44
|
+
exampleIndex: number | null;
|
|
45
|
+
attempt: number;
|
|
46
|
+
status: SuiteStatus;
|
|
47
|
+
durationMs?: number;
|
|
48
|
+
startedAt?: string;
|
|
49
|
+
finishedAt?: string;
|
|
50
|
+
error?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface RunResults {
|
|
53
|
+
source: TraceRunInfo['resultsSource'];
|
|
54
|
+
attempts: RunAttempt[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the scenario results of a run directory without a database.
|
|
58
|
+
*
|
|
59
|
+
* Cucumber messages (`messages.ndjson`, or its shard files) are the authoritative record: they hold
|
|
60
|
+
* every attempt in every runner project. The per-scenario `meta.json` files are the fallback — their
|
|
61
|
+
* directory is keyed without the browser, so a multi-browser run keeps only one of them per scenario.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readRunResults(runDir: string): Promise<RunResults>;
|
|
64
|
+
export declare function readRunManifest(runDir: string): RunManifest | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* The most recent run of `project` (and `env`, when given) under the artifacts root. Runs without a
|
|
67
|
+
* `run.json` are skipped: without it there is no way to tell which project or environment they ran.
|
|
68
|
+
*/
|
|
69
|
+
export declare function latestRunFor(artifactsRoot: string, project: string, env?: string): {
|
|
70
|
+
runId: string;
|
|
71
|
+
dir: string;
|
|
72
|
+
} | null;
|
|
73
|
+
export interface TraceabilityOptions {
|
|
74
|
+
project: Project;
|
|
75
|
+
/** run directory to read results from; omit for a static requirement → scenario matrix */
|
|
76
|
+
run?: {
|
|
77
|
+
runId: string;
|
|
78
|
+
dir: string;
|
|
79
|
+
};
|
|
80
|
+
/** feature files to consider (default: every feature of the project) */
|
|
81
|
+
files?: string[];
|
|
82
|
+
/** clock for `generatedAt` (tests pass a fixed date) */
|
|
83
|
+
now?: Date;
|
|
84
|
+
}
|
|
85
|
+
export declare function buildTraceabilityReport(opts: TraceabilityOptions): Promise<TraceabilityReport>;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=traceability.d.ts.map
|