@ng-prism/plugin-coverage 22.6.0 → 22.7.0-beta.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 +6 -6
- package/dist/coverage-contributions.d.ts +1 -10
- package/dist/coverage-contributions.d.ts.map +1 -1
- package/dist/coverage-contributions.js +0 -16
- package/dist/coverage-header-badge.component.js +2 -2
- package/dist/coverage-metric-tone.d.ts +4 -0
- package/dist/coverage-metric-tone.d.ts.map +1 -0
- package/dist/coverage-metric-tone.js +5 -0
- package/dist/coverage-panel.component.d.ts +7 -2
- package/dist/coverage-panel.component.d.ts.map +1 -1
- package/dist/coverage-panel.component.js +64 -68
- package/dist/coverage-plugin.browser.d.ts.map +1 -1
- package/dist/coverage-plugin.browser.js +1 -2
- package/dist/coverage-plugin.d.ts.map +1 -1
- package/dist/coverage-plugin.js +1 -2
- package/dist/coverage-summary.d.ts.map +1 -1
- package/dist/coverage-summary.js +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ng-prism/plugin-coverage
|
|
2
2
|
|
|
3
|
-
Test coverage plugin for [@ng-prism/core](https://github.com/ng-prism/ng-prism). Reads Istanbul/v8 coverage reports at build time and displays per-component test coverage metrics
|
|
3
|
+
Test coverage plugin for [@ng-prism/core](https://github.com/ng-prism/ng-prism). Reads Istanbul/v8 coverage reports at build time and displays per-component test coverage metrics in a panel and a library-wide figure in the header.
|
|
4
4
|
|
|
5
5
|
> **Full documentation:** [ng-prism Docs: Coverage Plugin](https://ng-prism.github.io/ng-prism/#/plugins/coverage)
|
|
6
6
|
|
|
@@ -36,9 +36,9 @@ export default defineConfig({
|
|
|
36
36
|
## What It Does
|
|
37
37
|
|
|
38
38
|
- Adds a **Coverage** panel to the addon tab bar
|
|
39
|
-
- Shows
|
|
40
|
-
-
|
|
41
|
-
-
|
|
39
|
+
- Shows the four metrics, Statements, Branches, Functions and Lines, as cards with a bar and a tick at the threshold
|
|
40
|
+
- Grades each against its threshold: ok at the target, warn from 75% of it, danger below, the same rule the header pill, the component head and the sidebar use
|
|
41
|
+
- Shows a **Coverage** pill in the header with the library-wide figure
|
|
42
42
|
- Works with any test runner that outputs Istanbul-compatible coverage (Jest, Karma, Vitest, nyc, c8)
|
|
43
43
|
|
|
44
44
|
## Options
|
|
@@ -67,13 +67,13 @@ npx nx test my-lib --coverage
|
|
|
67
67
|
ng run my-lib:prism
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Each component's Coverage tab shows its statement, branch, function
|
|
70
|
+
Each component's Coverage tab shows its statement, branch, function and line coverage. The tab carries no badge (since 22.7); the figures are in the panel, the component head and the header.
|
|
71
71
|
|
|
72
72
|
## How It Works
|
|
73
73
|
|
|
74
74
|
**Build time:** The `onComponentScanned` hook reads `coverage-summary.json` (cached per build run, with mtime-based invalidation) and matches entries to components by file path. Coverage data is injected into `showcaseConfig.meta.coverage`.
|
|
75
75
|
|
|
76
|
-
**Runtime:** The `CoveragePanelComponent` reads the metadata and renders the metric
|
|
76
|
+
**Runtime:** The `CoveragePanelComponent` reads the metadata and renders the metric cards with `PrismMetricCardComponent` from `@ng-prism/core`. The panel component is lazy-loaded via `loadComponent`.
|
|
77
77
|
|
|
78
78
|
## License
|
|
79
79
|
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import type { NavigationDecorationDefinition
|
|
2
|
-
/**
|
|
3
|
-
* The score on the panel's own tab.
|
|
4
|
-
*
|
|
5
|
-
* Declared by the plugin because the core's built-in badge uses a fixed 90/70
|
|
6
|
-
* scale and could show red where the navigation marker, which uses the
|
|
7
|
-
* configured thresholds, shows amber. The build-time `summary` wins when
|
|
8
|
-
* present; otherwise the badge is derived from `thresholds`.
|
|
9
|
-
*/
|
|
10
|
-
export declare function coverageBadge(component: RuntimeComponent): PanelBadge | null;
|
|
1
|
+
import type { NavigationDecorationDefinition } from '@ng-prism/core/plugin';
|
|
11
2
|
export declare const COVERAGE_NAVIGATION_DECORATION: NavigationDecorationDefinition;
|
|
12
3
|
//# sourceMappingURL=coverage-contributions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-contributions.d.ts","sourceRoot":"","sources":["../src/coverage-contributions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,
|
|
1
|
+
{"version":3,"file":"coverage-contributions.d.ts","sourceRoot":"","sources":["../src/coverage-contributions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAoB,MAAM,uBAAuB,CAAC;AAY9F,eAAO,MAAM,8BAA8B,EAAE,8BAW5C,CAAC"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { deriveCoverageSummary } from './coverage-summary.js';
|
|
2
1
|
/**
|
|
3
2
|
* The runtime contributions both entry points declare. `coverage-plugin.ts`
|
|
4
3
|
* carries the build-time hook and must never be reachable from a browser
|
|
@@ -7,21 +6,6 @@ import { deriveCoverageSummary } from './coverage-summary.js';
|
|
|
7
6
|
function componentMeta(component) {
|
|
8
7
|
return component.meta?.showcaseConfig?.meta?.['coverage'] ?? null;
|
|
9
8
|
}
|
|
10
|
-
/**
|
|
11
|
-
* The score on the panel's own tab.
|
|
12
|
-
*
|
|
13
|
-
* Declared by the plugin because the core's built-in badge uses a fixed 90/70
|
|
14
|
-
* scale and could show red where the navigation marker, which uses the
|
|
15
|
-
* configured thresholds, shows amber. The build-time `summary` wins when
|
|
16
|
-
* present; otherwise the badge is derived from `thresholds`.
|
|
17
|
-
*/
|
|
18
|
-
export function coverageBadge(component) {
|
|
19
|
-
const meta = componentMeta(component);
|
|
20
|
-
if (!meta?.found)
|
|
21
|
-
return null;
|
|
22
|
-
const variant = meta.summary?.variant ?? (meta.thresholds ? deriveCoverageSummary(meta.score, meta.thresholds).variant : 'default');
|
|
23
|
-
return { text: String(meta.score), variant };
|
|
24
|
-
}
|
|
25
9
|
export const COVERAGE_NAVIGATION_DECORATION = {
|
|
26
10
|
id: 'coverage',
|
|
27
11
|
icon: 'shield-check',
|
|
@@ -31,7 +31,7 @@ export class CoverageHeaderBadgeComponent {
|
|
|
31
31
|
}, /* @ts-ignore */
|
|
32
32
|
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
33
33
|
static ɵfac = function CoverageHeaderBadgeComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || CoverageHeaderBadgeComponent)(); };
|
|
34
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CoverageHeaderBadgeComponent, selectors: [["prism-coverage-header-badge"]], decls: 1, vars: 1, consts: [["icon", "shield-check", "label", "Library coverage", 3, "value", "variant", "title"]], template: function CoverageHeaderBadgeComponent_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CoverageHeaderBadgeComponent, selectors: [["prism-coverage-header-badge"]], decls: 1, vars: 1, consts: [["icon", "shield-check", "label", "Library coverage", "shortLabel", "Coverage", 3, "value", "variant", "title"]], template: function CoverageHeaderBadgeComponent_Template(rf, ctx) { if (rf & 1) {
|
|
35
35
|
i0.ɵɵconditionalCreate(0, CoverageHeaderBadgeComponent_Conditional_0_Template, 1, 3, "prism-metric-badge", 0);
|
|
36
36
|
} if (rf & 2) {
|
|
37
37
|
let tmp_0_0;
|
|
@@ -40,6 +40,6 @@ export class CoverageHeaderBadgeComponent {
|
|
|
40
40
|
}
|
|
41
41
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CoverageHeaderBadgeComponent, [{
|
|
42
42
|
type: Component,
|
|
43
|
-
args: [{ selector: 'prism-coverage-header-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismMetricBadgeComponent], template: "@if (data(); as d) {\n <prism-metric-badge\n icon=\"shield-check\"\n label=\"Library coverage\"\n [value]=\"d.score + '%'\"\n [variant]=\"d.variant\"\n [title]=\"d.title\" />\n}\n" }]
|
|
43
|
+
args: [{ selector: 'prism-coverage-header-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismMetricBadgeComponent], template: "@if (data(); as d) {\n <prism-metric-badge\n icon=\"shield-check\"\n label=\"Library coverage\"\n shortLabel=\"Coverage\"\n [value]=\"d.score + '%'\"\n [variant]=\"d.variant\"\n [title]=\"d.title\" />\n}\n" }]
|
|
44
44
|
}], null, null); })();
|
|
45
45
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CoverageHeaderBadgeComponent, { className: "CoverageHeaderBadgeComponent", filePath: "coverage-header-badge.component.ts", lineNumber: 15 }); })();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type StatusTone } from '@ng-prism/core/plugin';
|
|
2
|
+
/** One coverage metric graded against its own threshold, by the rule every key figure uses. */
|
|
3
|
+
export declare function coverageMetricTone(pct: number, threshold: number): StatusTone;
|
|
4
|
+
//# sourceMappingURL=coverage-metric-tone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-metric-tone.d.ts","sourceRoot":"","sources":["../src/coverage-metric-tone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AAExF,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAE7E"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { statusFor, thresholdFromTarget } from '@ng-prism/core/plugin';
|
|
2
|
+
/** One coverage metric graded against its own threshold, by the rule every key figure uses. */
|
|
3
|
+
export function coverageMetricTone(pct, threshold) {
|
|
4
|
+
return statusFor(pct, thresholdFromTarget(threshold));
|
|
5
|
+
}
|
|
@@ -3,12 +3,17 @@ import * as i0 from "@angular/core";
|
|
|
3
3
|
export declare class CoveragePanelComponent {
|
|
4
4
|
readonly activeComponent: import("@angular/core").InputSignal<unknown>;
|
|
5
5
|
protected readonly coverage: import("@angular/core").Signal<CoverageData | null>;
|
|
6
|
+
protected readonly thresholds: import("@angular/core").Signal<CoverageThresholds>;
|
|
7
|
+
/** One card per metric, graded against that metric's own threshold, which also marks the bar. */
|
|
6
8
|
protected readonly summaryStats: import("@angular/core").Signal<{
|
|
7
|
-
label:
|
|
9
|
+
label: "Lines" | "Branches" | "Functions" | "Statements";
|
|
8
10
|
pct: number;
|
|
11
|
+
threshold: number;
|
|
12
|
+
tone: import("@ng-prism/core").BadgeTone;
|
|
13
|
+
statusLabel: "OK" | "Warning" | "Critical";
|
|
14
|
+
title: string;
|
|
9
15
|
}[]>;
|
|
10
16
|
protected readonly files: import("@angular/core").Signal<FileCoverageDetail[]>;
|
|
11
|
-
protected readonly thresholds: import("@angular/core").Signal<CoverageThresholds>;
|
|
12
17
|
protected fileName(path: string): string;
|
|
13
18
|
static ɵfac: i0.ɵɵFactoryDeclaration<CoveragePanelComponent, never>;
|
|
14
19
|
static ɵcmp: i0.ɵɵComponentDeclaration<CoveragePanelComponent, "prism-coverage-panel", never, { "activeComponent": { "alias": "activeComponent"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-panel.component.d.ts","sourceRoot":"","sources":["../src/coverage-panel.component.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coverage-panel.component.d.ts","sourceRoot":"","sources":["../src/coverage-panel.component.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;;AAShG,qBAQa,sBAAsB;IAC/B,QAAQ,CAAC,eAAe,+CAAwB;IAEhD,SAAS,CAAC,QAAQ,CAAC,QAAQ,sDAIxB;IAEH,SAAS,CAAC,QAAQ,CAAC,UAAU,qDAA0F;IAEvH,iGAAiG;IACjG,SAAS,CAAC,QAAQ,CAAC,YAAY;;;;;;;SA2B5B;IAEH,SAAS,CAAC,QAAQ,CAAC,KAAK,uDAAsE;IAE9F,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;yCA3C/B,sBAAsB;2CAAtB,sBAAsB;CAgDlC"}
|
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
|
2
|
+
import { PrismMetricCardComponent, toneForStatus } from '@ng-prism/core';
|
|
3
|
+
import { coverageMetricTone } from './coverage-metric-tone.js';
|
|
2
4
|
import * as i0 from "@angular/core";
|
|
3
5
|
const _forTrack0 = ($index, $item) => $item.label;
|
|
4
6
|
const _forTrack1 = ($index, $item) => $item.path;
|
|
5
|
-
function
|
|
6
|
-
i0.ɵɵ
|
|
7
|
-
i0.ɵɵtext(2);
|
|
8
|
-
i0.ɵɵdomElementEnd();
|
|
9
|
-
i0.ɵɵdomElementStart(3, "div", 6);
|
|
10
|
-
i0.ɵɵtext(4);
|
|
11
|
-
i0.ɵɵdomElementStart(5, "small");
|
|
12
|
-
i0.ɵɵtext(6, "%");
|
|
13
|
-
i0.ɵɵdomElementEnd()();
|
|
14
|
-
i0.ɵɵdomElementStart(7, "div", 7);
|
|
15
|
-
i0.ɵɵdomElement(8, "div", 8);
|
|
16
|
-
i0.ɵɵdomElementEnd()();
|
|
7
|
+
function CoveragePanelComponent_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
8
|
+
i0.ɵɵelement(0, "prism-metric-card", 2);
|
|
17
9
|
} if (rf & 2) {
|
|
18
10
|
const stat_r1 = ctx.$implicit;
|
|
19
|
-
i0.ɵɵ
|
|
20
|
-
i0.ɵɵtextInterpolate(stat_r1.label);
|
|
21
|
-
i0.ɵɵadvance(2);
|
|
22
|
-
i0.ɵɵtextInterpolate(stat_r1.pct);
|
|
23
|
-
i0.ɵɵadvance(4);
|
|
24
|
-
i0.ɵɵstyleProp("width", stat_r1.pct, "%");
|
|
11
|
+
i0.ɵɵproperty("label", stat_r1.label)("value", "" + stat_r1.pct)("tone", stat_r1.tone)("statusLabel", stat_r1.statusLabel)("fraction", stat_r1.pct / 100)("threshold", stat_r1.threshold / 100)("title", stat_r1.title);
|
|
25
12
|
} }
|
|
26
|
-
function
|
|
27
|
-
i0.ɵɵ
|
|
13
|
+
function CoveragePanelComponent_Conditional_0_Conditional_3_For_13_Template(rf, ctx) { if (rf & 1) {
|
|
14
|
+
i0.ɵɵelementStart(0, "div", 5)(1, "span", 6);
|
|
28
15
|
i0.ɵɵtext(2);
|
|
29
|
-
i0.ɵɵ
|
|
30
|
-
i0.ɵɵ
|
|
16
|
+
i0.ɵɵelementEnd();
|
|
17
|
+
i0.ɵɵelementStart(3, "span", 7);
|
|
31
18
|
i0.ɵɵtext(4);
|
|
32
|
-
i0.ɵɵ
|
|
33
|
-
i0.ɵɵ
|
|
19
|
+
i0.ɵɵelementEnd();
|
|
20
|
+
i0.ɵɵelementStart(5, "span", 7);
|
|
34
21
|
i0.ɵɵtext(6);
|
|
35
|
-
i0.ɵɵ
|
|
36
|
-
i0.ɵɵ
|
|
22
|
+
i0.ɵɵelementEnd();
|
|
23
|
+
i0.ɵɵelementStart(7, "span", 7);
|
|
37
24
|
i0.ɵɵtext(8);
|
|
38
|
-
i0.ɵɵ
|
|
39
|
-
i0.ɵɵ
|
|
25
|
+
i0.ɵɵelementEnd();
|
|
26
|
+
i0.ɵɵelementStart(9, "span", 7);
|
|
40
27
|
i0.ɵɵtext(10);
|
|
41
|
-
i0.ɵɵ
|
|
28
|
+
i0.ɵɵelementEnd()();
|
|
42
29
|
} if (rf & 2) {
|
|
43
30
|
const file_r2 = ctx.$implicit;
|
|
44
31
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -61,46 +48,45 @@ function CoveragePanelComponent_Conditional_0_Conditional_4_For_13_Template(rf,
|
|
|
61
48
|
i0.ɵɵadvance();
|
|
62
49
|
i0.ɵɵtextInterpolate1("", file_r2.statements.pct, "%");
|
|
63
50
|
} }
|
|
64
|
-
function
|
|
65
|
-
i0.ɵɵ
|
|
51
|
+
function CoveragePanelComponent_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
i0.ɵɵelementStart(0, "div", 3)(1, "div", 4)(2, "span");
|
|
66
53
|
i0.ɵɵtext(3, "File");
|
|
67
|
-
i0.ɵɵ
|
|
68
|
-
i0.ɵɵ
|
|
54
|
+
i0.ɵɵelementEnd();
|
|
55
|
+
i0.ɵɵelementStart(4, "span");
|
|
69
56
|
i0.ɵɵtext(5, "Lines");
|
|
70
|
-
i0.ɵɵ
|
|
71
|
-
i0.ɵɵ
|
|
57
|
+
i0.ɵɵelementEnd();
|
|
58
|
+
i0.ɵɵelementStart(6, "span");
|
|
72
59
|
i0.ɵɵtext(7, "Branch");
|
|
73
|
-
i0.ɵɵ
|
|
74
|
-
i0.ɵɵ
|
|
60
|
+
i0.ɵɵelementEnd();
|
|
61
|
+
i0.ɵɵelementStart(8, "span");
|
|
75
62
|
i0.ɵɵtext(9, "Func");
|
|
76
|
-
i0.ɵɵ
|
|
77
|
-
i0.ɵɵ
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
i0.ɵɵelementStart(10, "span");
|
|
78
65
|
i0.ɵɵtext(11, "Stmt");
|
|
79
|
-
i0.ɵɵ
|
|
80
|
-
i0.ɵɵrepeaterCreate(12,
|
|
81
|
-
i0.ɵɵ
|
|
66
|
+
i0.ɵɵelementEnd()();
|
|
67
|
+
i0.ɵɵrepeaterCreate(12, CoveragePanelComponent_Conditional_0_Conditional_3_For_13_Template, 11, 13, "div", 5, _forTrack1);
|
|
68
|
+
i0.ɵɵelementEnd();
|
|
82
69
|
} if (rf & 2) {
|
|
83
70
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
84
71
|
i0.ɵɵadvance(12);
|
|
85
72
|
i0.ɵɵrepeater(ctx_r2.files());
|
|
86
73
|
} }
|
|
87
74
|
function CoveragePanelComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
88
|
-
i0.ɵɵ
|
|
89
|
-
i0.ɵɵrepeaterCreate(
|
|
90
|
-
i0.ɵɵ
|
|
91
|
-
i0.ɵɵconditionalCreate(
|
|
92
|
-
i0.ɵɵdomElementEnd();
|
|
75
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
76
|
+
i0.ɵɵrepeaterCreate(1, CoveragePanelComponent_Conditional_0_For_2_Template, 1, 7, "prism-metric-card", 2, _forTrack0);
|
|
77
|
+
i0.ɵɵelementEnd();
|
|
78
|
+
i0.ɵɵconditionalCreate(3, CoveragePanelComponent_Conditional_0_Conditional_3_Template, 14, 0, "div", 3);
|
|
93
79
|
} if (rf & 2) {
|
|
94
80
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
95
|
-
i0.ɵɵadvance(
|
|
81
|
+
i0.ɵɵadvance();
|
|
96
82
|
i0.ɵɵrepeater(ctx_r2.summaryStats());
|
|
97
83
|
i0.ɵɵadvance(2);
|
|
98
|
-
i0.ɵɵconditional(ctx_r2.files().length ?
|
|
84
|
+
i0.ɵɵconditional(ctx_r2.files().length ? 3 : -1);
|
|
99
85
|
} }
|
|
100
86
|
function CoveragePanelComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
101
|
-
i0.ɵɵ
|
|
87
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "p");
|
|
102
88
|
i0.ɵɵtext(2, "No coverage data. Run tests with coverage enabled.");
|
|
103
|
-
i0.ɵɵ
|
|
89
|
+
i0.ɵɵelementEnd()();
|
|
104
90
|
} }
|
|
105
91
|
const FALLBACK_THRESHOLDS = {
|
|
106
92
|
lines: 80,
|
|
@@ -116,39 +102,49 @@ export class CoveragePanelComponent {
|
|
|
116
102
|
return comp?.meta?.showcaseConfig?.meta?.['coverage'] ?? null;
|
|
117
103
|
}, /* @ts-ignore */
|
|
118
104
|
...(ngDevMode ? [{ debugName: "coverage" }] : /* istanbul ignore next */ []));
|
|
105
|
+
thresholds = computed(() => this.coverage()?.thresholds ?? FALLBACK_THRESHOLDS, /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "thresholds" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** One card per metric, graded against that metric's own threshold, which also marks the bar. */
|
|
119
108
|
summaryStats = computed(() => {
|
|
120
109
|
const c = this.coverage();
|
|
110
|
+
const t = this.thresholds();
|
|
121
111
|
if (!c?.found)
|
|
122
112
|
return [];
|
|
113
|
+
const LABEL = { ok: 'OK', warn: 'Warning', danger: 'Critical' };
|
|
123
114
|
return [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
]
|
|
115
|
+
['Lines', 'lines'],
|
|
116
|
+
['Branches', 'branches'],
|
|
117
|
+
['Functions', 'functions'],
|
|
118
|
+
['Statements', 'statements']
|
|
119
|
+
].map(([label, key]) => {
|
|
120
|
+
const detail = c[key];
|
|
121
|
+
const status = coverageMetricTone(detail.pct, t[key]);
|
|
122
|
+
return {
|
|
123
|
+
label,
|
|
124
|
+
pct: detail.pct,
|
|
125
|
+
threshold: t[key],
|
|
126
|
+
tone: toneForStatus(status),
|
|
127
|
+
statusLabel: LABEL[status],
|
|
128
|
+
title: `${label}: ${detail.covered} of ${detail.total} covered · target ${t[key]}%`
|
|
129
|
+
};
|
|
130
|
+
});
|
|
129
131
|
}, /* @ts-ignore */
|
|
130
132
|
...(ngDevMode ? [{ debugName: "summaryStats" }] : /* istanbul ignore next */ []));
|
|
131
|
-
files = computed(() =>
|
|
132
|
-
return this.coverage()?.files ?? [];
|
|
133
|
-
}, /* @ts-ignore */
|
|
133
|
+
files = computed(() => this.coverage()?.files ?? [], /* @ts-ignore */
|
|
134
134
|
...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
135
|
-
thresholds = computed(() => {
|
|
136
|
-
return this.coverage()?.thresholds ?? FALLBACK_THRESHOLDS;
|
|
137
|
-
}, /* @ts-ignore */
|
|
138
|
-
...(ngDevMode ? [{ debugName: "thresholds" }] : /* istanbul ignore next */ []));
|
|
139
135
|
fileName(path) {
|
|
140
136
|
const parts = path.split('/');
|
|
141
137
|
return parts[parts.length - 1];
|
|
142
138
|
}
|
|
143
139
|
static ɵfac = function CoveragePanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || CoveragePanelComponent)(); };
|
|
144
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CoveragePanelComponent, selectors: [["prism-coverage-panel"]], inputs: { activeComponent: [1, "activeComponent"] }, decls: 2, vars: 1, consts: [[1, "cov-
|
|
145
|
-
i0.ɵɵconditionalCreate(0, CoveragePanelComponent_Conditional_0_Template,
|
|
140
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CoveragePanelComponent, selectors: [["prism-coverage-panel"]], inputs: { activeComponent: [1, "activeComponent"] }, decls: 2, vars: 1, consts: [[1, "cov-empty"], [1, "cov-body"], ["unit", "%", 3, "label", "value", "tone", "statusLabel", "fraction", "threshold", "title"], [1, "cov-files"], [1, "cov-file", "cov-file--header"], [1, "cov-file"], [1, "cov-file-name"], [1, "cov-file-val"]], template: function CoveragePanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
141
|
+
i0.ɵɵconditionalCreate(0, CoveragePanelComponent_Conditional_0_Template, 4, 1)(1, CoveragePanelComponent_Conditional_1_Template, 3, 0, "div", 0);
|
|
146
142
|
} if (rf & 2) {
|
|
147
143
|
i0.ɵɵconditional(ctx.coverage()?.found ? 0 : 1);
|
|
148
|
-
} },
|
|
144
|
+
} }, dependencies: [PrismMetricCardComponent], styles: [".cov-body[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 12px;\n padding: 16px 18px;\n}\n\n.cov-files[_ngcontent-%COMP%] {\n padding: 12px;\n margin: 0 18px 16px;\n background: var(--%NS%prism-bg-surface);\n border: 1px solid var(--%NS%prism-border);\n border-radius: 6px;\n}\n\n.cov-file[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr auto auto auto auto;\n gap: 16px;\n align-items: center;\n padding: 7px 4px;\n font-family: var(--%NS%font-mono);\n font-size: 12px;\n border-bottom: 1px solid var(--%NS%prism-border);\n}\n\n.cov-file[_ngcontent-%COMP%]:last-child {\n border-bottom: 0;\n}\n\n.cov-file--header[_ngcontent-%COMP%] {\n padding-bottom: 8px;\n font-size: 10.5px;\n font-weight: 700;\n color: var(--%NS%prism-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n border-bottom: 1px solid var(--%NS%prism-border);\n}\n\n.cov-file-name[_ngcontent-%COMP%] {\n color: var(--%NS%prism-text-2);\n}\n\n.cov-file-val[_ngcontent-%COMP%] {\n min-width: 54px;\n color: var(--%NS%prism-text);\n text-align: right;\n}\n\n.cov-file-val.bad[_ngcontent-%COMP%] {\n color: var(--%NS%prism-danger);\n}\n\n.cov-empty[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 120px;\n font-size: 13px;\n color: var(--%NS%prism-text-muted);\n text-align: center;\n}"] });
|
|
149
145
|
}
|
|
150
146
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CoveragePanelComponent, [{
|
|
151
147
|
type: Component,
|
|
152
|
-
args: [{ selector: 'prism-coverage-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (coverage()?.found) {\n <div class=\"cov-body\">\n
|
|
148
|
+
args: [{ selector: 'prism-coverage-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismMetricCardComponent], template: "@if (coverage()?.found) {\n <div class=\"cov-body\">\n @for (stat of summaryStats(); track stat.label) {\n <prism-metric-card\n unit=\"%\"\n [label]=\"stat.label\"\n [value]=\"'' + stat.pct\"\n [tone]=\"stat.tone\"\n [statusLabel]=\"stat.statusLabel\"\n [fraction]=\"stat.pct / 100\"\n [threshold]=\"stat.threshold / 100\"\n [title]=\"stat.title\" />\n }\n </div>\n\n @if (files().length) {\n <div class=\"cov-files\">\n <div class=\"cov-file cov-file--header\">\n <span>File</span>\n <span>Lines</span>\n <span>Branch</span>\n <span>Func</span>\n <span>Stmt</span>\n </div>\n @for (file of files(); track file.path) {\n <div class=\"cov-file\">\n <span class=\"cov-file-name\">{{ fileName(file.path) }}</span>\n <span\n class=\"cov-file-val\"\n [class.bad]=\"file.lines.pct < thresholds().lines\"\n >{{ file.lines.pct }}%</span\n >\n <span\n class=\"cov-file-val\"\n [class.bad]=\"file.branches.pct < thresholds().branches\"\n >{{ file.branches.pct }}%</span\n >\n <span\n class=\"cov-file-val\"\n [class.bad]=\"file.functions.pct < thresholds().functions\"\n >{{ file.functions.pct }}%</span\n >\n <span\n class=\"cov-file-val\"\n [class.bad]=\"file.statements.pct < thresholds().statements\"\n >{{ file.statements.pct }}%</span\n >\n </div>\n }\n </div>\n }\n} @else {\n <div class=\"cov-empty\">\n <p>No coverage data. Run tests with coverage enabled.</p>\n </div>\n}\n", styles: [".cov-body {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 12px;\n padding: 16px 18px;\n}\n\n.cov-files {\n padding: 12px;\n margin: 0 18px 16px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: 6px;\n}\n\n.cov-file {\n display: grid;\n grid-template-columns: 1fr auto auto auto auto;\n gap: 16px;\n align-items: center;\n padding: 7px 4px;\n font-family: var(--font-mono);\n font-size: 12px;\n border-bottom: 1px solid var(--prism-border);\n}\n\n.cov-file:last-child {\n border-bottom: 0;\n}\n\n.cov-file--header {\n padding-bottom: 8px;\n font-size: 10.5px;\n font-weight: 700;\n color: var(--prism-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n border-bottom: 1px solid var(--prism-border);\n}\n\n.cov-file-name {\n color: var(--prism-text-2);\n}\n\n.cov-file-val {\n min-width: 54px;\n color: var(--prism-text);\n text-align: right;\n}\n\n.cov-file-val.bad {\n color: var(--prism-danger);\n}\n\n.cov-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 120px;\n font-size: 13px;\n color: var(--prism-text-muted);\n text-align: center;\n}\n"] }]
|
|
153
149
|
}], null, { activeComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeComponent", required: false }] }] }); })();
|
|
154
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CoveragePanelComponent, { className: "CoveragePanelComponent", filePath: "coverage-panel.component.ts", lineNumber:
|
|
150
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CoveragePanelComponent, { className: "CoveragePanelComponent", filePath: "coverage-panel.component.ts", lineNumber: 21 }); })();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-plugin.browser.d.ts","sourceRoot":"","sources":["../src/coverage-plugin.browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,qBAAqB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"coverage-plugin.browser.d.ts","sourceRoot":"","sources":["../src/coverage-plugin.browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,qBAAqB,GAAG,aAAa,CAsB9E"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COVERAGE_NAVIGATION_DECORATION
|
|
1
|
+
import { COVERAGE_NAVIGATION_DECORATION } from './coverage-contributions.js';
|
|
2
2
|
export function coveragePlugin(_options) {
|
|
3
3
|
return {
|
|
4
4
|
name: '@ng-prism/plugin-coverage',
|
|
@@ -6,7 +6,6 @@ export function coveragePlugin(_options) {
|
|
|
6
6
|
{
|
|
7
7
|
id: 'coverage',
|
|
8
8
|
label: 'Coverage',
|
|
9
|
-
badge: coverageBadge,
|
|
10
9
|
icon: 'shield-check',
|
|
11
10
|
loadComponent: () => import('./coverage-panel.component.js').then((m) => m.CoveragePanelComponent)
|
|
12
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-plugin.d.ts","sourceRoot":"","sources":["../src/coverage-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAIrF,eAAO,MAAM,2BAA2B,EAAE,kBAKzC,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAW1G;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"coverage-plugin.d.ts","sourceRoot":"","sources":["../src/coverage-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAIrF,eAAO,MAAM,2BAA2B,EAAE,kBAKzC,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAW1G;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,aAAa,CAwD7E"}
|
package/dist/coverage-plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COVERAGE_NAVIGATION_DECORATION
|
|
1
|
+
import { COVERAGE_NAVIGATION_DECORATION } from './coverage-contributions.js';
|
|
2
2
|
const DEFAULT_COVERAGE_PATH = 'coverage/coverage-summary.json';
|
|
3
3
|
export const DEFAULT_COVERAGE_THRESHOLDS = {
|
|
4
4
|
lines: 80,
|
|
@@ -58,7 +58,6 @@ export function coveragePlugin(options) {
|
|
|
58
58
|
{
|
|
59
59
|
id: 'coverage',
|
|
60
60
|
label: 'Coverage',
|
|
61
|
-
badge: coverageBadge,
|
|
62
61
|
loadComponent: () => import('./coverage-panel.component.js').then((m) => m.CoveragePanelComponent)
|
|
63
62
|
}
|
|
64
63
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-summary.d.ts","sourceRoot":"","sources":["../src/coverage-summary.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coverage-summary.d.ts","sourceRoot":"","sources":["../src/coverage-summary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE/E;;;;;;;GAOG;AACH,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAEnE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,eAAe,CAKpG"}
|
package/dist/coverage-summary.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { statusFor, thresholdFromTarget } from '@ng-prism/core/plugin';
|
|
1
2
|
/**
|
|
2
3
|
* Threshold derivation lives in its own module so both sides can reach it:
|
|
3
4
|
* `coverage-plugin.ts` carries the build-time hook that reads the istanbul
|
|
@@ -12,6 +13,6 @@ export function avgThreshold(thresholds) {
|
|
|
12
13
|
}
|
|
13
14
|
export function deriveCoverageSummary(score, thresholds) {
|
|
14
15
|
const avg = avgThreshold(thresholds);
|
|
15
|
-
const variant = score
|
|
16
|
-
return { variant, label: `Coverage
|
|
16
|
+
const variant = statusFor(score, thresholdFromTarget(avg));
|
|
17
|
+
return { variant, label: `Coverage ${score}% · target ${avg}%` };
|
|
17
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-prism/plugin-coverage",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.7.0-beta.1",
|
|
4
4
|
"description": "Test coverage panel for ng-prism.",
|
|
5
5
|
"ng-add": {
|
|
6
6
|
"save": "devDependencies"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@angular/core": ">=20.0.0",
|
|
38
|
-
"@ng-prism/core": "^22.
|
|
38
|
+
"@ng-prism/core": "^22.7.0-0",
|
|
39
39
|
"@angular-devkit/schematics": ">=20.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|