• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / ui5-tooling-extensions / 12990171368

27 Jan 2025 01:32PM UTC coverage: 100.0%. First build
12990171368

Pull #358

github

web-flow
Merge 76ee63fdb into 9b50d198d
Pull Request #358: ci(github-actions): Bump coverallsapp/github-action from 2.3.4 to 2.3.6

51 of 51 branches covered (100.0%)

Branch coverage included in aggregate %.

106 of 106 relevant lines covered (100.0%)

6.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/packages/middleware-code-coverage/lib/util.js
1
import xml2js from "xml2js";
2
import {Buffer} from "node:buffer";
3
import {readFile} from "node:fs/promises";
4

5
/**
6
 * Returns the configuration for instrumenting the files
7
 *
8
 * @public
9
 * @param {object} configuration instrumentation configuration
10
 * @param {@ui5/fs/Resource[]} resources
11
 * @returns {Promise<object>} configuration
12
 */
13
export async function createInstrumentationConfig(configuration = {}, resources) {
13✔
14
        const excludedPatterns = resources ? await excludePatterns(resources) : [];
22✔
15

16
        const {instrument, report, ...generalConfig} = configuration;
22✔
17

18
        return {
22✔
19
                // General configuration
20
                cwd: "./",
21
                excludePatterns: excludedPatterns,
22
                // General config overwrites
23
                ...generalConfig,
24

25
                // Intrumenter configuration
26
                ...{instrument: createInstrumenterConfig(instrument)},
27

28
                // Reporter configuration
29
                ...{report: createReporterConfig(report)},
30
        };
31
}
32

33
/**
34
 * Returns the source map of the latest instrumented resource
35
 *
36
 * @public
37
 * @param {Instrumenter} instrumenter
38
 * @returns {string} sourceMap
39
 */
40
export function getLatestSourceMap(instrumenter) {
41
        const sourceMap = instrumenter.lastSourceMap();
4✔
42

43
        if (!sourceMap) {
4✔
44
                return "";
1✔
45
        }
46

47
        return (
3✔
48
                "\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," +
49
                Buffer.from(JSON.stringify(sourceMap), "utf8").toString("base64")
50
        );
51
}
52

53
/**
54
 * Checks whether a request to resource should be instrumented
55
 *
56
 * @public
57
 * @param {object} request
58
 * @param {object} config
59
 * @returns {boolean}
60
 */
61
export function shouldInstrumentResource(request, config) {
62
        return (
19✔
63
                request.path &&
65✔
64
                request.path.endsWith(".js") && // Only .js file requests
65
                !isFalsyValue(request.query.instrument) && // instrument only flagged files, ignore "falsy" values
66
                !(config && config.excludePatterns || []).some((pattern) => {
18✔
67
                        if (pattern instanceof RegExp) {
4✔
68
                                // The ones comming from .library files are regular expressions
69
                                return pattern.test(request.path);
2✔
70
                        } else {
71
                                return request.path.includes(pattern);
2✔
72
                        }
73
                })
74
        );
75
}
76

77
/**
78
 * Returns the configuration for the instrumenter
79
 *
80
 * @private
81
 * @param {object} configuration
82
 * @returns {object}
83
 */
84
function createInstrumenterConfig(configuration = {}) {
19✔
85
        const defaultValues = {
22✔
86
                produceSourceMap: true,
87
                coverageGlobalScope: "window.top",
88
                coverageGlobalScopeFunc: false,
89
        };
90

91
        return {...defaultValues, ...configuration};
22✔
92
}
93

94
/**
95
 * Returns the configuration for the reporting
96
 *
97
 * @private
98
 * @param {object} configuration Reporting configuration
99
 * @returns {object}
100
 */
101
function createReporterConfig(configuration = {}) {
20✔
102
        const defaultValues = {
22✔
103
                "reporter": ["html"],
104
                "report-dir": "./tmp/coverage-reports",
105
                "watermarks": {
106
                        statements: [50, 80],
107
                        functions: [50, 80],
108
                        branches: [50, 80],
109
                        lines: [50, 80],
110
                },
111
        };
112

113
        return {...defaultValues, ...configuration};
22✔
114
}
115

