Skip to content

Commit ece9fa6

Browse files
d3xter666matz3KlattG
authored
feat(middleware-code-coverage): allow coverage watermarks configuration via frontend (#145)
This feature enables the extension of default or ui5.yaml's Istanbul configuration, so that certain settings could be adjusted from the frontend. JIRA: CPOUI5FOUNDATION-709 --------- Co-authored-by: Matthias Oßwald <[email protected]> Co-authored-by: Günter Klatt <[email protected]>
1 parent 1487d8c commit ece9fa6

File tree

4 files changed

+313
-167
lines changed

4 files changed

+313
-167
lines changed

packages/middleware-code-coverage/README.md

Lines changed: 20 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ npm install @ui5/middleware-code-coverage --save-dev
9393

9494
**New:**
9595

96-
```html title="unitTests.qunit.html"
96+
```diff html title="unitTests.qunit.html"
9797
<!DOCTYPE html>
9898
<html>
9999
<head>
@@ -111,7 +111,8 @@ npm install @ui5/middleware-code-coverage --save-dev
111111
112112
<script src="../../resources/sap/ui/thirdparty/qunit-2.js"></script>
113113
<script src="../../resources/sap/ui/qunit/qunit-junit.js"></script>
114-
<script src="../../resources/sap/ui/qunit/qunit-coverage-istanbul.js"
114+
- <script src="../../resources/sap/ui/qunit/qunit-coverage.js"
115+
+ <script src="../../resources/sap/ui/qunit/qunit-coverage-istanbul.js"
115116
data-sap-ui-cover-only="ui5/sample/"
116117
data-sap-ui-cover-never="ui5/sample/test/"></script>
117118
<script src="../../resources/sap/ui/thirdparty/sinon.js"></script>
@@ -170,135 +171,40 @@ Defaults to:
170171
}
171172
```
172173

173-
## How it works
174-
175-
The middleware adds an HTTP endpoint to the development server.
176-
177-
The custom middleware intercepts every `.js`-file before it is sent to the client. The file is then instrumented on the fly, including the dynamic creation of a `sourcemap`.
178-
179-
The instrumented code and the `sourcemap` are subsequently delivered to the client instead of the original `.js`-file.
174+
### Front-End Configuration
180175

181-
## API
176+
You can override [`watermarks`](https://github.com/istanbuljs/nyc/blob/ab7c53b2f340b458789a746dff2abd3e2e4790c3/README.md#high-and-low-watermarks) (since UI5 1.119.0) via data attributes in the script tag for `qunit-coverage-istanbul.js`':
182177

183-
This REST API is the underlying foundation of the middleware.
178+
```diff html title="unitTests.qunit.html"
179+
...
184180
185-
**Note:** The `/.ui5/` path is reserved for UI5 Core modules and must not be used for third-party modules.
181+
<script src="../../resources/sap/ui/qunit/qunit-coverage-istanbul.js"
182+
data-sap-ui-cover-only="ui5/sample/"
183+
data-sap-ui-cover-never="ui5/sample/test/"
184+
+ data-sap-ui-cover-watermarks-statements="[90,95]"
185+
+ data-sap-ui-cover-watermarks-functions="[90,95]"
186+
+ data-sap-ui-cover-watermarks-branches="[90,95]"
187+
+ data-sap-ui-cover-watermarks-lines="[90,95]"></script>
186188
187-
---
188-
### GET `{path/to/resource}?instrument=true`
189-
190-
A resource could be instrumented for code coverage by appending `?instrument=true` as a query parameter. **Note:** If a resource has already been excluded via `excludePatterns` in middleware's configuration, the query parameter is ignored.
191-
192-
**Example:**
193-
194-
```js
195-
// OpenUI5
196-
197-
GET /resources/sap/m/ComboBox.js?instrument=true
198-
GET /resources/sap/m/ComboBoxBase.js?instrument=true
199-
GET /resources/sap/m/ComboBoxBaseRenderer.js?instrument=true
200-
GET /resources/sap/m/ComboBoxRenderer.js?instrument=true
201-
GET /resources/sap/m/ComboBoxTextField.js?instrument=true
202-
GET /resources/sap/m/ComboBoxTextFieldRenderer.js?instrument=true
189+
...
203190
```
204191

205-
---
206-
207-
### GET `/.ui5/coverage/ping`
208-
209-
Healthcheck. Useful when checking for the middleware's existence.
210-
211-
**Example:**
212-
213-
```js
214-
fetch("/.ui5/coverage/ping", {
215-
method: "GET",
216-
});
217-
```
218-
219-
---
220-
221-
### POST `/.ui5/coverage/report`
192+
## How It Works
222193

