@teambit/defender.ui.test-page 0.0.56 → 0.0.59

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.
@@ -40,7 +40,117 @@ const lanes_hooks_use_viewed_lane_from_url_1 = require("@teambit/lanes.hooks.use
40
40
  const classnames_1 = __importDefault(require("classnames"));
41
41
  const react_1 = __importStar(require("react"));
42
42
  const defender_ui_test_table_1 = require("@teambit/defender.ui.test-table");
43
+ const design_content_table_1 = require("@teambit/design.content.table");
44
+ const base_react_navigation_link_1 = require("@teambit/base-react.navigation.link");
43
45
  const tests_page_module_scss_1 = __importDefault(require("./tests-page.module.scss"));
46
+ /**
47
+ * Displays the total row with color-coded values
48
+ * 0-25 - red
49
+ * 26-50 - orange
50
+ * 51-75 - yellow
51
+ * 76-100 - green
52
+ */
53
+ const getColor = (pct) => {
54
+ if (pct < 25)
55
+ return 'var(--negative-color)';
56
+ if (pct < 50)
57
+ return 'var(--warning-color)';
58
+ if (pct < 75)
59
+ return '#EEB90F';
60
+ return 'var(--positive-color)';
61
+ };
62
+ const StyledRow = ({ row, type, displayValue }) => {
63
+ if (!row)
64
+ return null;
65
+ const data = row.data[type];
66
+ if (!data) {
67
+ return null;
68
+ }
69
+ return (react_1.default.createElement("span", { style: { color: getColor(data.pct) } }, displayValue(data)));
70
+ };
71
+ const StyledTotalRow = (props) => (react_1.default.createElement(StyledRow, Object.assign({}, props, { displayValue: data => `${data.covered}/${data.total}` })));
72
+ const StyledPctRow = (props) => (react_1.default.createElement(StyledRow, Object.assign({}, props, { displayValue: data => `${data.pct}%` })));
73
+ const createColumn = (id, header, type) => [
74
+ {
75
+ id: `${id}_pct`,
76
+ header: '%',
77
+ cell: ({ row }) => (row ? react_1.default.createElement(StyledPctRow, { row: row, type: type }) : null),
78
+ value: (row) => { var _a; return (_a = row === null || row === void 0 ? void 0 : row.data[type].pct) !== null && _a !== void 0 ? _a : 0; },
79
+ className: {
80
+ td: tests_page_module_scss_1.default.coverage_column,
81
+ th: tests_page_module_scss_1.default.coverage_column,
82
+ }
83
+ },
84
+ {
85
+ id: `${id}_total`,
86
+ header: 'Total',
87
+ cell: ({ row }) => (row ? react_1.default.createElement(StyledTotalRow, { row: row, type: type }) : null),
88
+ value: (row) => { var _a; return (_a = row === null || row === void 0 ? void 0 : row.data[type].covered) !== null && _a !== void 0 ? _a : 0; },
89
+ className: {
90
+ td: tests_page_module_scss_1.default.coverage_column,
91
+ th: tests_page_module_scss_1.default.coverage_column,
92
+ }
93
+ },
94
+ ];
95
+ const calculatePercentage = (data) => {
96
+ const covered = data.lines.covered
97
+ + data.branches.covered
98
+ + data.functions.covered
99
+ + data.statements.covered;
100
+ const total = data.lines.total
101
+ + data.branches.total
102
+ + data.functions.total
103
+ + data.statements.total;
104
+ return (covered / total) * 100;
105
+ };
106
+ const columns = [
107
+ {
108
+ id: 'path',
109
+ header: 'File',
110
+ cell: ({ row }) => (react_1.default.createElement("div", { className: tests_page_module_scss_1.default.filePath },
111
+ react_1.default.createElement(base_react_navigation_link_1.Link, { href: `../~code/${row === null || row === void 0 ? void 0 : row.path}${document.location.search}` }, row === null || row === void 0 ? void 0 : row.path)))
112
+ },
113
+ {
114
+ id: 'progress',
115
+ header: 'Overall',
116
+ cell: ({ row }) => {
117
+ if (!row) {
118
+ return null;
119
+ }
120
+ const coveredPercentage = calculatePercentage(row === null || row === void 0 ? void 0 : row.data);
121
+ return (react_1.default.createElement("div", { className: tests_page_module_scss_1.default.summaryProgressBar },
122
+ react_1.default.createElement("div", { className: tests_page_module_scss_1.default.progressBarFill, style: {
123
+ width: `${coveredPercentage}%`,
124
+ backgroundColor: getColor(coveredPercentage)
125
+ } })));
126
+ },
127
+ value: (file) => { var _a; return (_a = file === null || file === void 0 ? void 0 : file.data.lines.pct) !== null && _a !== void 0 ? _a : 0; },
128
+ className: {
129
+ td: tests_page_module_scss_1.default.coverage_column,
130
+ th: tests_page_module_scss_1.default.coverage_column,
131
+ }
132
+ },
133
+ {
134
+ id: 'lines',
135
+ header: 'Lines',
136
+ columns: createColumn('lines', 'Lines', 'lines')
137
+ },
138
+ {
139
+ id: 'functions',
140
+ header: 'Functions',
141
+ columns: createColumn('functions', 'Functions', 'functions')
142
+ },
143
+ {
144
+ id: 'statements',
145
+ header: 'Statements',
146
+ columns: createColumn('statements', 'Statements', 'statements')
147
+ },
148
+ {
149
+ id: 'branches',
150
+ header: 'Branches',
151
+ columns: createColumn('branches', 'Branches', 'branches')
152
+ },
153
+ ];
44
154
  const TESTS_SUBSCRIPTION_CHANGED = (0, client_1.gql) `
45
155
  subscription OnTestsChanged($id: String!) {
46
156
  testsChanged(id: $id) {
@@ -86,13 +196,87 @@ const GET_COMPONENT = (0, client_1.gql) `
86
196
  error
87
197
  }
