api-tests-coverage 1.0.0 → 1.0.2
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/dashboard/assets/_basePickBy-DK6M9Uvz.js +1 -0
- package/dist/dashboard/assets/_baseUniq-BPCxdwlX.js +1 -0
- package/dist/dashboard/assets/arc-225azWF8.js +1 -0
- package/dist/dashboard/assets/architectureDiagram-VXUJARFQ-BW9j-2rE.js +36 -0
- package/dist/dashboard/assets/blockDiagram-VD42YOAC-CcWyVh_8.js +122 -0
- package/dist/dashboard/assets/c4Diagram-YG6GDRKO-Bq0y8Ms0.js +10 -0
- package/dist/dashboard/assets/channel-CPmdNASe.js +1 -0
- package/dist/dashboard/assets/chunk-4BX2VUAB-BrlBx_h4.js +1 -0
- package/dist/dashboard/assets/chunk-55IACEB6-BTGWaOkw.js +1 -0
- package/dist/dashboard/assets/chunk-B4BG7PRW-B-3Me5OV.js +165 -0
- package/dist/dashboard/assets/chunk-DI55MBZ5-BLZCyXU3.js +220 -0
- package/dist/dashboard/assets/chunk-FMBD7UC4-Bm9KRQE1.js +15 -0
- package/dist/dashboard/assets/chunk-QN33PNHL-DtWoo2Hn.js +1 -0
- package/dist/dashboard/assets/chunk-QZHKN3VN-DfaeFdsW.js +1 -0
- package/dist/dashboard/assets/chunk-TZMSLE5B-CM7Di7Gz.js +1 -0
- package/dist/dashboard/assets/classDiagram-2ON5EDUG-DTSnWk0q.js +1 -0
- package/dist/dashboard/assets/classDiagram-v2-WZHVMYZB-DTSnWk0q.js +1 -0
- package/dist/dashboard/assets/clone-DiFVAewv.js +1 -0
- package/dist/dashboard/assets/cose-bilkent-S5V4N54A-CI6S9tNC.js +1 -0
- package/dist/dashboard/assets/cytoscape.esm-CyJtwmzi.js +331 -0
- package/dist/dashboard/assets/dagre-6UL2VRFP-CD25jMwx.js +4 -0
- package/dist/dashboard/assets/diagram-PSM6KHXK-B2ziHyX_.js +24 -0
- package/dist/dashboard/assets/diagram-QEK2KX5R-DpPMBb2T.js +43 -0
- package/dist/dashboard/assets/diagram-S2PKOQOG-BplBVLaZ.js +24 -0
- package/dist/dashboard/assets/erDiagram-Q2GNP2WA-C6Kdrqa_.js +60 -0
- package/dist/dashboard/assets/flowDiagram-NV44I4VS-CBo4bgv8.js +162 -0
- package/dist/dashboard/assets/ganttDiagram-JELNMOA3-DL8oIX3C.js +267 -0
- package/dist/dashboard/assets/gitGraphDiagram-V2S2FVAM-5oPGxe6l.js +65 -0
- package/dist/dashboard/assets/graph-DWVIsnRu.js +1 -0
- package/dist/dashboard/assets/index-B2mS1bcV.js +522 -0
- package/dist/dashboard/assets/index-DBTGeaha.css +1 -0
- package/dist/dashboard/assets/infoDiagram-HS3SLOUP-D-bnjqH3.js +2 -0
- package/dist/dashboard/assets/journeyDiagram-XKPGCS4Q-DkT7QyWQ.js +139 -0
- package/dist/dashboard/assets/kanban-definition-3W4ZIXB7-CSO8OAdK.js +89 -0
- package/dist/dashboard/assets/katex-O9d3_IXG.js +261 -0
- package/dist/dashboard/assets/layout-BRUlIe_x.js +1 -0
- package/dist/dashboard/assets/mindmap-definition-VGOIOE7T-DabWpbNO.js +68 -0
- package/dist/dashboard/assets/pieDiagram-ADFJNKIX-x6W4JfPL.js +30 -0
- package/dist/dashboard/assets/quadrantDiagram-AYHSOK5B-Cs_QHGFN.js +7 -0
- package/dist/dashboard/assets/requirementDiagram-UZGBJVZJ-Y6IeHRvx.js +64 -0
- package/dist/dashboard/assets/sankeyDiagram-TZEHDZUN-CZT51ITh.js +10 -0
- package/dist/dashboard/assets/sequenceDiagram-WL72ISMW-DyAQKRQ-.js +145 -0
- package/dist/dashboard/assets/stateDiagram-FKZM4ZOC-DeadfBBo.js +1 -0
- package/dist/dashboard/assets/stateDiagram-v2-4FDKWEC3-D58eQQQS.js +1 -0
- package/dist/dashboard/assets/timeline-definition-IT6M3QCI-DYmeeClO.js +61 -0
- package/dist/dashboard/assets/treemap-GDKQZRPO-CKPSdFAX.js +162 -0
- package/dist/dashboard/assets/xychartDiagram-PRI3JC2R-Ch7ZZyVX.js +7 -0
- package/dist/dashboard/dist/assets/_basePickBy-DK6M9Uvz.js +1 -0
- package/dist/dashboard/dist/assets/_basePickBy-P9JMLvtQ.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-BLr5OOl5.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-BPCxdwlX.js +1 -0
- package/dist/dashboard/dist/assets/arc-225azWF8.js +1 -0
- package/dist/dashboard/dist/assets/arc-FwwTLzl4.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-BW9j-2rE.js +36 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-C7QAcrIt.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-BYvjSDpK.js +122 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-CcWyVh_8.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-Bq0y8Ms0.js +10 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-dUTtO4_k.js +10 -0
- package/dist/dashboard/dist/assets/channel-CPmdNASe.js +1 -0
- package/dist/dashboard/dist/assets/channel-DRY_ja-e.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BrlBx_h4.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-NmyQ9Lvf.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-BTGWaOkw.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-GAN8BMbh.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-B-3Me5OV.js +165 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-CKtaL90X.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-BLZCyXU3.js +220 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-GokZ0alw.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-Bm9KRQE1.js +15 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-D8tBTVUA.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-C59bvcTc.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-DtWoo2Hn.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-DfaeFdsW.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-E7ncuJVt.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BcLpi7P1.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CM7Di7Gz.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-DTSnWk0q.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dy8_C5lE.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-DTSnWk0q.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dy8_C5lE.js +1 -0
- package/dist/dashboard/dist/assets/clone-B0_qHCw2.js +1 -0
- package/dist/dashboard/dist/assets/clone-DiFVAewv.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BYK-qqpA.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-CI6S9tNC.js +1 -0
- package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-CD25jMwx.js +4 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-CNIXE38x.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-B2ziHyX_.js +24 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-Dza_WM04.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-175MJwNN.js +43 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-DpPMBb2T.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-BplBVLaZ.js +24 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DpUYqpiH.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-C6Kdrqa_.js +60 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-YGhaTIGv.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-CBo4bgv8.js +162 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-D9XX51TY.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BnlL2FL1.js +267 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-DL8oIX3C.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-5oPGxe6l.js +65 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-IqGQHaKk.js +65 -0
- package/dist/dashboard/dist/assets/graph-DOoKbdQ6.js +1 -0
- package/dist/dashboard/dist/assets/graph-DWVIsnRu.js +1 -0
- package/dist/dashboard/dist/assets/index-B2mS1bcV.js +522 -0
- package/dist/dashboard/dist/assets/index-CTnNA-vP.js +522 -0
- package/dist/dashboard/dist/assets/index-DBTGeaha.css +1 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-D-PYXUrg.js +2 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-D-bnjqH3.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DIxnZShx.js +139 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DkT7QyWQ.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-CSO8OAdK.js +89 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-DIQJ-dLy.js +89 -0
- package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
- package/dist/dashboard/dist/assets/layout-BRUlIe_x.js +1 -0
- package/dist/dashboard/dist/assets/layout-Dx_sC6cU.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-BWOv2jfH.js +68 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-DabWpbNO.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-B-ZMfN-z.js +30 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-x6W4JfPL.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-Cs_QHGFN.js +7 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-lHEZJOJ6.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-Dmgv7ZrT.js +64 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-Y6IeHRvx.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-CZT51ITh.js +10 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DYnsmtzq.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-Cp0y_3Co.js +145 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-DyAQKRQ-.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-CmJ3FBc_.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DeadfBBo.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-D58eQQQS.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-DBJHmcPu.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-CnYcftUT.js +61 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-DYmeeClO.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-B-4BtJ8O.js +162 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CKPSdFAX.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-Ch7ZZyVX.js +7 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-jiOJ2YB4.js +7 -0
- package/dist/dashboard/dist/index.html +14 -0
- package/dist/dashboard/dist/reports/business-coverage.json +201 -0
- package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
- package/dist/dashboard/dist/reports/coverage-summary.json +763 -0
- package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
- package/dist/dashboard/dist/reports/error-coverage.json +367 -0
- package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
- package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
- package/dist/dashboard/dist/reports/security-coverage.json +299 -0
- package/dist/dashboard/dist/vite.svg +1 -0
- package/dist/dashboard/index.html +14 -0
- package/dist/dashboard/reports/business-coverage.json +201 -0
- package/dist/dashboard/reports/coverage-intelligence.json +728 -0
- package/dist/dashboard/reports/coverage-summary.json +763 -0
- package/dist/dashboard/reports/endpoint-coverage.json +336 -0
- package/dist/dashboard/reports/error-coverage.json +367 -0
- package/dist/dashboard/reports/missing-tests-recommendations.json +285 -0
- package/dist/dashboard/reports/risk-prioritization.json +312 -0
- package/dist/dashboard/reports/security-coverage.json +299 -0
- package/dist/dashboard/vite.svg +1 -0
- package/dist/src/index.js +74 -4
- package/dist/src/serveDashboard.d.ts +30 -0
- package/dist/src/serveDashboard.d.ts.map +1 -0
- package/dist/src/serveDashboard.js +191 -0
- package/package.json +3 -2
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
{
|
|
2
|
+
"total": 27,
|
|
3
|
+
"covered": 7,
|
|
4
|
+
"percentage": 23.08,
|
|
5
|
+
"scanFindings": 0,
|
|
6
|
+
"categorySummary": {
|
|
7
|
+
"authentication": {
|
|
8
|
+
"total": 1,
|
|
9
|
+
"covered": 1
|
|
10
|
+
},
|
|
11
|
+
"authorization": {
|
|
12
|
+
"total": 13,
|
|
13
|
+
"covered": 0
|
|
14
|
+
},
|
|
15
|
+
"input-validation": {
|
|
16
|
+
"total": 11,
|
|
17
|
+
"covered": 5
|
|
18
|
+
},
|
|
19
|
+
"cryptography": {
|
|
20
|
+
"total": 1,
|
|
21
|
+
"covered": 0
|
|
22
|
+
},
|
|
23
|
+
"session-management": {
|
|
24
|
+
"total": 0,
|
|
25
|
+
"covered": 0
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"controls": [
|
|
29
|
+
{
|
|
30
|
+
"id": "authentication:bearerAuth",
|
|
31
|
+
"category": "authentication",
|
|
32
|
+
"description": "Authentication via security scheme \"bearerAuth\" (http/bearer)",
|
|
33
|
+
"covered": true,
|
|
34
|
+
"matchedTests": [
|
|
35
|
+
"returns 401 when no token",
|
|
36
|
+
"returns 401 when no token provided",
|
|
37
|
+
"returns 401 for invalid token",
|
|
38
|
+
"returns 401 without auth"
|
|
39
|
+
],
|
|
40
|
+
"coveredByScanReport": false
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "cryptography:https",
|
|
44
|
+
"category": "cryptography",
|
|
45
|
+
"description": "API servers include non-HTTPS URLs – cryptographic transport security may be missing",
|
|
46
|
+
"covered": false,
|
|
47
|
+
"matchedTests": [],
|
|
48
|
+
"coveredByScanReport": false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "authorization:get:/wallets",
|
|
52
|
+
"category": "authorization",
|
|
53
|
+
"description": "Authorization check for GET /wallets",
|
|
54
|
+
"endpoint": "GET /wallets",
|
|
55
|
+
"covered": false,
|
|
56
|
+
"matchedTests": [],
|
|
57
|
+
"coveredByScanReport": false
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "authorization:post:/wallets",
|
|
61
|
+
"category": "authorization",
|
|
62
|
+
"description": "Authorization check for POST /wallets",
|
|
63
|
+
"endpoint": "POST /wallets",
|
|
64
|
+
"covered": false,
|
|
65
|
+
"matchedTests": [],
|
|
66
|
+
"coveredByScanReport": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "input-validation:post:/wallets",
|
|
70
|
+
"category": "input-validation",
|
|
71
|
+
"description": "Input validation for POST /wallets",
|
|
72
|
+
"endpoint": "POST /wallets",
|
|
73
|
+
"covered": true,
|
|
74
|
+
"matchedTests": [
|
|
75
|
+
"returns 400 when currency is missing",
|
|
76
|
+
"returns 400 for unsupported currency",
|
|
77
|
+
"returns 422 for insufficient funds",
|
|
78
|
+
"returns 401 for invalid token",
|
|
79
|
+
"returns 400 for invalid currency",
|
|
80
|
+
"returns 422 with insufficient funds"
|
|
81
|
+
],
|
|
82
|
+
"coveredByScanReport": false
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "authorization:get:/wallets/{id}",
|
|
86
|
+
"category": "authorization",
|
|
87
|
+
"description": "Authorization check for GET /wallets/{id}",
|
|
88
|
+
"endpoint": "GET /wallets/{id}",
|
|
89
|
+
"covered": false,
|
|
90
|
+
"matchedTests": [],
|
|
91
|
+
"coveredByScanReport": false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "input-validation:get:/wallets/{id}",
|
|
95
|
+
"category": "input-validation",
|
|
96
|
+
"description": "Input validation for GET /wallets/{id}",
|
|
97
|
+
"endpoint": "GET /wallets/{id}",
|
|
98
|
+
"covered": true,
|
|
99
|
+
"matchedTests": [
|
|
100
|
+
"returns 422 for insufficient funds",
|
|
101
|
+
"returns 400 for invalid currency",
|
|
102
|
+
"returns 422 with insufficient funds"
|
|
103
|
+
],
|
|
104
|
+
"coveredByScanReport": false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "authorization:delete:/wallets/{id}",
|
|
108
|
+
"category": "authorization",
|
|
109
|
+
"description": "Authorization check for DELETE /wallets/{id}",
|
|
110
|
+
"endpoint": "DELETE /wallets/{id}",
|
|
111
|
+
"covered": false,
|
|
112
|
+
"matchedTests": [],
|
|
113
|
+
"coveredByScanReport": false
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "input-validation:delete:/wallets/{id}",
|
|
117
|
+
"category": "input-validation",
|
|
118
|
+
"description": "Input validation for DELETE /wallets/{id}",
|
|
119
|
+
"endpoint": "DELETE /wallets/{id}",
|
|
120
|
+
"covered": true,
|
|
121
|
+
"matchedTests": [
|
|
122
|
+
"returns 422 for insufficient funds",
|
|
123
|
+
"returns 400 for invalid currency",
|
|
124
|
+
"returns 422 with insufficient funds"
|
|
125
|
+
],
|
|
126
|
+
"coveredByScanReport": false
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "authorization:patch:/wallets/{id}/freeze",
|
|
130
|
+
"category": "authorization",
|
|
131
|
+
"description": "Authorization check for PATCH /wallets/{id}/freeze",
|
|
132
|
+
"endpoint": "PATCH /wallets/{id}/freeze",
|
|
133
|
+
"covered": false,
|
|
134
|
+
"matchedTests": [],
|
|
135
|
+
"coveredByScanReport": false
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "input-validation:patch:/wallets/{id}/freeze",
|
|
139
|
+
"category": "input-validation",
|
|
140
|
+
"description": "Input validation for PATCH /wallets/{id}/freeze",
|
|
141
|
+
"endpoint": "PATCH /wallets/{id}/freeze",
|
|
142
|
+
"covered": false,
|
|
143
|
+
"matchedTests": [],
|
|
144
|
+
"coveredByScanReport": false
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "authorization:patch:/wallets/{id}/unfreeze",
|
|
148
|
+
"category": "authorization",
|
|
149
|
+
"description": "Authorization check for PATCH /wallets/{id}/unfreeze",
|
|
150
|
+
"endpoint": "PATCH /wallets/{id}/unfreeze",
|
|
151
|
+
"covered": false,
|
|
152
|
+
"matchedTests": [],
|
|
153
|
+
"coveredByScanReport": false
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "input-validation:patch:/wallets/{id}/unfreeze",
|
|
157
|
+
"category": "input-validation",
|
|
158
|
+
"description": "Input validation for PATCH /wallets/{id}/unfreeze",
|
|
159
|
+
"endpoint": "PATCH /wallets/{id}/unfreeze",
|
|
160
|
+
"covered": false,
|
|
161
|
+
"matchedTests": [],
|
|
162
|
+
"coveredByScanReport": false
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "authorization:post:/wallets/{id}/fund",
|
|
166
|
+
"category": "authorization",
|
|
167
|
+
"description": "Authorization check for POST /wallets/{id}/fund",
|
|
168
|
+
"endpoint": "POST /wallets/{id}/fund",
|
|
169
|
+
"covered": false,
|
|
170
|
+
"matchedTests": [],
|
|
171
|
+
"coveredByScanReport": false
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "input-validation:post:/wallets/{id}/fund",
|
|
175
|
+
"category": "input-validation",
|
|
176
|
+
"description": "Input validation for POST /wallets/{id}/fund",
|
|
177
|
+
"endpoint": "POST /wallets/{id}/fund",
|
|
178
|
+
"covered": false,
|
|
179
|
+
"matchedTests": [],
|
|
180
|
+
"coveredByScanReport": false
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "authorization:post:/wallets/{id}/debit",
|
|
184
|
+
"category": "authorization",
|
|
185
|
+
"description": "Authorization check for POST /wallets/{id}/debit",
|
|
186
|
+
"endpoint": "POST /wallets/{id}/debit",
|
|
187
|
+
"covered": false,
|
|
188
|
+
"matchedTests": [],
|
|
189
|
+
"coveredByScanReport": false
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"id": "input-validation:post:/wallets/{id}/debit",
|
|
193
|
+
"category": "input-validation",
|
|
194
|
+
"description": "Input validation for POST /wallets/{id}/debit",
|
|
195
|
+
"endpoint": "POST /wallets/{id}/debit",
|
|
196
|
+
"covered": false,
|
|
197
|
+
"matchedTests": [],
|
|
198
|
+
"coveredByScanReport": false
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "authorization:post:/wallets/{id}/transfer",
|
|
202
|
+
"category": "authorization",
|
|
203
|
+
"description": "Authorization check for POST /wallets/{id}/transfer",
|
|
204
|
+
"endpoint": "POST /wallets/{id}/transfer",
|
|
205
|
+
"covered": false,
|
|
206
|
+
"matchedTests": [],
|
|
207
|
+
"coveredByScanReport": false
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"id": "input-validation:post:/wallets/{id}/transfer",
|
|
211
|
+
"category": "input-validation",
|
|
212
|
+
"description": "Input validation for POST /wallets/{id}/transfer",
|
|
213
|
+
"endpoint": "POST /wallets/{id}/transfer",
|
|
214
|
+
"covered": false,
|
|
215
|
+
"matchedTests": [],
|
|
216
|
+
"coveredByScanReport": false
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "authorization:post:/payments",
|
|
220
|
+
"category": "authorization",
|
|
221
|
+
"description": "Authorization check for POST /payments",
|
|
222
|
+
"endpoint": "POST /payments",
|
|
223
|
+
"covered": false,
|
|
224
|
+
"matchedTests": [],
|
|
225
|
+
"coveredByScanReport": false
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"id": "input-validation:post:/payments",
|
|
229
|
+
"category": "input-validation",
|
|
230
|
+
"description": "Input validation for POST /payments",
|
|
231
|
+
"endpoint": "POST /payments",
|
|
232
|
+
"covered": true,
|
|
233
|
+
"matchedTests": [
|
|
234
|
+
"returns 400 for missing required fields",
|
|
235
|
+
"returns 422 for invalid (non-existent) wallet",
|
|
236
|
+
"returns 422 when refunding a non-completed payment"
|
|
237
|
+
],
|
|
238
|
+
"coveredByScanReport": false
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "authorization:get:/payments/{id}",
|
|
242
|
+
"category": "authorization",
|
|
243
|
+
"description": "Authorization check for GET /payments/{id}",
|
|
244
|
+
"endpoint": "GET /payments/{id}",
|
|
245
|
+
"covered": false,
|
|
246
|
+
"matchedTests": [],
|
|
247
|
+
"coveredByScanReport": false
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"id": "input-validation:get:/payments/{id}",
|
|
251
|
+
"category": "input-validation",
|
|
252
|
+
"description": "Input validation for GET /payments/{id}",
|
|
253
|
+
"endpoint": "GET /payments/{id}",
|
|
254
|
+
"covered": true,
|
|
255
|
+
"matchedTests": [
|
|
256
|
+
"returns 422 when refunding a non-completed payment"
|
|
257
|
+
],
|
|
258
|
+
"coveredByScanReport": false
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"id": "authorization:post:/payments/{id}/refund",
|
|
262
|
+
"category": "authorization",
|
|
263
|
+
"description": "Authorization check for POST /payments/{id}/refund",
|
|
264
|
+
"endpoint": "POST /payments/{id}/refund",
|
|
265
|
+
"covered": false,
|
|
266
|
+
"matchedTests": [],
|
|
267
|
+
"coveredByScanReport": false
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"id": "input-validation:post:/payments/{id}/refund",
|
|
271
|
+
"category": "input-validation",
|
|
272
|
+
"description": "Input validation for POST /payments/{id}/refund",
|
|
273
|
+
"endpoint": "POST /payments/{id}/refund",
|
|
274
|
+
"covered": false,
|
|
275
|
+
"matchedTests": [],
|
|
276
|
+
"coveredByScanReport": false
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "authorization:get:/transactions",
|
|
280
|
+
"category": "authorization",
|
|
281
|
+
"description": "Authorization check for GET /transactions",
|
|
282
|
+
"endpoint": "GET /transactions",
|
|
283
|
+
"covered": false,
|
|
284
|
+
"matchedTests": [],
|
|
285
|
+
"coveredByScanReport": false
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"id": "authentication:JWT",
|
|
289
|
+
"category": "authentication",
|
|
290
|
+
"description": "JWT token signature and expiry validation",
|
|
291
|
+
"covered": true,
|
|
292
|
+
"matchedTests": [
|
|
293
|
+
"returns 401 for expired JWT",
|
|
294
|
+
"returns 401 for invalid JWT signature"
|
|
295
|
+
],
|
|
296
|
+
"coveredByScanReport": false
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/dist/src/index.js
CHANGED
|
@@ -59,6 +59,7 @@ const astAnalysisOrchestrator_1 = require("./ast/astAnalysisOrchestrator");
|
|
|
59
59
|
const projectDiscovery_1 = require("./discovery/projectDiscovery");
|
|
60
60
|
const businessRuleInference_1 = require("./inference/businessRuleInference");
|
|
61
61
|
const integrationFlowInference_1 = require("./inference/integrationFlowInference");
|
|
62
|
+
const serveDashboard_1 = require("./serveDashboard");
|
|
62
63
|
// Register all language AST analyzers at startup.
|
|
63
64
|
// This side-effect import ensures each language module's registerAnalyzer() call runs.
|
|
64
65
|
(0, astAnalysisOrchestrator_1.registerAllAnalyzers)();
|
|
@@ -1344,8 +1345,11 @@ program
|
|
|
1344
1345
|
.option('--export-inferred-rules', 'Export inferred rules/flows as editable YAML files')
|
|
1345
1346
|
.option('--no-infer-business-rules', 'Disable business rule inference')
|
|
1346
1347
|
.option('--no-infer-integration-flows', 'Disable integration flow inference')
|
|
1348
|
+
.option('--dashboard', 'Start the coverage dashboard after analysis')
|
|
1349
|
+
.option('--port <port>', 'Port for the dashboard server (requires --dashboard)', parseInt)
|
|
1350
|
+
.option('--open', 'Open the dashboard in your browser automatically (requires --dashboard)')
|
|
1347
1351
|
.action(async (options) => {
|
|
1348
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
1352
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1349
1353
|
const { metricsPort, serviceName } = setupObservability();
|
|
1350
1354
|
const logger = (0, observability_1.getLogger)();
|
|
1351
1355
|
const configPath = program.opts()['config'];
|
|
@@ -1419,11 +1423,48 @@ program
|
|
|
1419
1423
|
console.log(' Exported: generated-integration-flows.yaml');
|
|
1420
1424
|
}
|
|
1421
1425
|
}
|
|
1422
|
-
// ── 4.
|
|
1426
|
+
// ── 4. Endpoint coverage analysis → coverage-summary.json ─────────────
|
|
1427
|
+
const allCoverageResults = [];
|
|
1428
|
+
if (artifacts.specs.length > 0) {
|
|
1429
|
+
const testsGlob = artifacts.testFiles.length > 0
|
|
1430
|
+
? '{' + artifacts.testFiles.join(',') + '}'
|
|
1431
|
+
: path.join(rootDir, '**', '*');
|
|
1432
|
+
const detectedLanguages = artifacts.languages;
|
|
1433
|
+
for (const specPath of artifacts.specs) {
|
|
1434
|
+
try {
|
|
1435
|
+
console.log(`\nAnalyzing endpoint coverage for: ${path.basename(specPath)}`);
|
|
1436
|
+
const endpoints = await (0, endpointCoverage_1.parseOpenApiSpec)(specPath);
|
|
1437
|
+
const coverageMap = await (0, endpointCoverage_1.analyzeTestCoverage)(endpoints, testsGlob, detectedLanguages);
|
|
1438
|
+
const report = (0, endpointCoverage_1.buildCoverageReport)(coverageMap);
|
|
1439
|
+
const result = {
|
|
1440
|
+
type: 'endpoint',
|
|
1441
|
+
totalItems: report.total,
|
|
1442
|
+
coveredItems: report.covered,
|
|
1443
|
+
coveragePercent: report.percentage,
|
|
1444
|
+
details: report,
|
|
1445
|
+
};
|
|
1446
|
+
allCoverageResults.push(result);
|
|
1447
|
+
console.log(` ${report.covered}/${report.total} endpoints covered (${report.percentage}%)`);
|
|
1448
|
+
}
|
|
1449
|
+
catch (err) {
|
|
1450
|
+
warnings.push(`Endpoint coverage failed for ${path.basename(specPath)}: ` +
|
|
1451
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
if (allCoverageResults.length > 0) {
|
|
1455
|
+
const observabilityInfo = (0, observability_1.buildObservabilityInfo)(metricsPort);
|
|
1456
|
+
(0, reporting_1.generateMultiFormatReports)(allCoverageResults, ['json'], reportsDir, {}, observabilityInfo);
|
|
1457
|
+
console.log(`\nReports written to: ${reportsDir}`);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
warnings.push('No API spec files found; endpoint coverage analysis skipped.');
|
|
1462
|
+
}
|
|
1463
|
+
// ── 5. Emit warnings ───────────────────────────────────────────────────
|
|
1423
1464
|
for (const w of warnings) {
|
|
1424
1465
|
console.warn(`[WARN] ${w}`);
|
|
1425
1466
|
}
|
|
1426
|
-
// ──
|
|
1467
|
+
// ── 6. Configuration override log ──────────────────────────────────────
|
|
1427
1468
|
if (options['inferBusinessRules'] === false) {
|
|
1428
1469
|
console.log('\nConfiguration override detected: business rule inference disabled via CLI');
|
|
1429
1470
|
}
|
|
@@ -1431,7 +1472,36 @@ program
|
|
|
1431
1472
|
console.log('\nConfiguration override detected: integration flow inference disabled via CLI');
|
|
1432
1473
|
}
|
|
1433
1474
|
logger.info({ event: 'analyze_complete', warnings: warnings.length }, 'Agnostic project analysis complete');
|
|
1434
|
-
await finaliseObservability(
|
|
1475
|
+
await finaliseObservability(allCoverageResults, {}, metricsPort, serviceName);
|
|
1476
|
+
// ── 7. Optionally launch the dashboard ─────────────────────────────────
|
|
1477
|
+
if (options['dashboard']) {
|
|
1478
|
+
(0, serveDashboard_1.serveDashboard)({
|
|
1479
|
+
reportsDir: reportsDir,
|
|
1480
|
+
port: (_l = options['port']) !== null && _l !== void 0 ? _l : 4000,
|
|
1481
|
+
open: Boolean(options['open']),
|
|
1482
|
+
});
|
|
1483
|
+
// Keep the process alive — the HTTP server holds the event loop open
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
// ─── serve command ────────────────────────────────────────────────────────────
|
|
1487
|
+
program
|
|
1488
|
+
.command('serve')
|
|
1489
|
+
.description('Start the coverage dashboard UI and serve reports from your reports directory.')
|
|
1490
|
+
.option('--reports-dir <dir>', 'Directory containing report JSON files (default: reports/)')
|
|
1491
|
+
.option('--port <port>', 'Port to listen on (default: 4000)', parseInt)
|
|
1492
|
+
.option('--open', 'Open the dashboard in your browser automatically')
|
|
1493
|
+
.action((options) => {
|
|
1494
|
+
var _a, _b, _c, _d;
|
|
1495
|
+
const configPath = program.opts()['config'];
|
|
1496
|
+
const analyzerCfg = (0, config_1.loadCentralConfig)(configPath);
|
|
1497
|
+
const reportsDir = (_c = (_a = options['reportsDir']) !== null && _a !== void 0 ? _a : (_b = analyzerCfg.reports) === null || _b === void 0 ? void 0 : _b.outputDir) !== null && _c !== void 0 ? _c : 'reports';
|
|
1498
|
+
const port = (_d = options['port']) !== null && _d !== void 0 ? _d : 4000;
|
|
1499
|
+
(0, serveDashboard_1.serveDashboard)({
|
|
1500
|
+
reportsDir,
|
|
1501
|
+
port,
|
|
1502
|
+
open: Boolean(options['open']),
|
|
1503
|
+
});
|
|
1504
|
+
// Keep the process alive while the server runs
|
|
1435
1505
|
});
|
|
1436
1506
|
// Parse the command-line arguments
|
|
1437
1507
|
program.parse(process.argv);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in dashboard HTTP server.
|
|
3
|
+
*
|
|
4
|
+
* Serves the pre-built React dashboard from the npm package's
|
|
5
|
+
* `dashboard/dist/` directory and intercepts `/reports/*` requests to
|
|
6
|
+
* serve from the caller's actual reports directory — giving every user
|
|
7
|
+
* a live dashboard against their own coverage data with a single command.
|
|
8
|
+
*
|
|
9
|
+
* Usage (via CLI):
|
|
10
|
+
* npx api-tests-coverage serve [--reports-dir reports/] [--port 4000] [--open]
|
|
11
|
+
*/
|
|
12
|
+
import * as http from 'http';
|
|
13
|
+
export interface ServeDashboardOptions {
|
|
14
|
+
/** Directory containing coverage report JSON files. Default: reports/ */
|
|
15
|
+
reportsDir?: string;
|
|
16
|
+
/** Port to listen on. Default: 4000 */
|
|
17
|
+
port?: number;
|
|
18
|
+
/** Open the browser automatically. Default: false */
|
|
19
|
+
open?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start the dashboard HTTP server and return the server instance.
|
|
23
|
+
*
|
|
24
|
+
* The server:
|
|
25
|
+
* 1. Intercepts `/reports/*` → serves from `reportsDir`
|
|
26
|
+
* 2. Everything else → serves static files from `dashboard/dist/`
|
|
27
|
+
* 3. Unknown paths fall back to `index.html` (SPA client-side routing)
|
|
28
|
+
*/
|
|
29
|
+
export declare function serveDashboard(options?: ServeDashboardOptions): http.Server;
|
|
30
|
+
//# sourceMappingURL=serveDashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serveDashboard.d.ts","sourceRoot":"","sources":["../../src/serveDashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAwD7B,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,IAAI,CAAC,MAAM,CAkF/E"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in dashboard HTTP server.
|
|
4
|
+
*
|
|
5
|
+
* Serves the pre-built React dashboard from the npm package's
|
|
6
|
+
* `dashboard/dist/` directory and intercepts `/reports/*` requests to
|
|
7
|
+
* serve from the caller's actual reports directory — giving every user
|
|
8
|
+
* a live dashboard against their own coverage data with a single command.
|
|
9
|
+
*
|
|
10
|
+
* Usage (via CLI):
|
|
11
|
+
* npx api-tests-coverage serve [--reports-dir reports/] [--port 4000] [--open]
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.serveDashboard = serveDashboard;
|
|
48
|
+
const http = __importStar(require("http"));
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const url = __importStar(require("url"));
|
|
52
|
+
const child_process_1 = require("child_process");
|
|
53
|
+
// ─── MIME type map ─────────────────────────────────────────────────────────────
|
|
54
|
+
const MIME_TYPES = {
|
|
55
|
+
'.html': 'text/html; charset=utf-8',
|
|
56
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
57
|
+
'.mjs': 'application/javascript; charset=utf-8',
|
|
58
|
+
'.css': 'text/css; charset=utf-8',
|
|
59
|
+
'.json': 'application/json; charset=utf-8',
|
|
60
|
+
'.svg': 'image/svg+xml',
|
|
61
|
+
'.png': 'image/png',
|
|
62
|
+
'.ico': 'image/x-icon',
|
|
63
|
+
'.woff': 'font/woff',
|
|
64
|
+
'.woff2': 'font/woff2',
|
|
65
|
+
'.ttf': 'font/ttf',
|
|
66
|
+
'.jtl': 'text/plain',
|
|
67
|
+
'.csv': 'text/csv',
|
|
68
|
+
'.xml': 'application/xml',
|
|
69
|
+
};
|
|
70
|
+
// ─── Dashboard location ───────────────────────────────────────────────────────
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the path to the pre-built dashboard static files.
|
|
73
|
+
*
|
|
74
|
+
* Checks two locations (first match wins):
|
|
75
|
+
* 1. dist/dashboard/ — npm-published package (prepublishOnly copies dashboard/dist here)
|
|
76
|
+
* 2. dashboard/dist/ — repo clone with dashboard already built (make dashboard-build)
|
|
77
|
+
*/
|
|
78
|
+
function resolveDashboardDir() {
|
|
79
|
+
// dist/src/serveDashboard.js → ../dashboard → dist/dashboard/ (npm package)
|
|
80
|
+
const inDist = path.resolve(__dirname, '..', 'dashboard');
|
|
81
|
+
if (fs.existsSync(path.join(inDist, 'index.html'))) {
|
|
82
|
+
return inDist;
|
|
83
|
+
}
|
|
84
|
+
// Fallback for local development: dist/src/ → ../../dashboard/dist → dashboard/dist/
|
|
85
|
+
const inRepo = path.resolve(__dirname, '..', '..', 'dashboard', 'dist');
|
|
86
|
+
if (fs.existsSync(path.join(inRepo, 'index.html'))) {
|
|
87
|
+
return inRepo;
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`Dashboard build not found.\n` +
|
|
90
|
+
`Run: cd dashboard && npm run build\n` +
|
|
91
|
+
`Or: make dashboard-build`);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Start the dashboard HTTP server and return the server instance.
|
|
95
|
+
*
|
|
96
|
+
* The server:
|
|
97
|
+
* 1. Intercepts `/reports/*` → serves from `reportsDir`
|
|
98
|
+
* 2. Everything else → serves static files from `dashboard/dist/`
|
|
99
|
+
* 3. Unknown paths fall back to `index.html` (SPA client-side routing)
|
|
100
|
+
*/
|
|
101
|
+
function serveDashboard(options = {}) {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
const reportsDir = path.resolve((_a = options.reportsDir) !== null && _a !== void 0 ? _a : 'reports');
|
|
104
|
+
const port = (_b = options.port) !== null && _b !== void 0 ? _b : 4000;
|
|
105
|
+
const dashboardDir = resolveDashboardDir();
|
|
106
|
+
const server = http.createServer((req, res) => {
|
|
107
|
+
var _a, _b, _c, _d;
|
|
108
|
+
const parsedUrl = url.parse((_a = req.url) !== null && _a !== void 0 ? _a : '/');
|
|
109
|
+
let pathname = (_b = parsedUrl.pathname) !== null && _b !== void 0 ? _b : '/';
|
|
110
|
+
// ── 1. Serve /reports/* from the actual reports directory ──────────────
|
|
111
|
+
if (pathname.startsWith('/reports/')) {
|
|
112
|
+
const relative = pathname.slice('/reports/'.length);
|
|
113
|
+
const reportPath = path.join(reportsDir, relative);
|
|
114
|
+
// Security: prevent path traversal
|
|
115
|
+
if (!reportPath.startsWith(reportsDir + path.sep) && reportPath !== reportsDir) {
|
|
116
|
+
res.writeHead(403);
|
|
117
|
+
res.end('Forbidden');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const content = fs.readFileSync(reportPath);
|
|
122
|
+
const mimeType = (_c = MIME_TYPES[path.extname(reportPath)]) !== null && _c !== void 0 ? _c : 'application/octet-stream';
|
|
123
|
+
res.writeHead(200, {
|
|
124
|
+
'Content-Type': mimeType,
|
|
125
|
+
'Cache-Control': 'no-cache',
|
|
126
|
+
'Access-Control-Allow-Origin': '*',
|
|
127
|
+
});
|
|
128
|
+
res.end(content);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
132
|
+
res.end(JSON.stringify({ error: `Report not found: ${relative}` }));
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// ── 2. Serve static dashboard assets ───────────────────────────────────
|
|
137
|
+
if (pathname === '/')
|
|
138
|
+
pathname = '/index.html';
|
|
139
|
+
const filePath = path.join(dashboardDir, pathname);
|
|
140
|
+
// Security: prevent escaping dashboard dir
|
|
141
|
+
if (!filePath.startsWith(dashboardDir)) {
|
|
142
|
+
res.writeHead(403);
|
|
143
|
+
res.end('Forbidden');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const content = fs.readFileSync(filePath);
|
|
148
|
+
const mimeType = (_d = MIME_TYPES[path.extname(filePath)]) !== null && _d !== void 0 ? _d : 'text/plain';
|
|
149
|
+
res.writeHead(200, {
|
|
150
|
+
'Content-Type': mimeType,
|
|
151
|
+
'Cache-Control': path.extname(filePath) === '.html' ? 'no-cache' : 'max-age=86400',
|
|
152
|
+
});
|
|
153
|
+
res.end(content);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// ── 3. SPA fallback — serve index.html for all unknown paths ───────
|
|
157
|
+
try {
|
|
158
|
+
const indexContent = fs.readFileSync(path.join(dashboardDir, 'index.html'));
|
|
159
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' });
|
|
160
|
+
res.end(indexContent);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
164
|
+
res.end('Dashboard build missing. Run: cd dashboard && npm run build');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
server.listen(port, () => {
|
|
169
|
+
const serverUrl = `http://localhost:${port}`;
|
|
170
|
+
console.log(`\n=== API Coverage Dashboard ===`);
|
|
171
|
+
console.log(` URL: ${serverUrl}`);
|
|
172
|
+
console.log(` Reports dir: ${reportsDir}`);
|
|
173
|
+
console.log(`\nPress Ctrl+C to stop.\n`);
|
|
174
|
+
if (options.open) {
|
|
175
|
+
openBrowser(serverUrl);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return server;
|
|
179
|
+
}
|
|
180
|
+
// ─── Browser opener ───────────────────────────────────────────────────────────
|
|
181
|
+
function openBrowser(targetUrl) {
|
|
182
|
+
try {
|
|
183
|
+
const cmd = process.platform === 'darwin' ? `open "${targetUrl}"` :
|
|
184
|
+
process.platform === 'win32' ? `start "" "${targetUrl}"` :
|
|
185
|
+
`xdg-open "${targetUrl}"`;
|
|
186
|
+
(0, child_process_1.execSync)(cmd, { stdio: 'ignore' });
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Silently ignore — browser open is best-effort
|
|
190
|
+
}
|
|
191
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-tests-coverage",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "CLI and library to measure how thoroughly your test suite exercises your API surface area",
|
|
5
5
|
"main": "dist/src/lib/index.js",
|
|
6
6
|
"types": "dist/src/lib/index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/src/",
|
|
12
12
|
"dist/action/",
|
|
13
|
+
"dist/dashboard/",
|
|
13
14
|
"README.md",
|
|
14
15
|
"config.yaml.example"
|
|
15
16
|
],
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"license": "MIT",
|
|
36
37
|
"scripts": {
|
|
37
38
|
"build": "tsc",
|
|
38
|
-
"prepublishOnly": "npm run build",
|
|
39
|
+
"prepublishOnly": "npm run build && cd dashboard && npm run build && cd .. && cp -r dashboard/dist dist/dashboard",
|
|
39
40
|
"start": "node dist/src/index.js",
|
|
40
41
|
"test": "jest",
|
|
41
42
|
"coverage": "node scripts/run-coverage.js",
|