223-
Sends `__coverage__` data to the middleware. A static report is generated with the provided data. Reports could be accessed via the `/.ui5/coverage/report/${reportType}` route. The available report types could be found [here](https://github.com/istanbuljs/istanbuljs/tree/73c25ce79f91010d1ff073aa6ff3fd01114f90db/packages/istanbul-reports/lib).
194+
The middleware adds an HTTP endpoint to the development server. For more information about the endpoints, see the [API document](./docs/API.md).
224195

225-
**Note:** Report types could be defined and limited via the middleware's configuration.
196+
The custom middleware intercepts every `.js`-file before it is sent to the client. The file is then instrumented on the fly, which includes the dynamic creation of a `sourcemap`.
226197

227-
**Example:**
228-
229-
```js
230-
fetch("/.ui5/coverage/report", {
231-
method: "POST",
232-
body: JSON.stringify(window.__coverage__),
233-
headers: {
234-
"Content-Type": "application/json",
235-
},
236-
});
237-
```
238-
239-
---
240-
241-
### GET `/.ui5/coverage/report/${reportType}`
242-
243-
Returns the generated report(s) from the last generation via the `/.ui5/coverage/report` route.
244-
245-
**Example:**
246-
247-
```js
248-
GET /.ui5/coverage/report/html
249-
GET /.ui5/coverage/report/lcov
250-
```
198+
The instrumented code and the `sourcemap` are subsequently delivered to the client instead of the original `.js`-file.
251199

252200
## Integration
253201

254-
The middleware is integrated into OpenUI5 out of the box, but it is not limited just to it. With the configuration and the public API, developers could set up the middleware to suit their projects' needs.
202+
The middleware is integrated into OpenUI5 out of the box, but you are not limited by this. With the [configuration](#configuration) and the [public API](./docs/API.md), you can set up the middleware to suit your projects' needs.
255203

256204
### OpenUI5 QUnit Integration
257205

258206
The `qunit-coverage-istanbul.js` (part of `sap.ui.core` library) file requests the instrumented source files by the middleware. While the tests are running, `qunit-coverage-istanbul.js` takes care of collecting and storing the coverage records into the `window.__coverage__` global variable. After the tests are executed, `qunit-coverage-istanbul.js` sends this data to the middleware, which then generates the code coverage report. Afterwards, the code coverage is displayed on the test page.
259207

260-
### Custom Integration
261-
262-
Below is an example of a sample scenario to integrate UI5 Middleware Code Coverage.
263-
264-
```js
265-
// A module in the browser
266-
267-
const isMiddlewareAvailable = await fetch("/.ui5/coverage/ping", {
268-
method: "GET",
269-
});
270-
271-
if (isMiddlewareAvailable) {
272-
273-
const generatedReports = await fetch("/.ui5/coverage/report", {
274-
method: "POST",
275-
body: JSON.stringify(window.__coverage__),
276-
headers: {
277-
"Content-Type": "application/json",
278-
},
279-
});
280-
281-
// Extract the html report from the list of reports
282-
const htmlReport = generatedReports.availableReports.find(
283-
(report) => report.report === "html"
284-
);
285-
286-
if (htmlReport) {
287-
288-
const body = document.body;
289-
const iFrameElem = document.createElement("iframe");
290-
291-
iFrameElem.src = "/.ui5/coverage/report/" + htmlReport.destination;
292-
iFrameElem.style.border = "none";
293-
iFrameElem.style.width = "100%";
294-
iFrameElem.style.height = "100vh";
295-
iFrameElem.sandbox = "allow-scripts";
296-
297-
body.appendChild(iFrameElem);
298-
299-
}
300-
}
301-
```
302208

303209
## Code of Conduct
304210

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# API
2+
3+
This REST API is the underlying foundation of the middleware.
4+
5+
**Note:** The `/.ui5/` path is reserved for UI5 Core modules and must not be used for third-party modules.
6+
7+
---
8+
### GET `{path/to/resource}?instrument=true`
9+
10+
A resource could be instrumented for code coverage by appending `?instrument=true` as a query parameter. **Note:** If a resource has already been excluded via `excludePatterns` in the middleware's configuration, the query parameter is ignored.
11+
12+
**Example:**
13+
14+
```js
15+
// OpenUI5
16+
17+
GET /resources/sap/m/ComboBox.js?instrument=true
18+
GET /resources/sap/m/ComboBoxBase.js?instrument=true
19+
GET /resources/sap/m/ComboBoxBaseRenderer.js?instrument=true
20+
GET /resources/sap/m/ComboBoxRenderer.js?instrument=true
21+
GET /resources/sap/m/ComboBoxTextField.js?instrument=true
22+
GET /resources/sap/m/ComboBoxTextFieldRenderer.js?instrument=true
23+
```
24+
25+
---
26+
27+
### GET `/.ui5/coverage/ping`
28+
29+
Health check. Useful when checking for the middleware's existence.
30+
31+
**Example:**
32+
33+
```js
34+
fetch("/.ui5/coverage/ping", {
35+
method: "GET",
36+
});
37+
```
38+
39+
---
40+
41+
### POST `/.ui5/coverage/report`
42+
43+
Sends `__coverage__` data to the middleware. A static report is generated with the data provided. Reports can be accessed via the `/.ui5/coverage/report/${reportType}` route. The available report types can be found [here](https://github.com/istanbuljs/istanbuljs/tree/73c25ce79f91010d1ff073aa6ff3fd01114f90db/packages/istanbul-reports/lib).
44+
45+
**Note:** Report types can be defined and limited via the middleware's configuration.
46+
47+
**Note:** You can also provide report settings from the front end via the request body. Currently, only [`watermarks`](https://github.com/istanbuljs/nyc/blob/ab7c53b2f340b458789a746dff2abd3e2e4790c3/README.md#high-and-low-watermarks) are supported (available since UI5 1.119.0). Front end-defined settings take precedence over default or `ui5.yaml`-configured ones. For their usage in OpenUI5 HTML test pages, see the [Front-End Configuration](../README.md#frontend-configuration) section of the main document.
48+
49+
**Example:**
50+
51+
```js
52+
fetch("/.ui5/coverage/report", {
53+
method: "POST",
54+
body: JSON.stringify({
55+
coverage: window.__coverage__,
56+
watermarks: { // Optional: report setting
57+
statements: [75, 90],
58+
functions: [75, 90],
59+
branches: [75, 90],
60+
lines: [75, 90]
61+
}
62+
}),
63+
headers: {
64+
"Content-Type": "application/json",
65+
},
66+
});
67+
```
68+
69+
---
70+
71+
### GET `/.ui5/coverage/report/${reportType}`
72+
73+
Returns the most recent report(s) via the `/.ui5/coverage/report` route.
74+
75+
**Example:**
76+
77+
```js
78+
GET /.ui5/coverage/report/html
79+
GET /.ui5/coverage/report/lcov
80+
```
81+
82+
## Custom Integration
83+
84+
Sample scenario to integrate UI5 Middleware Code Coverage:
85+
86+
```js
87+
// A module in the browser
88+
89+
const isMiddlewareAvailable = await fetch("/.ui5/coverage/ping", {
90+
method: "GET",
91+
});
92+
93+
if (isMiddlewareAvailable) {
94+
95+
const generatedReports = await fetch("/.ui5/coverage/report", {
96+
method: "POST",
97+
body: JSON.stringify({
98+
coverage: window.__coverage__,
99+
watermarks: { // Optional: report setting
100+
statements: [75, 90],
101+
functions: [75, 90],
102+
branches: [75, 90],
103+
lines: [75, 90]
104+
}
105+
}),
106+
headers: {
107+
"Content-Type": "application/json",
108+
},
109+
});
110+
111+
// Extract the html report from the list of reports
112+
const htmlReport = generatedReports.availableReports.find(
113+
(report) => report.report === "html"
114+
);
115+
116+
if (htmlReport) {
117+
118+
const body = document.body;
119+
const iFrameElem = document.createElement("iframe");
120+
121+
iFrameElem.src = "/.ui5/coverage/report/" + htmlReport.destination;
122+
iFrameElem.style.border = "none";
123+
iFrameElem.style.width = "100%";
124+
iFrameElem.style.height = "100vh";
125+
iFrameElem.sandbox = "allow-scripts";
126+
127+
body.appendChild(iFrameElem);
128+
129+
}
130+
}
131+
```

packages/middleware-code-coverage/lib/coverage-reporter.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import path from "node:path";
1212
/**
1313
* Reports the coverage
1414
*
15-
* @param {object} globalCoverageMap
15+
* @param {object} coverageData
1616
* @param {*} config
1717
* @param {object} resources Resource collections
1818
* @param {module:@ui5/fs.AbstractReader} resources.all Reader or Collection to read resources of the
@@ -25,10 +25,20 @@ import path from "node:path";
2525
* Logger instance of the custom middleware instance
2626
* @returns {@ui5/middleware-code-coverage/Coverage}
2727
*/
28-
export default async function(globalCoverageMap, config, resources, log) {
28+
export default async function(coverageData, config, resources, log) {
29+
let {coverage: globalCoverageMap, watermarks} = coverageData;
30+
31+
// For compatibility reasons with the old structure, we need first to check
32+
// whether the "coverage" property is present in coverageData or use the
33+
// whole coverageData object (old structure).
34+
globalCoverageMap = globalCoverageMap || coverageData;
35+
2936
const coverageMap =
3037
istanbulLibCoverage.createCoverageMap(globalCoverageMap);
31-
const {report: reportConfig} = config;
38+
const reportConfig = {...config.report};
39+
40+
// Frontend config for watermarks should take precedence if present.
41+
reportConfig.watermarks = {...reportConfig.watermarks, ...watermarks};
3242

3343
// Get & stash code from the resources
3444
// Later this would be needed to create the reports

0 commit comments

Comments
 (0)