88
198
  }
199
+ coverage {
200
+ total {
201
+ lines {
202
+ total
203
+ covered
204
+ pct
205
+ }
206
+ functions {
207
+ total
208
+ covered
209
+ pct
210
+ }
211
+ statements {
212
+ total
213
+ covered
214
+ pct
215
+ }
216
+ branches {
217
+ total
218
+ covered
219
+ pct
220
+ }
221
+ }
222
+ files {
223
+ path
224
+ data {
225
+ lines {
226
+ total
227
+ covered
228
+ pct
229
+ }
230
+ functions {
231
+ total
232
+ covered
233
+ pct
234
+ }
235
+ statements {
236
+ total
237
+ covered
238
+ pct
239
+ }
240
+ branches {
241
+ total
242
+ covered
243
+ pct
244
+ }
245
+ }
246
+ }
247
+ }
89
248
  }
90
249
  }
91
250
  }
92
251
  }
93
252
  `;
253
+ const TotalCoverageSummary = ({ coverageResult }) => {
254
+ const { lines, statements, functions, branches } = coverageResult.total;
255
+ const data = [
256
+ { label: "Statements", value: `${statements.covered}/${statements.total}`, pct: statements.pct },
257
+ { label: "Branches", value: `${branches.covered}/${branches.total}`, pct: branches.pct },
258
+ { label: "Functions", value: `${functions.covered}/${functions.total}`, pct: functions.pct },
259
+ { label: "Lines", value: `${lines.covered}/${lines.total}`, pct: lines.pct },
260
+ ];
261
+ const totalCovered = lines.covered + branches.covered + functions.covered + statements.covered;
262
+ const totalLines = lines.total + branches.total + functions.total + statements.total;
263
+ return (react_1.default.createElement("div", { style: { display: "flex", flexDirection: "column", marginBottom: "20px" } },
264
+ react_1.default.createElement("div", { className: tests_page_module_scss_1.default.container }, data.map((item) => (react_1.default.createElement("div", { key: item.label, className: tests_page_module_scss_1.default.item },
265
+ react_1.default.createElement("span", { className: tests_page_module_scss_1.default.percentage, style: {
266
+ color: getColor(item.pct)
267
+ } },
268
+ item.pct,
269
+ "%"),
270
+ react_1.default.createElement("span", { className: tests_page_module_scss_1.default.label }, item.label),
271
+ react_1.default.createElement("span", { className: tests_page_module_scss_1.default.badge }, item.value))))),
272
+ react_1.default.createElement("div", { className: tests_page_module_scss_1.default.summaryProgressBar },
273
+ react_1.default.createElement("div", { className: tests_page_module_scss_1.default.progressBarFill, style: {
274
+ width: `${totalCovered / totalLines * 100}%`,
275
+ backgroundColor: getColor(totalCovered / totalLines * 100)
276
+ } }))));
277
+ };
94
278
  function TestsPage({ className, emptyState }) {
95
- var _a, _b, _c, _d;
279
+ var _a, _b, _c, _d, _e;
96
280
  const query = (0, ui_foundation_ui_react_router_use_query_1.useQuery)();
97
281
  const component = (0, react_1.useContext)(component_1.ComponentContext);
98
282
  const viewedLaneFromUrl = (0, lanes_hooks_use_viewed_lane_from_url_1.useViewedLaneFromUrl)();
@@ -103,15 +287,16 @@ function TestsPage({ className, emptyState }) {
103
287
  const onTestsChanged = (0, client_1.useSubscription)(TESTS_SUBSCRIPTION_CHANGED, {
104
288
  variables: { id },
105
289
  });
106
- const { data } = (0, client_1.useQuery)(GET_COMPONENT, {
290
+ const { data, loading } = (0, client_1.useQuery)(GET_COMPONENT, {
107
291
  variables: { id },
108
292
  });
109
293
  const testData = ((_a = onTestsChanged.data) === null || _a === void 0 ? void 0 : _a.testsChanged) || ((_b = data === null || data === void 0 ? void 0 : data.getHost) === null || _b === void 0 ? void 0 : _b.getTests);
110
294
  const testResults = (_c = testData === null || testData === void 0 ? void 0 : testData.testsResults) === null || _c === void 0 ? void 0 : _c.testFiles;
295
+ const testCoverage = (_d = testData === null || testData === void 0 ? void 0 : testData.testsResults) === null || _d === void 0 ? void 0 : _d.coverage;
111
296
  // TODO: change loading EmptyBox
112
- if (testData === null || testData === void 0 ? void 0 : testData.loading)
297
+ if (loading || (testData === null || testData === void 0 ? void 0 : testData.loading))
113
298
  return react_1.default.createElement(defender_ui_test_loader_1.TestLoader, null);
114
- const env = (_d = component.environment) === null || _d === void 0 ? void 0 : _d.id;
299
+ const env = (_e = component.environment) === null || _e === void 0 ? void 0 : _e.id;
115
300
  const EmptyStateTemplate = emptyState.get(env || '');
116
301
  if ((testResults === null || (testData === null || testData === void 0 ? void 0 : testData.testsResults) === null) &&
117
302
  component.host === 'teambit.workspace/workspace' &&
@@ -132,6 +317,14 @@ function TestsPage({ className, emptyState }) {
132
317
  react_1.default.createElement("div", null,
133
318
  react_1.default.createElement(documenter_ui_heading_1.H1, { className: tests_page_module_scss_1.default.title }, "Tests"),
134
319
  react_1.default.createElement(design_ui_separator_1.Separator, { isPresentational: true, className: tests_page_module_scss_1.default.separator }),
135
- react_1.default.createElement(defender_ui_test_table_1.TestTable, { testResults: testResults, className: tests_page_module_scss_1.default.testBlock }))));
320
+ react_1.default.createElement(documenter_ui_heading_1.H2, { className: tests_page_module_scss_1.default.subtitle }, "Tests Results"),
321
+ react_1.default.createElement(defender_ui_test_table_1.TestTable, { testResults: testResults, className: tests_page_module_scss_1.default.testBlock }),
322
+ testCoverage && testCoverage.files.length > 0 && (react_1.default.createElement(react_1.default.Fragment, null,
323
+ react_1.default.createElement(design_ui_separator_1.Separator, { isPresentational: true, className: tests_page_module_scss_1.default.separator }),
324
+ react_1.default.createElement(documenter_ui_heading_1.H2, { className: tests_page_module_scss_1.default.subtitle }, "Coverage Report"),
325
+ react_1.default.createElement(TotalCoverageSummary, { coverageResult: testCoverage }),
326
+ react_1.default.createElement(design_content_table_1.Table, { data: testCoverage.files, columns: columns, sorting: {
327
+ enable: true,
328
+ } }))))));
136
329
  }
137
330
  //# sourceMappingURL=tests-page.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tests-page.js","sourceRoot":"","sources":["../tests-page.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tests-page.js","sourceRoot":"","sources":["../tests-page.tsx"],"names":[],"mappings":"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"}
@@ -10,6 +10,10 @@
10
10
  margin-bottom: 24px;
11
11
  font-size: var(--bit-h-xs, 26px);
12
12
  }
13
+ .subtitle {
14
+ margin-bottom: 24px;
15
+ font-size: var(--bit-h-xxs, 18px);
16
+ }
13
17
  .separator {
14
18
  margin-bottom: 41px;
15
19
  }
@@ -18,3 +22,74 @@
18
22
  .testBlock {
19
23
  margin-bottom: 50px;
20
24
  }
25
+
26
+ th {
27
+ font-weight: bold;
28
+ }
29
+
30
+ .container {
31
+ display: flex;
32
+ gap: 20px;
33
+ margin-bottom: 30px;
34
+ }
35
+
36
+ .item {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 6px;
40
+ }
41
+
42
+ .percentage {
43
+ font-weight: bolder;
44
+ }
45
+
46
+ .label {
47
+ color: #666;
48
+ }
49
+
50
+ .badge {
51
+ background-color: var(--surface-neutral-active-color, #eeeff2);
52
+ color: #666;
53
+ padding: 2px 6px;
54
+ border-radius: 12px;
55
+ font-size: 0.85em;
56
+ }
57
+
58
+ .coverage_column {
59
+ width: 75px;
60
+ }
61
+
62
+ .filePath {
63
+ font-family: monospace;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-items: center;
67
+ gap: 4px;
68
+ }
69
+
70
+ .progressBar {
71
+ width: 150px;
72
+ border-radius: 50px;
73
+ height: 6px;
74
+ display: inline-block;
75
+ margin-left: 10px;
76
+ background-color: lightgray;
77
+ overflow: hidden;
78
+ position: relative;
79
+ }
80
+
81
+ .progressBarFill {
82
+ height: 100%;
83
+ border-radius: 50px;
84
+ width: 0;
85
+ }
86
+
87
+ .summaryProgressBar {
88
+ width: 100%;
89
+ border-radius: 50px;
90
+ height: 6px;
91
+ display: inline-block;
92
+ background-color: lightgray;
93
+ overflow: hidden;
94
+ position: relative;
95
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@teambit/defender.ui.test-page",
3
- "version": "0.0.56",
3
+ "version": "0.0.59",
4
4
  "homepage": "https://bit.cloud/teambit/defender/ui/test-page",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "teambit.defender",
8
8
  "name": "ui/test-page",
9
- "version": "0.0.56"
9
+ "version": "0.0.59"
10
10
  },
11
11
  "dependencies": {
12
12
  "classnames": "2.2.6",
@@ -17,9 +17,10 @@
17
17
  "@teambit/design.ui.empty-box": "0.0.363",
18
18
  "@teambit/design.ui.separator": "0.0.354",
19
19
  "@teambit/documenter.ui.heading": "4.1.1",
20
+ "@teambit/design.content.table": "0.0.2",
20
21
  "@teambit/mdx.ui.mdx-layout": "1.0.11",
21
22
  "@teambit/ui-foundation.ui.react-router.use-query": "0.0.505",
22
- "@teambit/lanes.hooks.use-viewed-lane-from-url": "0.0.236"
23
+ "@teambit/lanes.hooks.use-viewed-lane-from-url": "0.0.237"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/classnames": "2.2.11",
@@ -34,7 +35,8 @@
34
35
  "peerDependencies": {
35
36
  "@apollo/client": "^3.6.0",
36
37
  "react": "^16.8.0 || ^17.0.0",
37
- "react-dom": "^16.8.0 || ^17.0.0"
38
+ "react-dom": "^16.8.0 || ^17.0.0",
39
+ "@teambit/base-react.navigation.link": "2.0.32"
38
40
  },
39
41
  "license": "Apache-2.0",
40
42
  "optionalDependencies": {},
@@ -10,6 +10,10 @@
10
10
  margin-bottom: 24px;
11
11
  font-size: var(--bit-h-xs, 26px);
12
12
  }
13
+ .subtitle {
14
+ margin-bottom: 24px;
15
+ font-size: var(--bit-h-xxs, 18px);
16
+ }
13
17
  .separator {
14
18
  margin-bottom: 41px;
15
19
  }
@@ -18,3 +22,74 @@
18
22
  .testBlock {
19
23
  margin-bottom: 50px;
20
24
  }
25
+
26
+ th {
27
+ font-weight: bold;
28
+ }
29
+
30
+ .container {
31
+ display: flex;
32
+ gap: 20px;
33
+ margin-bottom: 30px;
34
+ }
35
+
36
+ .item {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 6px;
40
+ }
41
+
42
+ .percentage {
43
+ font-weight: bolder;
44
+ }
45
+
46
+ .label {
47
+ color: #666;
48
+ }
49
+
50
+ .badge {
51
+ background-color: var(--surface-neutral-active-color, #eeeff2);
52
+ color: #666;
53
+ padding: 2px 6px;
54
+ border-radius: 12px;
55
+ font-size: 0.85em;
56
+ }
57
+
58
+ .coverage_column {
59
+ width: 75px;
60
+ }
61
+
62
+ .filePath {
63
+ font-family: monospace;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-items: center;
67
+ gap: 4px;
68
+ }
69
+
70
+ .progressBar {
71
+ width: 150px;
72
+ border-radius: 50px;
73
+ height: 6px;
74
+ display: inline-block;
75
+ margin-left: 10px;
76
+ background-color: lightgray;
77
+ overflow: hidden;
78
+ position: relative;
79
+ }
80
+
81
+ .progressBarFill {
82
+ height: 100%;
83
+ border-radius: 50px;
84
+ width: 0;
85
+ }
86
+
87
+ .summaryProgressBar {
88
+ width: 100%;
89
+ border-radius: 50px;
90
+ height: 6px;
91
+ display: inline-block;
92
+ background-color: lightgray;
93
+ overflow: hidden;
94
+ position: relative;
95
+ }
package/tests-page.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useQuery, useSubscription, gql } from '@apollo/client';
2
2
  import { ComponentContext } from '@teambit/component';
3
3
  import { useQuery as useRouterQuery } from '@teambit/ui-foundation.ui.react-router.use-query';
4
- import { H1 } from '@teambit/documenter.ui.heading';
4
+ import { H1, H2 } from '@teambit/documenter.ui.heading';
5
5
  import { Separator } from '@teambit/design.ui.separator';
6
6
  import { EmptyBox } from '@teambit/design.ui.empty-box';
7
7
  import { MDXLayout } from '@teambit/mdx.ui.mdx-layout';
@@ -12,8 +12,176 @@ import { useViewedLaneFromUrl } from '@teambit/lanes.hooks.use-viewed-lane-from-
12
12
  import classNames from 'classnames';
13
13
  import React, { HTMLAttributes, useContext } from 'react';
14
14
  import { TestTable } from '@teambit/defender.ui.test-table';
15
+ import { Table, type ColumnProps, type CellFunctionProps } from '@teambit/design.content.table';
16
+ import { Link } from '@teambit/base-react.navigation.link';
15
17
  import styles from './tests-page.module.scss';
16
18
 
19
+ type CoverageResults = {
20
+ files: CoverageFile[]
21
+ total: CoverageData
22
+ }
23
+
24
+ type CoverageStats = {
25
+ pct: number
26
+ total: number
27
+ covered: number
28
+ skipped: number
29
+ }
30
+
31
+ type CoverageFile = {
32
+ path: string
33
+ data: CoverageData
34
+ }
35
+
36
+ type CoverageData = {
37
+ lines: CoverageStats
38
+ statements: CoverageStats
39
+ functions: CoverageStats
40
+ branches: CoverageStats
41
+ }
42
+
43
+ /**
44
+ * Displays the total row with color-coded values
45
+ * 0-25 - red
46
+ * 26-50 - orange
47
+ * 51-75 - yellow
48
+ * 76-100 - green
49
+ */
50
+ const getColor = (pct: number) => {
51
+ if (pct < 25) return 'var(--negative-color)';
52
+ if (pct < 50) return 'var(--warning-color)';
53
+ if (pct < 75) return '#EEB90F';
54
+ return 'var(--positive-color)';
55
+ }
56
+
57
+ const StyledRow: React.FC<{
58
+ row: CoverageFile | undefined | null,
59
+ type: keyof CoverageFile['data'],
60
+ displayValue: (data: CoverageFile['data'][keyof CoverageFile['data']]) => string
61
+ }> = ({ row, type, displayValue }) => {
62
+ if (!row) return null;
63
+
64
+ const data = row.data[type];
65
+
66
+ if (!data) {
67
+ return null;
68
+ }
69
+
70
+ return (
71
+ <span style={{ color: getColor(data.pct) }}>
72
+ {displayValue(data)}
73
+ </span>
74
+ )
75
+ }
76
+
77
+ const StyledTotalRow = (props: { row: CoverageFile | undefined | null, type: keyof CoverageFile['data'] }) => (
78
+ <StyledRow {...props} displayValue={data => `${data.covered}/${data.total}`} />
79
+ );
80
+
81
+ const StyledPctRow = (props: { row: CoverageFile | undefined | null, type: keyof CoverageFile['data'] }) => (
82
+ <StyledRow {...props} displayValue={data => `${data.pct}%`} />
83
+ );
84
+
85
+ const createColumn = (id: string, header: string, type: keyof CoverageFile['data']) => [
86
+ {
87
+ id: `${id}_pct`,
88
+ header: '%',
89
+ cell: ({ row }: CellFunctionProps<CoverageFile>) => (
90
+ row ? <StyledPctRow row={row} type={type} /> : null
91
+ ),
92
+ value: (row?: CoverageFile) => row?.data[type].pct ?? 0,
93
+ className: {
94
+ td: styles.coverage_column,
95
+ th: styles.coverage_column,
96
+ }
97
+ },
98
+ {
99
+ id: `${id}_total`,
100
+ header: 'Total',
101
+ cell: ({ row }: CellFunctionProps<CoverageFile>) => (
102
+ row ? <StyledTotalRow row={row} type={type} /> : null
103
+ ),
104
+ value: (row?: CoverageFile) => row?.data[type].covered ?? 0,
105
+ className: {
106
+ td: styles.coverage_column,
107
+ th: styles.coverage_column,
108
+ }
109
+ },
110
+ ];
111
+
112
+ const calculatePercentage = (data: CoverageData) => {
113
+ const covered = data.lines.covered
114
+ + data.branches.covered
115
+ + data.functions.covered
116
+ + data.statements.covered;
117
+
118
+ const total = data.lines.total
119
+ + data.branches.total
120
+ + data.functions.total
121
+ + data.statements.total;
122
+ return (covered / total) * 100;
123
+ }
124
+
125
+ const columns: ColumnProps<CoverageFile>[] = [
126
+ {
127
+ id: 'path',
128
+ header: 'File',
129
+ cell: ({ row }) => (
130
+ <div className={styles.filePath}>
131
+ <Link href={`../~code/${row?.path}${document.location.search}`}>
132
+ {row?.path}
133
+ </Link>
134
+ </div>
135
+ )
136
+ },
137
+ {
138
+ id: 'progress',
139
+ header: 'Overall',
140
+ cell: ({ row }) => {
141
+ if (!row) {
142
+ return null;
143
+ }
144
+
145
+ const coveredPercentage = calculatePercentage(row?.data);
146
+
147
+ return (
148
+ <div className={styles.summaryProgressBar}>
149
+ <div className={styles.progressBarFill} style={{
150
+ width: `${coveredPercentage}%`,
151
+ backgroundColor: getColor(coveredPercentage)
152
+ }}
153
+ />
154
+ </div>
155
+ )
156
+ },
157
+ value: (file) => file?.data.lines.pct ?? 0,
158
+ className: {
159
+ td: styles.coverage_column,
160
+ th: styles.coverage_column,
161
+ }
162
+ },
163
+ {
164
+ id: 'lines',
165
+ header: 'Lines',
166
+ columns: createColumn('lines', 'Lines', 'lines')
167
+ },
168
+ {
169
+ id: 'functions',
170
+ header: 'Functions',
171
+ columns: createColumn('functions', 'Functions', 'functions')
172
+ },
173
+ {
174
+ id: 'statements',
175
+ header: 'Statements',
176
+ columns: createColumn('statements', 'Statements', 'statements')
177
+ },
178
+ {
179
+ id: 'branches',
180
+ header: 'Branches',
181
+ columns: createColumn('branches', 'Branches', 'branches')
182
+ },
183
+ ];
184
+
17
185
  const TESTS_SUBSCRIPTION_CHANGED = gql`
