ui5-test-runner 5.12.0 → 5.13.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/index.js +3 -2
- package/package.json +8 -7
- package/src/defaults/json-report.js +36 -0
- package/src/start.js +67 -79
package/index.js
CHANGED
|
@@ -74,10 +74,11 @@ async function main () {
|
|
|
74
74
|
|
|
75
75
|
if (job.mode === 'batch') {
|
|
76
76
|
return await batch(job)
|
|
77
|
-
.finally(() => {
|
|
77
|
+
.finally(async () => {
|
|
78
78
|
if (startedCommand) {
|
|
79
|
-
|
|
79
|
+
await startedCommand.stop()
|
|
80
80
|
}
|
|
81
|
+
cleanHandles(job)
|
|
81
82
|
})
|
|
82
83
|
}
|
|
83
84
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.1",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"test:unit:debug": "node --inspect node_modules/jest/bin/jest.js --runInBand --no-coverage",
|
|
24
24
|
"pretest:e2e": "npm install -g puppeteer selenium-webdriver playwright webdriverio jsdom",
|
|
25
25
|
"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",
|
|
26
|
+
"serve:e2e": "node test/e2e/serve.js",
|
|
27
|
+
"test:e2e:legacy-only": "node . --batch \"test/e2e/JS_LEGACY_[\\w_]*\\.json\" --report-dir e2e --start serve:e2e --start-wait-url http://localhost:8081 --start-wait-method HEAD --start-timeout 30s --debug-verbose start handle --ci",
|
|
26
28
|
"test:report": "node ./src/defaults/report.js ./test/report && reserve --config ./test/report/reserve.json",
|
|
27
29
|
"test:text-report": "node ./src/defaults/text-report.js ./test/report",
|
|
28
30
|
"build:doc": "node build/doc",
|
|
@@ -51,17 +53,16 @@
|
|
|
51
53
|
"homepage": "https://github.com/ArnaudBuchholz/ui5-test-runner#readme",
|
|
52
54
|
"dependencies": {
|
|
53
55
|
"commander": "^12.1.0",
|
|
54
|
-
"pidtree": "^0.6.0",
|
|
55
56
|
"punybind": "^1.2.1",
|
|
56
57
|
"punyexpr": "1.2.0",
|
|
57
58
|
"reserve": "2.3.4"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
|
-
"@openui5/types": "^1.143.
|
|
61
|
-
"@semantic-release/npm": "^13.1.
|
|
62
|
-
"@ui5/cli": "^4.0.
|
|
61
|
+
"@openui5/types": "^1.143.1",
|
|
62
|
+
"@semantic-release/npm": "^13.1.3",
|
|
63
|
+
"@ui5/cli": "^4.0.38",
|
|
63
64
|
"@ui5/middleware-code-coverage": "^2.0.2",
|
|
64
|
-
"baseline-browser-mapping": "^2.
|
|
65
|
+
"baseline-browser-mapping": "^2.9.11",
|
|
65
66
|
"dotenv": "^16.5.0",
|
|
66
67
|
"jest": "^29.7.0",
|
|
67
68
|
"nock": "^14.0.10",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"semantic-release": "^25.0.2",
|
|
72
73
|
"standard": "^17.1.2",
|
|
73
74
|
"typescript": "^5.9.3",
|
|
74
|
-
"ui5-tooling-transpile": "^3.
|
|
75
|
+
"ui5-tooling-transpile": "^3.10.0"
|
|
75
76
|
},
|
|
76
77
|
"optionalDependencies": {
|
|
77
78
|
"fsevents": "^2.3.3"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join, isAbsolute } = require('path')
|
|
4
|
+
const { writeFile } = require('fs').promises
|
|
5
|
+
const [,, reportDir] = process.argv
|
|
6
|
+
const verbose = process.argv.includes('--verbose')
|
|
7
|
+
|
|
8
|
+
const log = verbose ? console.log : () => {}
|
|
9
|
+
|
|
10
|
+
log('🏗 Building JSON report...')
|
|
11
|
+
|
|
12
|
+
async function main () {
|
|
13
|
+
const jobPath = isAbsolute(reportDir) ? reportDir : join(process.cwd(), reportDir)
|
|
14
|
+
log('📦 job path :', jobPath)
|
|
15
|
+
const rawJob = require(join(jobPath, 'job.js'))
|
|
16
|
+
const cleanJob = JSON.parse(JSON.stringify(rawJob, (key, value) => {
|
|
17
|
+
if (value && value instanceof RegExp) {
|
|
18
|
+
return value.toString()
|
|
19
|
+
}
|
|
20
|
+
return value
|
|
21
|
+
}))
|
|
22
|
+
const json = JSON.stringify(cleanJob, null, 2)
|
|
23
|
+
log('📦 json :', json.length)
|
|
24
|
+
|
|
25
|
+
await writeFile(join(reportDir, 'report.json'), json)
|
|
26
|
+
log('✅ generated.')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
main()
|
|
30
|
+
.catch(reason => {
|
|
31
|
+
console.error(reason)
|
|
32
|
+
return -1
|
|
33
|
+
})
|
|
34
|
+
.then((code = 0) => {
|
|
35
|
+
process.exit(code)
|
|
36
|
+
})
|
package/src/start.js
CHANGED
|
@@ -1,58 +1,75 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const {
|
|
1
|
+
const { spawn } = require('child_process')
|
|
2
|
+
const { readFile, access, constants } = require('fs/promises')
|
|
3
3
|
const { join } = require('path')
|
|
4
4
|
const { getOutput } = require('./output')
|
|
5
|
-
const
|
|
5
|
+
const { platform } = require('os')
|
|
6
|
+
const { allocPromise } = require('./tools')
|
|
6
7
|
|
|
7
8
|
async function start (job) {
|
|
8
9
|
const { startWaitUrl: url, startWaitMethod: method } = job
|
|
9
|
-
|
|
10
|
+
const { startCommand: start } = job
|
|
10
11
|
const output = getOutput(job)
|
|
11
|
-
|
|
12
|
+
let [command, ...parameters] = start.split(' ')
|
|
12
13
|
|
|
13
14
|
job.status = 'Executing start command'
|
|
14
15
|
|
|
15
|
-
// check if
|
|
16
|
-
if (command
|
|
17
|
-
let [node] = process.argv
|
|
18
|
-
if (node.includes(' ')) {
|
|
19
|
-
node = `"${node}"`
|
|
20
|
-
}
|
|
21
|
-
output.debug('start', `Replacing node with ${node}`)
|
|
22
|
-
start = [node, ...parameters].join(' ')
|
|
23
|
-
} else {
|
|
24
|
-
// check if existing NPM script
|
|
16
|
+
// check if existing NPM script
|
|
17
|
+
if (command !== 'node' && parameters.length === 0) {
|
|
25
18
|
const packagePath = join(job.cwd, 'package.json')
|
|
26
19
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
await access(packagePath, constants.F_OK)
|
|
21
|
+
output.debug('start', 'Found package.json in cwd')
|
|
22
|
+
const packageFile = JSON.parse(await readFile(packagePath, 'utf-8'))
|
|
23
|
+
if (packageFile.scripts[command]) {
|
|
24
|
+
output.debug('start', 'Found matching script in package.json')
|
|
25
|
+
// grab npm-cli path
|
|
26
|
+
const { promise, resolve } = allocPromise()
|
|
27
|
+
const npmChildProcess = spawn('npm', {
|
|
28
|
+
shell: true,
|
|
29
|
+
encoding: 'utf8'
|
|
30
|
+
})
|
|
31
|
+
npmChildProcess.on('close', resolve)
|
|
32
|
+
const npmOutput = []
|
|
33
|
+
npmChildProcess.stdout.on('data', (data) => npmOutput.push(data.toString()))
|
|
34
|
+
await promise
|
|
35
|
+
const [, version, path] = /^npm@([^ ]+) (.*)$/gm.exec(npmOutput.join(''))
|
|
36
|
+
output.debug('start', `npm@${version} ${path}`)
|
|
37
|
+
parameters = [join(path, 'bin/npm-cli.js'), 'run', command]
|
|
38
|
+
command = 'node'
|
|
35
39
|
}
|
|
36
40
|
} catch (e) {
|
|
37
41
|
output.debug('start', 'Missing or invalid package.json in cwd', e)
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
if (command === 'node') {
|
|
46
|
+
const [node] = process.argv
|
|
47
|
+
output.debug('start', `Replacing node with ${node}`)
|
|
48
|
+
command = node
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
let startProcessExited = false
|
|
42
|
-
output.debug('start', '
|
|
43
|
-
const startProcess =
|
|
52
|
+
output.debug('start', 'Spawning', [command, ...parameters])
|
|
53
|
+
const startProcess = spawn(command, parameters, {
|
|
44
54
|
cwd: job.cwd,
|
|
45
|
-
windowsHide: true
|
|
55
|
+
windowsHide: true,
|
|
56
|
+
detached: true,
|
|
57
|
+
env: {
|
|
58
|
+
...process.env,
|
|
59
|
+
...job.env
|
|
60
|
+
}
|
|
46
61
|
})
|
|
47
62
|
startProcess.on('close', () => {
|
|
48
63
|
output.debug('start', 'Start command process exited')
|
|
49
64
|
startProcessExited = true
|
|
50
65
|
})
|
|
51
66
|
output.monitor(startProcess)
|
|
67
|
+
output.debug('start', `Spawned process id ${startProcess.pid}`)
|
|
52
68
|
|
|
53
69
|
job.status = 'Waiting for URL to be reachable'
|
|
54
70
|
|
|
55
71
|
const begin = Date.now()
|
|
72
|
+
let lastError
|
|
56
73
|
// eslint-disable-next-line no-unmodified-loop-condition
|
|
57
74
|
while (!startProcessExited && Date.now() - begin <= job.startTimeout) {
|
|
58
75
|
try {
|
|
@@ -62,7 +79,10 @@ async function start (job) {
|
|
|
62
79
|
break
|
|
63
80
|
}
|
|
64
81
|
} catch (e) {
|
|
65
|
-
|
|
82
|
+
if (e.toString() !== lastError) {
|
|
83
|
+
output.debug('start', url, e)
|
|
84
|
+
lastError = e.toString()
|
|
85
|
+
}
|
|
66
86
|
await new Promise(resolve => setTimeout(resolve, 250))
|
|
67
87
|
}
|
|
68
88
|
}
|
|
@@ -73,62 +93,30 @@ async function start (job) {
|
|
|
73
93
|
|
|
74
94
|
const stop = async () => {
|
|
75
95
|
job.status = 'Terminating start command'
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
if (platform() === 'win32') {
|
|
97
|
+
const killProcess = spawn('taskkill', ['/F', '/T', '/PID', startProcess.pid], {
|
|
98
|
+
windowsHide: true
|
|
99
|
+
})
|
|
100
|
+
const { promise, resolve } = allocPromise()
|
|
101
|
+
killProcess.on('close', resolve)
|
|
102
|
+
output.monitor(killProcess)
|
|
103
|
+
await promise
|
|
104
|
+
} else {
|
|
81
105
|
try {
|
|
82
|
-
|
|
83
|
-
} catch (
|
|
84
|
-
output.genericError(
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
output.debug('start', 'Child processes', JSON.stringify(childProcesses))
|
|
88
|
-
if (childProcesses.length === 0) {
|
|
89
|
-
try {
|
|
90
|
-
output.debug('start', 'Terminating start command')
|
|
91
|
-
process.kill(startProcess.pid, 'SIGKILL')
|
|
92
|
-
} catch (e) {
|
|
93
|
-
output.debug('start', 'Failed to terminate start command', startProcess.pid, ':', e)
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
const depth = {}
|
|
97
|
-
let deepest = 1
|
|
98
|
-
let deepless = childProcesses.length
|
|
99
|
-
while (deepless > 0) {
|
|
100
|
-
for (const { ppid, pid } of childProcesses) {
|
|
101
|
-
if (ppid === startProcess.pid) {
|
|
102
|
-
depth[pid] = 1
|
|
103
|
-
--deepless
|
|
104
|
-
} else {
|
|
105
|
-
const parentDepth = depth[ppid]
|
|
106
|
-
if (parentDepth !== undefined) {
|
|
107
|
-
depth[pid] = parentDepth + 1
|
|
108
|
-
deepest = Math.max(deepest, parentDepth + 1)
|
|
109
|
-
--deepless
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
output.debug('start', 'Child processes', JSON.stringify(depth), 'terminating', deepest)
|
|
115
|
-
for (const { pid } of childProcesses) {
|
|
116
|
-
if (depth[pid] === deepest) {
|
|
117
|
-
output.debug('start', 'Terminating start child process', pid)
|
|
118
|
-
try {
|
|
119
|
-
process.kill(pid, 'SIGKILL')
|
|
120
|
-
} catch (e) {
|
|
121
|
-
output.debug('start', 'Failed to terminate start child process', pid, ':', e)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
106
|
+
process.kill(-startProcess.pid)
|
|
107
|
+
} catch (error) {
|
|
108
|
+
output.genericError(error)
|
|
109
|
+
return
|
|
125
110
|
}
|
|
111
|
+
}
|
|
112
|
+
const begin = Date.now()
|
|
113
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
|
114
|
+
while (!startProcessExited && Date.now() - begin <= job.startTimeout) {
|
|
126
115
|
await new Promise(resolve => setTimeout(resolve, 250))
|
|
127
116
|
}
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
startProcess.kill()
|
|
117
|
+
if (startProcessExited) {
|
|
118
|
+
// Additional waiting time to release handles
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, 250))
|
|
132
120
|
}
|
|
133
121
|
}
|
|
134
122
|
|