116
/**
117
 * Determines if given <code>value</code> is falsy
118
 *
119
 * @private
120
 * @param {any} value
121
 * @returns {boolean} True when <code>value</code> is falsy, false if not
122
 */
123
function isFalsyValue(value) {
124
        return [false, 0, undefined, null, "false", "0", "undefined", "null"].includes(value);
18✔
125
}
126

127
/**
128
 * Analyzes .library files in order to check for jscoverage exclusions
129
 *
130
 * Note: .library: version="2.0" -> slash notation, and missing is "dot notation".
131
 * Note: We might consider to move this utility into the @ui5/project
132
 *
133
 * @private
134
 * @param {@ui5/fs/Resource[]} resources
135
 * @returns {Promise<RegExp[]>} exclude patterns
136
 */
137
async function excludePatterns(resources) {
138
        const aExcludes = [];
13✔
139
        // Read excludes from .library files
140
        const aDotLibrary = await resources.byGlob(["/resources/**/.library"]);
13✔
141
        for (const oDotLibrary of aDotLibrary) {
13✔
142
                const content = await oDotLibrary.getString();
4✔
143
                const result = await xml2js.parseStringPromise(content);
4✔
144
                if (
4✔
145
                        !(
146
                                result &&
23✔
147
                                result.library &&
148
                                result.library.appData &&
149
                                result.library.appData[0] &&
150
                                result.library.appData[0].jscoverage &&
151
                                result.library.appData[0].jscoverage[0]
152
                        )
153
                ) {
154
                        continue;
1✔
155
                }
156

157
                const oCoverage = result.library.appData[0].jscoverage[0];
3✔
158
                if (oCoverage.exclude) {
3✔
159
                        for (let j = 0; j < oCoverage.exclude.length; j++) {
2✔
160
                                const oExclude = oCoverage.exclude[j];
5✔
161

162
                                // Excludes marked with 'external="true"' are intended for a library local
163
                                // instrumentation only and should be ignored in a multi-library scenario
164
                                if (oExclude.$.external === "true") {
5✔
165
                                        continue;
1✔
166
                                }
167

168
                                let sPattern = oExclude.$.name;
4✔
169

170
                                // normalize the pattern
171
                                sPattern = sPattern.replace(/\./g, "/");
4✔
172

173
                                if (sPattern[0] === "/") {
4✔
174
                                        sPattern = "**" + sPattern;
1✔
175
                                }
176
                                if (sPattern.endsWith("/") && !sPattern.endsWith("**/")) {
4✔
177
                                        sPattern = sPattern + "**";
1✔
178
                                }
179
                                if (sPattern.endsWith("**")) {
4✔
180
                                        sPattern = sPattern + "/*";
1✔
181
                                }
182

183
                                // quote characters that might have been used but have a special meaning in regular expressions
184
                                sPattern = sPattern
4✔
185
                                        .replaceAll("[", "\\[")
186
                                        .replaceAll("]", "\\]")
187
                                        .replaceAll("(", "\\(")
188
                                        .replaceAll(")", "\\)")
189
                                        .replaceAll(".", "\\.");
190
                                // our wildcard '*' means 'any name segment, but not multiple components'
191
                                sPattern = sPattern.replace(/\*/g, "[^/]*");
4✔
192
                                // our wildcard '**/' means 'any number of name segments'
193
                                sPattern = sPattern.replace(
4✔
194
                                        /\[\^\/\]\*\[\^\/\]\*\//g,
195
                                        "([^/]+[/])*"
196
                                );
197
                                sPattern = "(" + sPattern + ")";
4✔
198
                                // add the resources path to the pattern
199
                                sPattern = "/resources/(" + sPattern + ")(-dbg)?.js$";
4✔
200

201
                                aExcludes.push(new RegExp(sPattern));
4✔
202
                        }
203
                }
204
        }
205

206
        return aExcludes;
13✔
207
}
208

209
/**
210
 * Reads and parses the JSON file located on the given <code>filePath</code>
211
 *
212
 * @private
213
 * @param {string} filePath Path to JSON file
214
 * @returns {object} The object representation of the JSON file
215
 */
216
export async function readJsonFile(filePath) {
217
        const content = await readFile(filePath, "utf8");
5✔
218
        return JSON.parse(content);
5✔
219
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc