ui5-test-runner 5.5.2 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -5
- package/src/batch.js +5 -1
- package/src/browsers.js +6 -2
- package/src/coverage.js +20 -10
- package/src/end.js +5 -1
- package/src/job.js +17 -0
- package/src/report.js +30 -8
- package/src/tests.js +0 -2
- package/src/ui5.js +28 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.1",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"test:coverall": "rimraf .nyc_output && jest --coverageDirectory .nyc_output --coverageReporters json && nyc --silent --no-clean npm run test:e2e && nyc merge .nyc_output .nyc_output/final/coverage.json && nyc report --temp-dir .nyc_output/final/ --report-dir coverage --branches 80 --functions 80 --lines 80 --statements 80",
|
|
18
18
|
"test:unit": "jest",
|
|
19
19
|
"test:unit:debug": "node --inspect node_modules/jest/bin/jest.js --runInBand --no-coverage",
|
|
20
|
-
"
|
|
20
|
+
"pretest:e2e": "npm install -g puppeteer selenium-webdriver playwright webdriverio jsdom",
|
|
21
|
+
"test:e2e": "node . --batch \"test/e2e/[\\w_]*\\.json\" --report-dir e2e --start \"node test/e2e/serve.js\" --start-wait-url http://localhost:8081 --start-wait-method HEAD --start-timeout 30s",
|
|
21
22
|
"test:report": "node ./src/defaults/report.js ./test/report && reserve --config ./test/report/reserve.json",
|
|
22
23
|
"test:text-report": "node ./src/defaults/text-report.js ./test/report",
|
|
23
24
|
"build:doc": "node build/doc",
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
"reserve": "2.1.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@openui5/types": "^1.
|
|
54
|
-
"@ui5/cli": "^4.0.
|
|
54
|
+
"@openui5/types": "^1.134.0",
|
|
55
|
+
"@ui5/cli": "^4.0.14",
|
|
55
56
|
"@ui5/middleware-code-coverage": "^2.0.1",
|
|
56
57
|
"dotenv": "^16.4.7",
|
|
57
58
|
"jest": "^29.7.0",
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
"rimraf": "^6.0.1",
|
|
61
62
|
"standard": "^17.1.2",
|
|
62
63
|
"typescript": "^5.8.2",
|
|
63
|
-
"ui5-tooling-transpile": "^3.7.
|
|
64
|
+
"ui5-tooling-transpile": "^3.7.3"
|
|
64
65
|
},
|
|
65
66
|
"optionalDependencies": {
|
|
66
67
|
"fsevents": "^2.3.3"
|
package/src/batch.js
CHANGED
|
@@ -76,7 +76,11 @@ const task = async function (batchItem) {
|
|
|
76
76
|
if (option) {
|
|
77
77
|
parameters.push(`--${longName}`)
|
|
78
78
|
if (!option.optional && !option.negate) {
|
|
79
|
-
if (
|
|
79
|
+
if (name === 'env') {
|
|
80
|
+
Object.keys(job.env).forEach(key => {
|
|
81
|
+
parameters.push(`${key}=${job.env[key]}`)
|
|
82
|
+
})
|
|
83
|
+
} else if (option.variadic) {
|
|
80
84
|
parameters.push(...job[name].map(value => value.toString()))
|
|
81
85
|
} else {
|
|
82
86
|
parameters.push(job[name].toString())
|
package/src/browsers.js
CHANGED
|
@@ -29,7 +29,11 @@ async function instantiate (job, config) {
|
|
|
29
29
|
const stdout = await open(stdoutFilename, 'w')
|
|
30
30
|
const stderr = await open(stderrFilename, 'w')
|
|
31
31
|
const childProcess = fork(job.browser, [browserConfigPath], {
|
|
32
|
-
stdio: [0, stdout, stderr, 'ipc']
|
|
32
|
+
stdio: [0, stdout, stderr, 'ipc'],
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
...job.env
|
|
36
|
+
}
|
|
33
37
|
})
|
|
34
38
|
const { promise, resolve } = allocPromise()
|
|
35
39
|
childProcess.on('close', async code => {
|
|
@@ -127,7 +131,7 @@ async function start (job, url, scripts = []) {
|
|
|
127
131
|
}
|
|
128
132
|
if (resolvedScripts.length) {
|
|
129
133
|
resolvedScripts.unshift(`(function () {
|
|
130
|
-
window['ui5-test-runner/base-host'] = 'http
|
|
134
|
+
window['ui5-test-runner/base-host'] = 'http://${job.callbackHost}:${job.port}'
|
|
131
135
|
}())`)
|
|
132
136
|
}
|
|
133
137
|
const progress = newProgress(job, url)
|
package/src/coverage.js
CHANGED
|
@@ -30,16 +30,19 @@ async function nyc (job, ...args) {
|
|
|
30
30
|
output.nyc(...args)
|
|
31
31
|
const childProcess = fork(nycScript, args, { stdio: 'pipe' })
|
|
32
32
|
output.monitor(childProcess)
|
|
33
|
-
const { promise, resolve
|
|
34
|
-
childProcess.on('close',
|
|
35
|
-
if (code !== 0) {
|
|
36
|
-
reject(UTRError.NYC_FAILED(`Return code ${code}`))
|
|
37
|
-
}
|
|
38
|
-
resolve()
|
|
39
|
-
})
|
|
33
|
+
const { promise, resolve } = allocPromise()
|
|
34
|
+
childProcess.on('close', resolve)
|
|
40
35
|
return promise
|
|
41
36
|
}
|
|
42
37
|
|
|
38
|
+
async function safeNyc (job, ...args) {
|
|
39
|
+
const code = await nyc(job, ...args)
|
|
40
|
+
if (code !== 0) {
|
|
41
|
+
const [command] = args
|
|
42
|
+
throw UTRError.NYC_FAILED(`nyc ${command} failed with code ${code}`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
43
46
|
const globalContextSearch = 'var global=new Function("return this")();'
|
|
44
47
|
const globalContextReplace = 'var global=window.top;'
|
|
45
48
|
|
|
@@ -84,7 +87,7 @@ async function instrument (job) {
|
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
job.status = 'Instrumenting'
|
|
87
|
-
await
|
|
90
|
+
await safeNyc(job, 'instrument', job.webapp, join(job.coverageTempDir, 'instrumented'), '--nycrc-path', job[$nycSettingsPath])
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
function getUrlOrigin (job) {
|
|
@@ -250,7 +253,7 @@ async function generateCoverageReport (job) {
|
|
|
250
253
|
const coverageMergedDir = join(job.coverageTempDir, 'merged')
|
|
251
254
|
await createDir(coverageMergedDir)
|
|
252
255
|
const coverageFilename = join(coverageMergedDir, 'coverage.json')
|
|
253
|
-
await
|
|
256
|
+
await safeNyc(job, 'merge', job.coverageTempDir, coverageFilename)
|
|
254
257
|
if (job[$coverageRemote]) {
|
|
255
258
|
await checkAllSourcesAreAvailable(job, coverageFilename)
|
|
256
259
|
}
|
|
@@ -271,13 +274,20 @@ async function generateCoverageReport (job) {
|
|
|
271
274
|
'--check-coverage'
|
|
272
275
|
)
|
|
273
276
|
}
|
|
274
|
-
await nyc(job, 'report', ...reporters, ...checks, '--temp-dir', coverageMergedDir, '--report-dir', job.coverageReportDir, '--nycrc-path', job[$nycSettingsPath])
|
|
277
|
+
const returnCode = await nyc(job, 'report', ...reporters, ...checks, '--temp-dir', coverageMergedDir, '--report-dir', job.coverageReportDir, '--nycrc-path', job[$nycSettingsPath])
|
|
275
278
|
if (checks.length) {
|
|
276
279
|
// The checks are not triggered if the coverage is empty
|
|
277
280
|
const lcov = await stat(join(job.coverageReportDir, 'lcov.info'))
|
|
278
281
|
if (lcov.size === 0) {
|
|
279
282
|
throw UTRError.NYC_FAILED('No coverage information extracted')
|
|
280
283
|
}
|
|
284
|
+
if (returnCode === 1) {
|
|
285
|
+
// Assuming coverage report shows the error
|
|
286
|
+
output.debug('coverage', `nyc report failed with code ${returnCode}`)
|
|
287
|
+
job.failed = true
|
|
288
|
+
}
|
|
289
|
+
} else if (returnCode !== 0) {
|
|
290
|
+
throw UTRError.NYC_FAILED(`nyc report failed with code ${returnCode}`)
|
|
281
291
|
}
|
|
282
292
|
}
|
|
283
293
|
|
package/src/end.js
CHANGED
package/src/job.js
CHANGED
|
@@ -79,6 +79,7 @@ function getCommand (cwd) {
|
|
|
79
79
|
|
|
80
80
|
const DEBUG_OPTION = '(🐞 for debugging purpose)'
|
|
81
81
|
const EXPERIMENTAL_OPTION = '[⚠️ experimental]'
|
|
82
|
+
const DANGEROURS_OPTION = '[💣 use carefully]'
|
|
82
83
|
|
|
83
84
|
command
|
|
84
85
|
.name(name)
|
|
@@ -110,6 +111,8 @@ function getCommand (cwd) {
|
|
|
110
111
|
.option('-br, --browser-retry <count>', '[💻🔗🧪📡] Browser instantiation retries : if the command fails unexpectedly, it is re-executed (0 means no retry)', 1)
|
|
111
112
|
.option('-oi, --output-interval <interval>', '[💻🔗🧪📡] Interval for reporting progress on non interactive output (CI/CD) (0 means no output)', timeout, 30000)
|
|
112
113
|
.option('--offline [flag]', '[💻🔗🧪📡] Limit network usage (implies --no-npm-install)', boolean, false)
|
|
114
|
+
.option('--env <name=value...>', '[💻🔗🧪📡] Set environment variable', arrayOf(string))
|
|
115
|
+
.option('--callback-host <host>', `[💻🔗🧪📡] ${DANGEROURS_OPTION} Hostname for callbacks`, string, 'localhost')
|
|
113
116
|
|
|
114
117
|
// Common to legacy and url
|
|
115
118
|
.option('--webapp <path>', '[💻🔗] Base folder of the web application (relative to cwd)', 'webapp')
|
|
@@ -299,6 +302,20 @@ function finalize (job) {
|
|
|
299
302
|
})
|
|
300
303
|
}
|
|
301
304
|
|
|
305
|
+
if (!job.env) {
|
|
306
|
+
job.env = {}
|
|
307
|
+
} else {
|
|
308
|
+
job.env = job.env.reduce((dictionary, env) => {
|
|
309
|
+
const equalPos = env.indexOf('=')
|
|
310
|
+
if (equalPos === -1) {
|
|
311
|
+
dictionary[env] = ''
|
|
312
|
+
} else {
|
|
313
|
+
dictionary[env.slice(0, equalPos)] = env.slice(equalPos + 1)
|
|
314
|
+
}
|
|
315
|
+
return dictionary
|
|
316
|
+
}, {})
|
|
317
|
+
}
|
|
318
|
+
|
|
302
319
|
if (job.watchFolder) {
|
|
303
320
|
job.watch = true
|
|
304
321
|
job.watchFolder = updateToAbsolute(job.watchFolder)
|
package/src/report.js
CHANGED
|
@@ -22,7 +22,17 @@ async function save (job) {
|
|
|
22
22
|
|
|
23
23
|
function generateTextReport (job) {
|
|
24
24
|
const { promise, resolve } = allocPromise()
|
|
25
|
-
const childProcess = fork(
|
|
25
|
+
const childProcess = fork(
|
|
26
|
+
join(__dirname, 'defaults/text-report.js'),
|
|
27
|
+
[job.reportDir, process.stdout.columns || ''],
|
|
28
|
+
{
|
|
29
|
+
stdio: 'pipe',
|
|
30
|
+
env: {
|
|
31
|
+
...process.env,
|
|
32
|
+
...job.env
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
)
|
|
26
36
|
getOutput(job).monitor(childProcess, true)
|
|
27
37
|
childProcess.on('close', resolve)
|
|
28
38
|
return promise
|
|
@@ -33,15 +43,31 @@ module.exports = {
|
|
|
33
43
|
|
|
34
44
|
async generate (job) {
|
|
35
45
|
const output = getOutput(job)
|
|
46
|
+
try {
|
|
47
|
+
await generateCoverageReport(job)
|
|
48
|
+
} catch (e) {
|
|
49
|
+
output.genericError(e)
|
|
50
|
+
job.failed = true
|
|
51
|
+
}
|
|
52
|
+
job.status = 'Generating reports'
|
|
36
53
|
job.end = new Date()
|
|
37
54
|
job.failed = !!job.failed
|
|
38
|
-
job.status = 'Generating reports'
|
|
39
55
|
job.testPageHashes = job.testPageUrls.map(url => filename(url))
|
|
40
56
|
await save(job)
|
|
41
57
|
await generateTextReport(job)
|
|
42
58
|
const promises = job.reportGenerator.map(generator => {
|
|
43
59
|
const { promise, resolve } = allocPromise()
|
|
44
|
-
const childProcess = fork(
|
|
60
|
+
const childProcess = fork(
|
|
61
|
+
generator,
|
|
62
|
+
[job.reportDir],
|
|
63
|
+
{
|
|
64
|
+
stdio: 'pipe',
|
|
65
|
+
env: {
|
|
66
|
+
...process.env,
|
|
67
|
+
...job.env
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
)
|
|
45
71
|
const buffers = output.monitor(childProcess, false)
|
|
46
72
|
childProcess.on('close', exitCode => {
|
|
47
73
|
if (exitCode !== 0) {
|
|
@@ -51,11 +77,7 @@ module.exports = {
|
|
|
51
77
|
})
|
|
52
78
|
return promise
|
|
53
79
|
})
|
|
54
|
-
promises.push(generateCoverageReport(job).catch(e => {
|
|
55
|
-
output.genericError(e)
|
|
56
|
-
job.failed = true
|
|
57
|
-
}))
|
|
58
80
|
await Promise.all(promises)
|
|
59
|
-
job.status = 'Reports
|
|
81
|
+
job.status = 'Reports generated'
|
|
60
82
|
}
|
|
61
83
|
}
|
package/src/tests.js
CHANGED
package/src/ui5.js
CHANGED
|
@@ -8,6 +8,7 @@ const { getOutput, newProgress } = require('./output')
|
|
|
8
8
|
const { download } = require('./tools')
|
|
9
9
|
const { $statusProgressCount, $statusProgressTotal } = require('./symbols')
|
|
10
10
|
const { parallelize } = require('./parallelize')
|
|
11
|
+
const { relative: relativePath } = require('path')
|
|
11
12
|
|
|
12
13
|
const buildCacheBase = job => {
|
|
13
14
|
const [, hostName] = /https?:\/\/([^/]*)/.exec(job.ui5)
|
|
@@ -128,27 +129,37 @@ module.exports = {
|
|
|
128
129
|
'ignore-unverifiable-certificate': true
|
|
129
130
|
}]
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
for (let { relative, source } of job.libs) {
|
|
132
133
|
if (source.endsWith('/') || source.endsWith('\\')) {
|
|
133
134
|
source = source.substring(0, source.length - 1)
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
136
|
+
const relativeUrl = relative.replace(/\//g, '\\/')
|
|
137
|
+
if (source.startsWith(job.webapp)) {
|
|
138
|
+
const relativeAbsoluteUrl = relativePath(job.webapp, source).replace(/\\/g, '/')
|
|
139
|
+
getOutput(job).debug('libs', `${relative} maps to webapp sub directory, use internal redirection to ${relativeAbsoluteUrl}`)
|
|
140
|
+
mappings.unshift({
|
|
141
|
+
match: new RegExp(`\\/resources\\/${relativeUrl}(.*)`),
|
|
142
|
+
custom: (request, response, $1) => `${relativeAbsoluteUrl}${$1}`
|
|
143
|
+
})
|
|
144
|
+
} else {
|
|
145
|
+
mappings.unshift({
|
|
146
|
+
match: new RegExp(`\\/resources\\/${relativeUrl}(.*)`),
|
|
147
|
+
cwd: source,
|
|
148
|
+
file: '$1',
|
|
149
|
+
static: !job.watch && !job.debugDevMode
|
|
150
|
+
}, {
|
|
151
|
+
match: new RegExp(`\\/resources\\/${relativeUrl}(.*)`),
|
|
152
|
+
custom: (request, response, $1) => {
|
|
153
|
+
if ($1 === undefined) {
|
|
154
|
+
getOutput(job).debug('libs', `Unable to map ${relative} : $1 is undefined`)
|
|
155
|
+
} else {
|
|
156
|
+
getOutput(job).debug('libs', `Unable to map ${relative}/${$1} to ${join(source, $1)}`)
|
|
157
|
+
}
|
|
158
|
+
return 404
|
|
147
159
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
152
163
|
|
|
153
164
|
return mappings
|
|
154
165
|
}
|