@reportforge/playwright-pdf 0.24.0 → 0.25.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.
@@ -1,78 +1,78 @@
1
- <div class="section">
2
- {{!-- The release gate (when the layout renders it) already states the verdict
3
- prominently, so the chip would just repeat it — suppress it then. The layout
4
- passes options.releaseGateShown; it is absent for custom templates, so the
5
- chip renders by default there (a custom template may not include a gate). --}}
6
- {{#unless options.releaseGateShown}}
7
- <span class="verdict verdict--{{stats.verdict}}">
8
- {{verdictLabel stats.verdict}}
9
- </span>
10
- {{/unless}}
11
-
12
- <div class="kpi-strip">
13
- <div class="kpi-card kpi-card--total">
14
- <div class="kpi-card__value">{{stats.total}}</div>
15
- <div class="kpi-card__label">Total</div>
16
- </div>
17
- <div class="kpi-card kpi-card--pass">
18
- <div class="kpi-card__value">{{stats.passed}}</div>
19
- <div class="kpi-card__label">Passed</div>
20
- {{#if options.showPassRate}}
21
- <div class="kpi-card__rate">{{stats.passRate}}%</div>
22
- {{/if}}
23
- </div>
24
- {{!-- Zero-value status cards are dimmed: a coloured "0" for an absent status
25
- draws the eye to nothing and dilutes the counts that matter. --}}
26
- <div class="kpi-card kpi-card--fail{{#unless stats.failed}} kpi-card--zero{{/unless}}">
27
- <div class="kpi-card__value">{{stats.failed}}</div>
28
- <div class="kpi-card__label">Failed</div>
29
- </div>
30
- <div class="kpi-card kpi-card--timedout{{#unless stats.timedOut}} kpi-card--zero{{/unless}}">
31
- <div class="kpi-card__value">{{stats.timedOut}}</div>
32
- <div class="kpi-card__label">Timed Out</div>
33
- </div>
34
- <div class="kpi-card kpi-card--skip{{#unless stats.skipped}} kpi-card--zero{{/unless}}">
35
- <div class="kpi-card__value">{{stats.skipped}}</div>
36
- <div class="kpi-card__label">Skipped</div>
37
- </div>
38
- <div class="kpi-card kpi-card--flaky{{#unless stats.flaky}} kpi-card--zero{{/unless}}">
39
- <div class="kpi-card__value">{{stats.flaky}}</div>
40
- <div class="kpi-card__label">Flaky</div>
41
- </div>
42
- </div>
43
-
44
- <div class="summary-row">
45
- <div class="summary-row__item">
46
- <span class="summary-row__label">Duration</span>
47
- <span class="summary-row__value">{{formatDuration stats.duration}}</span>
48
- </div>
49
- {{!-- Pass rate normally shows on the Passed KPI card and the doughnut centre, so
50
- it is omitted here to avoid a third restatement on one page. Fallback: if
51
- BOTH of those are off (passRate modifier off AND charts off, e.g. a stripped
52
- minimal), show it here so the headline metric never disappears entirely. --}}
53
- {{#unless (or options.showPassRate @root.sections.charts)}}
54
- <div class="summary-row__item">
55
- <span class="summary-row__label">Pass Rate</span>
56
- <span class="summary-row__value" style="color:var(--pass);">{{stats.passRate}}%</span>
57
- </div>
58
- {{/unless}}
59
- <div class="summary-row__item">
60
- <span class="summary-row__label">Generated</span>
61
- <span class="summary-row__value">{{formatDate meta.generatedAt}}</span>
62
- </div>
63
- <div class="summary-row__item">
64
- <span class="summary-row__label">Project</span>
65
- <span class="summary-row__value">{{meta.projectName}}</span>
66
- </div>
67
- </div>
68
-
69
- {{#if stats.flaky}}
70
- <div class="flaky-callout">
71
- <span class="flaky-callout__icon">&#9888;</span>
72
- <div class="flaky-callout__body">
73
- <span class="flaky-callout__title">{{stats.flaky}} Flaky Test{{#if (gt stats.flaky 1)}}s{{/if}} Detected</span>
74
- <span class="flaky-callout__text">These tests passed on retry. Results may vary across re-runs. Investigate before shipping to production.</span>
75
- </div>
76
- </div>
77
- {{/if}}
78
- </div>
1
+ <div class="section">
2
+ {{!-- The release gate (when the layout renders it) already states the verdict
3
+ prominently, so the chip would just repeat it — suppress it then. The layout
4
+ passes options.releaseGateShown; it is absent for custom templates, so the
5
+ chip renders by default there (a custom template may not include a gate). --}}
6
+ {{#unless options.releaseGateShown}}
7
+ <span class="verdict verdict--{{stats.verdict}}">
8
+ {{verdictLabel stats.verdict}}
9
+ </span>
10
+ {{/unless}}
11
+
12
+ <div class="kpi-strip">
13
+ <div class="kpi-card kpi-card--total">
14
+ <div class="kpi-card__value">{{stats.total}}</div>
15
+ <div class="kpi-card__label">Total</div>
16
+ </div>
17
+ <div class="kpi-card kpi-card--pass">
18
+ <div class="kpi-card__value">{{stats.passed}}</div>
19
+ <div class="kpi-card__label">Passed</div>
20
+ {{#if options.showPassRate}}
21
+ <div class="kpi-card__rate">{{stats.passRate}}%</div>
22
+ {{/if}}
23
+ </div>
24
+ {{!-- Zero-value status cards are dimmed: a coloured "0" for an absent status
25
+ draws the eye to nothing and dilutes the counts that matter. --}}
26
+ <div class="kpi-card kpi-card--fail{{#unless stats.failed}} kpi-card--zero{{/unless}}">
27
+ <div class="kpi-card__value">{{stats.failed}}</div>
28
+ <div class="kpi-card__label">Failed</div>
29
+ </div>
30
+ <div class="kpi-card kpi-card--timedout{{#unless stats.timedOut}} kpi-card--zero{{/unless}}">
31
+ <div class="kpi-card__value">{{stats.timedOut}}</div>
32
+ <div class="kpi-card__label">Timed Out</div>
33
+ </div>
34
+ <div class="kpi-card kpi-card--skip{{#unless stats.skipped}} kpi-card--zero{{/unless}}">
35
+ <div class="kpi-card__value">{{stats.skipped}}</div>
36
+ <div class="kpi-card__label">Skipped</div>
37
+ </div>
38
+ <div class="kpi-card kpi-card--flaky{{#unless stats.flaky}} kpi-card--zero{{/unless}}">
39
+ <div class="kpi-card__value">{{stats.flaky}}</div>
40
+ <div class="kpi-card__label">Flaky</div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="summary-row">
45
+ <div class="summary-row__item">
46
+ <span class="summary-row__label">Duration</span>
47
+ <span class="summary-row__value">{{formatDuration stats.duration}}</span>
48
+ </div>
49
+ {{!-- Pass rate normally shows on the Passed KPI card and the doughnut centre, so
50
+ it is omitted here to avoid a third restatement on one page. Fallback: if
51
+ BOTH of those are off (passRate modifier off AND charts off, e.g. a stripped
52
+ minimal), show it here so the headline metric never disappears entirely. --}}
53
+ {{#unless (or options.showPassRate @root.sections.charts)}}
54
+ <div class="summary-row__item">
55
+ <span class="summary-row__label">Pass Rate</span>
56
+ <span class="summary-row__value" style="color:var(--pass);">{{stats.passRate}}%</span>
57
+ </div>
58
+ {{/unless}}
59
+ <div class="summary-row__item">
60
+ <span class="summary-row__label">Generated</span>
61
+ <span class="summary-row__value">{{formatDate meta.generatedAt}}</span>
62
+ </div>
63
+ <div class="summary-row__item">
64
+ <span class="summary-row__label">Project</span>
65
+ <span class="summary-row__value">{{meta.projectName}}</span>
66
+ </div>
67
+ </div>
68
+
69
+ {{#if stats.flaky}}
70
+ <div class="flaky-callout">
71
+ <span class="flaky-callout__icon">&#9888;</span>
72
+ <div class="flaky-callout__body">
73
+ <span class="flaky-callout__title">{{stats.flaky}} Flaky Test{{#if (gt stats.flaky 1)}}s{{/if}} Detected</span>
74
+ <span class="flaky-callout__text">These tests passed on retry. Results may vary across re-runs. Investigate before shipping to production.</span>
75
+ </div>
76
+ </div>
77
+ {{/if}}
78
+ </div>
@@ -1,35 +1,35 @@
1
- {{!-- Inline failure detail, rendered inside the single .failure-detail block in
2
- the suite breakdown (which provides the rail, indent, and page-break guard).
3
- The test row already shows the title/location, so this is error + screenshot
4
- + retry history only. Caller passes the context:
5
- failure — the FailureRecord for this test
6
- showFull — include the screenshot (compact templates pass false)
7
- showStack — render the stack trace instead of the bare message --}}
8
- {{#if (and failure.error.stack showStack)}}
9
- <pre>{{failure.error.stack}}</pre>
10
- {{else}}
11
- <div class="failure-item__message">{{failure.error.message}}</div>
12
- {{/if}}
13
-
14
- {{#if (and showFull failure.execution.failingStep)}}
15
- <div class="failing-step">
16
- <span class="failing-step__tag">Failed at</span>
17
- <span class="failing-step__title">{{failure.execution.failingStep.title}}</span>
18
- {{#if failure.execution.failingStep.location}}<span class="failing-step__loc">{{failure.execution.failingStep.location}}</span>{{/if}}
19
- </div>
20
- {{/if}}
21
-
22
- {{#if (and showFull failure.screenshotBase64)}}
23
- <img class="screenshot" src="{{failure.screenshotBase64}}" alt="Screenshot at failure">
24
- {{/if}}
25
-
26
- {{#if failure.retryHistory.length}}
27
- <div class="retry-history">
28
- Retry history:&nbsp;
29
- {{#each failure.retryHistory}}
30
- <span class="retry-attempt retry-attempt--{{status}}">
31
- #{{addOne attempt}} {{status}} · {{formatDuration duration}}
32
- </span>
33
- {{/each}}
34
- </div>
35
- {{/if}}
1
+ {{!-- Inline failure detail, rendered inside the single .failure-detail block in
2
+ the suite breakdown (which provides the rail, indent, and page-break guard).
3
+ The test row already shows the title/location, so this is error + screenshot
4
+ + retry history only. Caller passes the context:
5
+ failure — the FailureRecord for this test
6
+ showFull — include the screenshot (compact templates pass false)
7
+ showStack — render the stack trace instead of the bare message --}}
8
+ {{#if (and failure.error.stack showStack)}}
9
+ <pre>{{failure.error.stack}}</pre>
10
+ {{else}}
11
+ <div class="failure-item__message">{{failure.error.message}}</div>
12
+ {{/if}}
13
+
14
+ {{#if (and showFull failure.execution.failingStep)}}
15
+ <div class="failing-step">
16
+ <span class="failing-step__tag">Failed at</span>
17
+ <span class="failing-step__title">{{failure.execution.failingStep.title}}</span>
18
+ {{#if failure.execution.failingStep.location}}<span class="failing-step__loc">{{failure.execution.failingStep.location}}</span>{{/if}}
19
+ </div>
20
+ {{/if}}
21
+
22
+ {{#if (and showFull failure.screenshotBase64)}}
23
+ <img class="screenshot" src="{{failure.screenshotBase64}}" alt="Screenshot at failure">
24
+ {{/if}}
25
+
26
+ {{#if failure.retryHistory.length}}
27
+ <div class="retry-history">
28
+ Retry history:&nbsp;
29
+ {{#each failure.retryHistory}}
30
+ <span class="retry-attempt retry-attempt--{{status}}">
31
+ #{{addOne attempt}} {{status}} · {{formatDuration duration}}
32
+ </span>
33
+ {{/each}}
34
+ </div>
35
+ {{/if}}
@@ -1,17 +1,17 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>{{meta.title}}</title>
7
- <style>
8
- :root {
9
- --primary: {{meta.branding.primaryColor}};
10
- --accent: {{meta.branding.accentColor}};
11
- }
12
- {{{baseCSS}}}
13
- {{{templateCSS}}}
14
- </style>
15
- {{{chartjsScript}}}
16
- </head>
17
- <body>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{meta.title}}</title>
7
+ <style>
8
+ :root {
9
+ --primary: {{meta.branding.primaryColor}};
10
+ --accent: {{meta.branding.accentColor}};
11
+ }
12
+ {{{baseCSS}}}
13
+ {{{templateCSS}}}
14
+ </style>
15
+ {{{chartjsScript}}}
16
+ </head>
17
+ <body>
@@ -1,84 +1,84 @@
1
- {{#unless (gt stats.total 0)}}
2
- {{!-- Zero tests executed (e.g. a --grep that matched nothing): there is nothing
3
- to assess, so the gate must be NEUTRAL — never "APPROVED TO SHIP" over an
4
- empty run. --}}
5
- <div class="release-gate release-gate--neutral">
6
- <div class="release-gate__icon">&#x25CB;</div>
7
- <div class="release-gate__body">
8
- <div class="release-gate__label">Release Recommendation</div>
9
- <div class="release-gate__verdict release-gate__verdict--neutral">NO TESTS RAN</div>
10
- <div class="release-gate__detail">
11
- 0 tests were executed &mdash; nothing to assess. Check your test filters
12
- (e.g. --grep), project selection, or test discovery before relying on this report.
13
- </div>
14
- </div>
15
- <div class="release-gate__meta">
16
- {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
17
- {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
18
- </div>
19
- </div>
20
- {{else}}
21
- {{#if (gt stats.failed 0)}}
22
- <div class="release-gate release-gate--fail">
23
- <div class="release-gate__icon">&#x2715;</div>
24
- <div class="release-gate__body">
25
- <div class="release-gate__label">Release Recommendation</div>
26
- <div class="release-gate__verdict release-gate__verdict--fail">
27
- HOLD: {{stats.failed}} FAILURE{{#if (gt stats.failed 1)}}S{{/if}}
28
- </div>
29
- <div class="release-gate__detail">
30
- {{stats.failed}} of {{stats.total}} tests failed{{#if releaseGateTally}} ({{releaseGateTally}}){{/if}}.
31
- Each failure may be a product defect or an automation script issue.
32
- Review and verify manually before shipping.
33
- {{#if stats.flaky}} &nbsp;&middot;&nbsp; {{stats.flaky}} flaky (additional risk).{{/if}}
34
- </div>
35
- </div>
36
- <div class="release-gate__meta">
37
- {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
38
- {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
39
- </div>
40
- </div>
41
- {{else}}
42
- {{#if (gt stats.timedOut 0)}}
43
- <div class="release-gate release-gate--fail">
44
- <div class="release-gate__icon">&#x23F1;</div>
45
- <div class="release-gate__body">
46
- <div class="release-gate__label">Release Recommendation</div>
47
- <div class="release-gate__verdict release-gate__verdict--fail">
48
- HOLD: {{stats.timedOut}} TIMED OUT
49
- </div>
50
- <div class="release-gate__detail">
51
- {{stats.timedOut}} test{{#if (gt stats.timedOut 1)}}s{{/if}} exceeded timeout.
52
- Timeouts are often environment or automation issues.
53
- Review and verify manually before shipping.
54
- </div>
55
- </div>
56
- <div class="release-gate__meta">
57
- {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
58
- {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
59
- </div>
60
- </div>
61
- {{else}}
62
- <div class="release-gate release-gate--pass">
63
- <div class="release-gate__icon">&#x2713;</div>
64
- <div class="release-gate__body">
65
- <div class="release-gate__label">Release Recommendation</div>
66
- <div class="release-gate__verdict release-gate__verdict--pass">APPROVED TO SHIP</div>
67
- <div class="release-gate__detail">
68
- {{stats.passed}} of {{stats.total}} tests passed
69
- {{#if stats.skipped}}&nbsp;&middot;&nbsp; {{stats.skipped}} skipped{{/if}}
70
- {{#if stats.flaky}}&nbsp;&middot;&nbsp; {{stats.flaky}} flaky (monitor){{/if}}
71
- &nbsp;&middot;&nbsp; {{formatDuration stats.duration}}
72
- </div>
73
- </div>
74
- <div class="release-gate__meta">
75
- {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
76
- {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
77
- {{#if stats.flaky}}
78
- <span class="release-gate__flaky-warn">&#9888; {{stats.flaky}} flaky</span>
79
- {{/if}}
80
- </div>
81
- </div>
82
- {{/if}}
83
- {{/if}}
84
- {{/unless}}
1
+ {{#unless (gt stats.total 0)}}
2
+ {{!-- Zero tests executed (e.g. a --grep that matched nothing): there is nothing
3
+ to assess, so the gate must be NEUTRAL — never "APPROVED TO SHIP" over an
4
+ empty run. --}}
5
+ <div class="release-gate release-gate--neutral">
6
+ <div class="release-gate__icon">&#x25CB;</div>
7
+ <div class="release-gate__body">
8
+ <div class="release-gate__label">Release Recommendation</div>
9
+ <div class="release-gate__verdict release-gate__verdict--neutral">NO TESTS RAN</div>
10
+ <div class="release-gate__detail">
11
+ 0 tests were executed &mdash; nothing to assess. Check your test filters
12
+ (e.g. --grep), project selection, or test discovery before relying on this report.
13
+ </div>
14
+ </div>
15
+ <div class="release-gate__meta">
16
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
17
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
18
+ </div>
19
+ </div>
20
+ {{else}}
21
+ {{#if (gt stats.failed 0)}}
22
+ <div class="release-gate release-gate--fail">
23
+ <div class="release-gate__icon">&#x2715;</div>
24
+ <div class="release-gate__body">
25
+ <div class="release-gate__label">Release Recommendation</div>
26
+ <div class="release-gate__verdict release-gate__verdict--fail">
27
+ HOLD: {{stats.failed}} FAILURE{{#if (gt stats.failed 1)}}S{{/if}}
28
+ </div>
29
+ <div class="release-gate__detail">
30
+ {{stats.failed}} of {{stats.total}} tests failed{{#if releaseGateTally}} ({{releaseGateTally}}){{/if}}.
31
+ Each failure may be a product defect or an automation script issue.
32
+ Review and verify manually before shipping.
33
+ {{#if stats.flaky}} &nbsp;&middot;&nbsp; {{stats.flaky}} flaky (additional risk).{{/if}}
34
+ </div>
35
+ </div>
36
+ <div class="release-gate__meta">
37
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
38
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
39
+ </div>
40
+ </div>
41
+ {{else}}
42
+ {{#if (gt stats.timedOut 0)}}
43
+ <div class="release-gate release-gate--fail">
44
+ <div class="release-gate__icon">&#x23F1;</div>
45
+ <div class="release-gate__body">
46
+ <div class="release-gate__label">Release Recommendation</div>
47
+ <div class="release-gate__verdict release-gate__verdict--fail">
48
+ HOLD: {{stats.timedOut}} TIMED OUT
49
+ </div>
50
+ <div class="release-gate__detail">
51
+ {{stats.timedOut}} test{{#if (gt stats.timedOut 1)}}s{{/if}} exceeded timeout.
52
+ Timeouts are often environment or automation issues.
53
+ Review and verify manually before shipping.
54
+ </div>
55
+ </div>
56
+ <div class="release-gate__meta">
57
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
58
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
59
+ </div>
60
+ </div>
61
+ {{else}}
62
+ <div class="release-gate release-gate--pass">
63
+ <div class="release-gate__icon">&#x2713;</div>
64
+ <div class="release-gate__body">
65
+ <div class="release-gate__label">Release Recommendation</div>
66
+ <div class="release-gate__verdict release-gate__verdict--pass">APPROVED TO SHIP</div>
67
+ <div class="release-gate__detail">
68
+ {{stats.passed}} of {{stats.total}} tests passed
69
+ {{#if stats.skipped}}&nbsp;&middot;&nbsp; {{stats.skipped}} skipped{{/if}}
70
+ {{#if stats.flaky}}&nbsp;&middot;&nbsp; {{stats.flaky}} flaky (monitor){{/if}}
71
+ &nbsp;&middot;&nbsp; {{formatDuration stats.duration}}
72
+ </div>
73
+ </div>
74
+ <div class="release-gate__meta">
75
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
76
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
77
+ {{#if stats.flaky}}
78
+ <span class="release-gate__flaky-warn">&#9888; {{stats.flaky}} flaky</span>
79
+ {{/if}}
80
+ </div>
81
+ </div>
82
+ {{/if}}
83
+ {{/if}}
84
+ {{/unless}}
@@ -1,51 +1,51 @@
1
- {{#if tagGroups.length}}
2
- <div class="section">
3
- <h2>Requirements Traceability</h2>
4
- <p style="font-size:10px;color:var(--text-3);margin-bottom:12px;">
5
- {{#if requirementTagGroups.length}}Tests grouped by requirement ID tags.{{else}}No requirement ID tags found.{{/if}}
6
- Add <code>{ tag: '@REQ-001' }</code> to link tests to requirements.
7
- </p>
8
- {{#if requirementTagGroups.length}}
9
- <table>
10
- <thead>
11
- <tr>
12
- <th>Requirement / Tag</th>
13
- <th style="width:60px;">Tests</th>
14
- <th style="width:60px;">Passed</th>
15
- <th style="width:60px;">Failed</th>
16
- <th style="width:120px;">Pass Rate</th>
17
- </tr>
18
- </thead>
19
- <tbody>
20
- {{#each requirementTagGroups}}
21
- <tr class="no-break">
22
- <td><code class="req-matrix__tag" style="font-size:9.5px;">{{tag}}</code></td>
23
- <td>{{total}}</td>
24
- <td style="color:var(--pass);font-weight:600;">{{passed}}</td>
25
- <td style="color:{{#if failed}}var(--fail){{else}}var(--pass){{/if}};font-weight:600;">{{failed}}</td>
26
- <td>
27
- <div class="coverage-bar">
28
- <div class="coverage-bar__fill coverage-bar__fill--{{coverageBand coveragePct}}" style="width:{{coveragePct}}%;"></div>
29
- </div>
30
- <span style="font-size:9.5px;color:var(--text-3);">{{coveragePct}}%</span>
31
- </td>
32
- </tr>
33
- {{/each}}
34
- </tbody>
35
- </table>
36
- {{/if}}
37
- {{#if categoryTagGroups.length}}
38
- <div class="tag-summary no-break">
39
- <div class="tag-summary__label">Tag Summary</div>
40
- <div class="tag-summary__items">
41
- {{#each categoryTagGroups}}
42
- <span class="tag-summary__item{{#if failed}} tag-summary__item--fail{{/if}}">
43
- <code class="tag-summary__tag">{{tag}}</code>
44
- <span class="tag-summary__counts">{{passed}}/{{total}}</span>
45
- </span>
46
- {{/each}}
47
- </div>
48
- </div>
49
- {{/if}}
50
- </div>
51
- {{/if}}
1
+ {{#if tagGroups.length}}
2
+ <div class="section">
3
+ <h2>Requirements Traceability</h2>
4
+ <p style="font-size:10px;color:var(--text-3);margin-bottom:12px;">
5
+ {{#if requirementTagGroups.length}}Tests grouped by requirement ID tags.{{else}}No requirement ID tags found.{{/if}}
6
+ Add <code>{ tag: '@REQ-001' }</code> to link tests to requirements.
7
+ </p>
8
+ {{#if requirementTagGroups.length}}
9
+ <table>
10
+ <thead>
11
+ <tr>
12
+ <th>Requirement / Tag</th>
13
+ <th style="width:60px;">Tests</th>
14
+ <th style="width:60px;">Passed</th>
15
+ <th style="width:60px;">Failed</th>
16
+ <th style="width:120px;">Pass Rate</th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ {{#each requirementTagGroups}}
21
+ <tr class="no-break">
22
+ <td><code class="req-matrix__tag" style="font-size:9.5px;">{{tag}}</code></td>
23
+ <td>{{total}}</td>
24
+ <td style="color:var(--pass);font-weight:600;">{{passed}}</td>
25
+ <td style="color:{{#if failed}}var(--fail){{else}}var(--pass){{/if}};font-weight:600;">{{failed}}</td>
26
+ <td>
27
+ <div class="coverage-bar">
28
+ <div class="coverage-bar__fill coverage-bar__fill--{{coverageBand coveragePct}}" style="width:{{coveragePct}}%;"></div>
29
+ </div>
30
+ <span style="font-size:9.5px;color:var(--text-3);">{{coveragePct}}%</span>
31
+ </td>
32
+ </tr>
33
+ {{/each}}
34
+ </tbody>
35
+ </table>
36
+ {{/if}}
37
+ {{#if categoryTagGroups.length}}
38
+ <div class="tag-summary no-break">
39
+ <div class="tag-summary__label">Tag Summary</div>
40
+ <div class="tag-summary__items">
41
+ {{#each categoryTagGroups}}
42
+ <span class="tag-summary__item{{#if failed}} tag-summary__item--fail{{/if}}">
43
+ <code class="tag-summary__tag">{{tag}}</code>
44
+ <span class="tag-summary__counts">{{passed}}/{{total}}</span>
45
+ </span>
46
+ {{/each}}
47
+ </div>
48
+ </div>
49
+ {{/if}}
50
+ </div>
51
+ {{/if}}