18
186
  subscription OnTestsChanged($id: String!) {
19
187
  testsChanged(id: $id) {
@@ -60,12 +228,111 @@ const GET_COMPONENT = gql`
60
228
  error
61
229
  }
62
230
  }
231
+ coverage {
232
+ total {
233
+ lines {
234
+ total
235
+ covered
236
+ pct
237
+ }
238
+ functions {
239
+ total
240
+ covered
241
+ pct
242
+ }
243
+ statements {
244
+ total
245
+ covered
246
+ pct
247
+ }
248
+ branches {
249
+ total
250
+ covered
251
+ pct
252
+ }
253
+ }
254
+ files {
255
+ path
256
+ data {
257
+ lines {
258
+ total
259
+ covered
260
+ pct
261
+ }
262
+ functions {
263
+ total
264
+ covered
265
+ pct
266
+ }
267
+ statements {
268
+ total
269
+ covered
270
+ pct
271
+ }
272
+ branches {
273
+ total
274
+ covered
275
+ pct
276
+ }
277
+ }
278
+ }
279
+ }
63
280
  }
64
281
  }
65
282
  }
66
283
  }
67
284
  `;
68
285
 
286
+ type TotalCoverageSummaryProps = {
287
+ coverageResult: CoverageResults;
288
+ };
289
+
290
+ const TotalCoverageSummary: React.FC<TotalCoverageSummaryProps> = ({ coverageResult }) => {
291
+ const { lines, statements, functions, branches } = coverageResult.total
292
+
293
+ const data = [
294
+ { label: "Statements", value: `${statements.covered}/${statements.total}`, pct: statements.pct },
295
+ { label: "Branches", value: `${branches.covered}/${branches.total}`, pct: branches.pct },
296
+ { label: "Functions", value: `${functions.covered}/${functions.total}`, pct: functions.pct },
297
+ { label: "Lines", value: `${lines.covered}/${lines.total}`, pct: lines.pct },
298
+ ]
299
+
300
+ const totalCovered = lines.covered + branches.covered + functions.covered + statements.covered;
301
+ const totalLines = lines.total + branches.total + functions.total + statements.total;
302
+
303
+ return (
304
+ <div style={{ display: "flex", flexDirection: "column", marginBottom: "20px" }}>
305
+ <div className={styles.container}>
306
+ {data.map((item) => (
307
+ <div key={item.label} className={styles.item}>
308
+ <span className={styles.percentage}
309
+ style={{
310
+ color: getColor(item.pct)
311
+ }}
312
+ >
313
+ {item.pct}%
314
+ </span>
315
+ <span className={styles.label}>
316
+ {item.label}
317
+ </span>
318
+ <span className={styles.badge}>
319
+ {item.value}
320
+ </span>
321
+ </div>
322
+ ))}
323
+ </div>
324
+ {/** Display a progress bar for the total */}
325
+ <div className={styles.summaryProgressBar}>
326
+ <div className={styles.progressBarFill} style={{
327
+ width: `${totalCovered / totalLines * 100}%`,
328
+ backgroundColor: getColor(totalCovered / totalLines * 100)
329
+ }}
330
+ />
331
+ </div>
332
+ </div>
333
+ )
334
+ }
335
+
69
336
  type TestsPageProps = {
70
337
  emptyState: EmptyStateSlot;
71
338
  } & HTMLAttributes<HTMLDivElement>;
@@ -86,15 +353,16 @@ export function TestsPage({ className, emptyState }: TestsPageProps) {
86
353
  variables: { id },
87
354
  });
88
355
 
89
- const { data } = useQuery(GET_COMPONENT, {
356
+ const { data, loading } = useQuery(GET_COMPONENT, {
90
357
  variables: { id },
91
358
  });
92
359
 
93
360
  const testData = onTestsChanged.data?.testsChanged || data?.getHost?.getTests;
94
361
  const testResults = testData?.testsResults?.testFiles;
362
+ const testCoverage = testData?.testsResults?.coverage as CoverageResults;
95
363
 
96
364
  // TODO: change loading EmptyBox
97
- if (testData?.loading) return <TestLoader />;
365
+ if (loading || testData?.loading) return <TestLoader />;
98
366
 
99
367
  const env = component.environment?.id;
100
368
  const EmptyStateTemplate = emptyState.get(env || '');
@@ -139,7 +407,22 @@ export function TestsPage({ className, emptyState }: TestsPageProps) {
139
407
  <div>
140
408
  <H1 className={styles.title}>Tests</H1>
141
409
  <Separator isPresentational className={styles.separator} />
410
+ <H2 className={styles.subtitle}>Tests Results</H2>
142
411
  <TestTable testResults={testResults} className={styles.testBlock} />
412
+ {testCoverage && testCoverage.files.length > 0 && (
413
+ <>
414
+ <Separator isPresentational className={styles.separator} />
415
+ <H2 className={styles.subtitle}>Coverage Report</H2>
416
+ <TotalCoverageSummary coverageResult={testCoverage} />
417
+ <Table<CoverageFile>
418
+ data={testCoverage.files}
419
+ columns={columns}
420
+ sorting={{
421
+ enable: true,
422
+ }}
423
+ />
424
+ </>
425
+ )}
143
426
  </div>
144
427
  </div>
145
428
  );