ui5-test-runner 5.3.7 → 5.4.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/README.md +1 -0
- package/index.js +53 -32
- package/package.json +7 -7
- package/src/browsers.js +6 -0
- package/src/coverage.js +1 -1
- package/src/defaults/webdriverio.js +1 -1
- package/src/inject/qunit-hooks.js +6 -7
- package/src/job-mode.js +5 -3
- package/src/job.js +11 -6
- package/src/npm.js +8 -1
- package/src/output.js +15 -5
- package/src/start.js +91 -0
- package/src/tests.js +1 -9
- /package/src/defaults/{nyc.json → .nycrc.json} +0 -0
package/README.md
CHANGED
|
@@ -48,3 +48,4 @@ A self-sufficient test runner for UI5 applications enabling parallel execution o
|
|
|
48
48
|
|
|
49
49
|
* [Marian Zeis](https://github.com/marianfoo): Documentation page revamp [PR #54](https://github.com/ArnaudBuchholz/ui5-test-runner/pull/54)
|
|
50
50
|
* [Raj Singh](https://github.com/rajxsingh): Basic HTTP Authentication in Puppeteer [PR #71](https://github.com/ArnaudBuchholz/ui5-test-runner/pull/71)
|
|
51
|
+
* [Andreas Kunz](https://github.com/akudev): Improved documentation for TypeScript testing and reviewed `nyc` settings handling [PR #110](https://github.com/ArnaudBuchholz/ui5-test-runner/pull/110)
|
package/index.js
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
5
|
const { serve } = require('reserve')
|
|
6
|
+
const { watch } = require('fs')
|
|
7
|
+
const { capabilities } = require('./src/capabilities')
|
|
8
|
+
const { execute } = require('./src/tests')
|
|
6
9
|
const { fromCmdLine } = require('./src/job')
|
|
7
10
|
const { getOutput } = require('./src/output')
|
|
11
|
+
const { preload } = require('./src/ui5')
|
|
12
|
+
const { probe: probeBrowser } = require('./src/browsers')
|
|
13
|
+
const { recreateDir, allocPromise } = require('./src/tools')
|
|
8
14
|
const reserveConfigurationFactory = require('./src/reserve')
|
|
9
|
-
const
|
|
10
|
-
const { capabilities } = require('./src/capabilities')
|
|
11
|
-
const { watch } = require('fs')
|
|
12
|
-
const { recreateDir } = require('./src/tools')
|
|
15
|
+
const start = require('./src/start')
|
|
13
16
|
|
|
14
17
|
function send (message) {
|
|
15
18
|
if (process.send) {
|
|
@@ -20,7 +23,7 @@ function send (message) {
|
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
async function notifyAndExecuteTests (job
|
|
26
|
+
async function notifyAndExecuteTests (job) {
|
|
24
27
|
send({ msg: 'begin' })
|
|
25
28
|
try {
|
|
26
29
|
await execute(job)
|
|
@@ -44,8 +47,8 @@ async function main () {
|
|
|
44
47
|
job = fromCmdLine(process.cwd(), process.argv.slice(2))
|
|
45
48
|
output = getOutput(job)
|
|
46
49
|
await recreateDir(job.reportDir)
|
|
50
|
+
output.version()
|
|
47
51
|
if (job.mode === 'capabilities') {
|
|
48
|
-
output.version()
|
|
49
52
|
return capabilities(job)
|
|
50
53
|
}
|
|
51
54
|
const configuration = await reserveConfigurationFactory(job)
|
|
@@ -54,43 +57,61 @@ async function main () {
|
|
|
54
57
|
if (job.logServer) {
|
|
55
58
|
server.on('redirected', output.redirected)
|
|
56
59
|
}
|
|
60
|
+
|
|
61
|
+
const { promise: serverStarted, resolve: serverReady, reject: serverError } = allocPromise()
|
|
57
62
|
server
|
|
58
63
|
.on('ready', async ({ url, port }) => {
|
|
59
64
|
job.port = port
|
|
60
65
|
send({ msg: 'ready', port: job.port })
|
|
61
66
|
output.serving(url)
|
|
62
67
|
output.reportOnJobProgress()
|
|
63
|
-
|
|
64
|
-
job.status = 'Serving'
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
await notifyAndExecuteTests(job)
|
|
68
|
-
if (job.watch) {
|
|
69
|
-
delete job.start
|
|
70
|
-
if (!job.watching) {
|
|
71
|
-
output.watching(job.webapp)
|
|
72
|
-
watch(job.webapp, { recursive: true }, (eventType, filename) => {
|
|
73
|
-
output.changeDetected(eventType, filename)
|
|
74
|
-
if (!job.start) {
|
|
75
|
-
notifyAndExecuteTests(job)
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
job.watching = true
|
|
79
|
-
}
|
|
80
|
-
} else if (job.keepAlive) {
|
|
81
|
-
job.status = 'Serving'
|
|
82
|
-
} else if (job.failed) {
|
|
83
|
-
output.stop()
|
|
84
|
-
process.exit(-1)
|
|
85
|
-
} else {
|
|
86
|
-
output.stop()
|
|
87
|
-
process.exit(0)
|
|
88
|
-
}
|
|
68
|
+
serverReady()
|
|
89
69
|
})
|
|
90
70
|
.on('error', error => {
|
|
91
71
|
output.serverError(error)
|
|
92
72
|
send({ msg: 'error', error })
|
|
73
|
+
serverError()
|
|
93
74
|
})
|
|
75
|
+
await serverStarted
|
|
76
|
+
let startedCommand
|
|
77
|
+
if (job.start) {
|
|
78
|
+
output.reportOnJobProgress()
|
|
79
|
+
startedCommand = await start(job)
|
|
80
|
+
}
|
|
81
|
+
if (job.preload) {
|
|
82
|
+
await preload(job)
|
|
83
|
+
}
|
|
84
|
+
if (job.serveOnly) {
|
|
85
|
+
job.status = 'Serving'
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
await probeBrowser(job)
|
|
89
|
+
await notifyAndExecuteTests(job)
|
|
90
|
+
if (job.watch) {
|
|
91
|
+
delete job.start
|
|
92
|
+
if (!job.watching) {
|
|
93
|
+
output.watching(job.webapp)
|
|
94
|
+
watch(job.webapp, { recursive: true }, async (eventType, filename) => {
|
|
95
|
+
output.changeDetected(eventType, filename)
|
|
96
|
+
if (!job.start) {
|
|
97
|
+
await recreateDir(job.reportDir)
|
|
98
|
+
notifyAndExecuteTests(job)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
job.watching = true
|
|
102
|
+
}
|
|
103
|
+
} else if (job.keepAlive) {
|
|
104
|
+
job.status = 'Serving'
|
|
105
|
+
return
|
|
106
|
+
} else if (job.failed) {
|
|
107
|
+
process.exitCode = -1
|
|
108
|
+
}
|
|
109
|
+
output.stop()
|
|
110
|
+
await server.close()
|
|
111
|
+
if (startedCommand) {
|
|
112
|
+
await startedCommand.stop()
|
|
113
|
+
}
|
|
114
|
+
console.log('done ?')
|
|
94
115
|
}
|
|
95
116
|
|
|
96
117
|
main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,23 +44,23 @@
|
|
|
44
44
|
"homepage": "https://github.com/ArnaudBuchholz/ui5-test-runner#readme",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"commander": "^12.1.0",
|
|
47
|
+
"ps-tree": "^1.2.0",
|
|
47
48
|
"punybind": "^1.2.1",
|
|
48
49
|
"punyexpr": "^1.0.4",
|
|
49
50
|
"reserve": "2.0.5"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
|
-
"@openui5/types": "^1.
|
|
53
|
-
"@ui5/cli": "^4.0.
|
|
53
|
+
"@openui5/types": "^1.132.1",
|
|
54
|
+
"@ui5/cli": "^4.0.13",
|
|
54
55
|
"@ui5/middleware-code-coverage": "^2.0.1",
|
|
55
|
-
"dotenv": "^16.4.
|
|
56
|
+
"dotenv": "^16.4.7",
|
|
56
57
|
"jest": "^29.7.0",
|
|
57
58
|
"nock": "^13.5.6",
|
|
58
59
|
"nyc": "^17.1.0",
|
|
59
60
|
"rimraf": "^6.0.1",
|
|
60
61
|
"standard": "^17.1.2",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"ui5-tooling-transpile": "^3.5.1"
|
|
62
|
+
"typescript": "^5.7.3",
|
|
63
|
+
"ui5-tooling-transpile": "^3.5.3"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
66
|
"fsevents": "^2.3.3"
|
package/src/browsers.js
CHANGED
|
@@ -80,19 +80,25 @@ async function probe (job) {
|
|
|
80
80
|
return browserCapabilities
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
output.debug('browsers/probe', 'initial probing')
|
|
83
84
|
const browserCapabilities = await execute('probe')
|
|
85
|
+
output.debug('browsers/probe', 'browser capabilities', browserCapabilities)
|
|
84
86
|
job.browserCapabilities = browserCapabilities
|
|
85
87
|
|
|
86
88
|
const { modules } = browserCapabilities
|
|
87
89
|
const resolvedModules = {}
|
|
88
90
|
if (modules.length) {
|
|
89
91
|
for await (const name of browserCapabilities.modules) {
|
|
92
|
+
output.debug('browsers/probe', `resolving package ${name}...`)
|
|
90
93
|
resolvedModules[name] = await resolvePackage(job, name)
|
|
94
|
+
output.debug('browsers/probe', `package ${name} resolved`)
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
job.browserModules = resolvedModules
|
|
94
98
|
if (browserCapabilities['probe-with-modules']) {
|
|
99
|
+
output.debug('browsers/probe', 'probing with modules')
|
|
95
100
|
job.browserCapabilities = await execute('probe/with-modules')
|
|
101
|
+
output.debug('browsers/probe', 'browser capabilities', browserCapabilities)
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
if (job.debugCapabilitiesNoScript) {
|
package/src/coverage.js
CHANGED
|
@@ -60,7 +60,7 @@ const customFileSystem = {
|
|
|
60
60
|
|
|
61
61
|
async function instrument (job) {
|
|
62
62
|
await setupNyc(job)
|
|
63
|
-
job[$nycSettingsPath] = join(job.coverageTempDir, 'settings
|
|
63
|
+
job[$nycSettingsPath] = join(job.coverageTempDir, 'settings/.nycrc.json')
|
|
64
64
|
await cleanDir(job.coverageTempDir)
|
|
65
65
|
await createDir(join(job.coverageTempDir, 'settings'))
|
|
66
66
|
const settings = JSON.parse((await readFile(job.coverageSettings)).toString())
|
|
@@ -18,7 +18,7 @@ require('./browser')({
|
|
|
18
18
|
async capabilities ({ settings, options }) {
|
|
19
19
|
return {
|
|
20
20
|
modules: ['webdriverio'],
|
|
21
|
-
screenshot: '.png',
|
|
21
|
+
// screenshot: '.png', // TODO: https://github.com/webdriverio/webdriverio/issues/14108
|
|
22
22
|
scripts: true,
|
|
23
23
|
traces: []
|
|
24
24
|
}
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
details.isOpa = isOpa()
|
|
41
41
|
return post('QUnit/begin', details)
|
|
42
42
|
})
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
QUnit.testStart(function (details) {
|
|
45
45
|
return post('QUnit/testStart', extend(details))
|
|
46
46
|
})
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
QUnit.log(function (log) {
|
|
49
49
|
let ready = false
|
|
50
50
|
post('QUnit/log', extend(log))
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
})
|
|
65
65
|
}
|
|
66
66
|
})
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
QUnit.testDone(function (report) {
|
|
69
69
|
return post('QUnit/testDone', report)
|
|
70
70
|
})
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
QUnit.done(function (report) {
|
|
73
73
|
if (window.__coverage__) {
|
|
74
74
|
report.__coverage__ = window.__coverage__
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (typeof window.QUnit !== 'undefined' && QUnit.begin) {
|
|
81
|
-
installQUnitHooks()
|
|
81
|
+
installQUnitHooks()
|
|
82
82
|
} else {
|
|
83
83
|
let QUnit
|
|
84
84
|
let install = true
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
get: function () {
|
|
88
88
|
return QUnit
|
|
89
89
|
},
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
set: function (value) {
|
|
92
92
|
QUnit = value
|
|
93
93
|
if (QUnit && QUnit.begin && install) {
|
|
@@ -97,5 +97,4 @@
|
|
|
97
97
|
}
|
|
98
98
|
})
|
|
99
99
|
}
|
|
100
|
-
|
|
101
100
|
}())
|
package/src/job-mode.js
CHANGED
|
@@ -32,8 +32,9 @@ function buildAndCheckMode (job) {
|
|
|
32
32
|
'failFast',
|
|
33
33
|
'keepAlive',
|
|
34
34
|
'alternateNpmPath',
|
|
35
|
-
'outputInterval'
|
|
36
|
-
|
|
35
|
+
'outputInterval',
|
|
36
|
+
'screenshotTimeout'
|
|
37
|
+
], ['start'])
|
|
37
38
|
return 'capabilities'
|
|
38
39
|
}
|
|
39
40
|
if (job.url && job.url.length) {
|
|
@@ -50,7 +51,8 @@ function buildAndCheckMode (job) {
|
|
|
50
51
|
check(job, undefined, [
|
|
51
52
|
'coverageProxy',
|
|
52
53
|
'coverageProxyInclude',
|
|
53
|
-
'coverageProxyExclude'
|
|
54
|
+
'coverageProxyExclude',
|
|
55
|
+
'start'
|
|
54
56
|
])
|
|
55
57
|
return 'legacy'
|
|
56
58
|
}
|
package/src/job.js
CHANGED
|
@@ -31,8 +31,9 @@ function buildArgs (parameters) {
|
|
|
31
31
|
} else {
|
|
32
32
|
args = before
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const longName = `--${toLongName(name)}`
|
|
35
|
+
args.push(longName)
|
|
36
|
+
if (!longName.startsWith('--no-') && value !== null) {
|
|
36
37
|
if (Array.isArray(value)) {
|
|
37
38
|
args.push(...value)
|
|
38
39
|
} else {
|
|
@@ -107,7 +108,7 @@ function getCommand (cwd) {
|
|
|
107
108
|
.option('-bt, --browser-close-timeout <timeout>', '[💻🔗🧪] Maximum waiting time for browser close', timeout, 2000)
|
|
108
109
|
.option('-br, --browser-retry <count>', '[💻🔗🧪] Browser instantiation retries : if the command fails unexpectedly, it is re-executed (0 means no retry)', 1)
|
|
109
110
|
.option('-oi, --output-interval <interval>', '[💻🔗🧪] Interval for reporting progress on non interactive output (CI/CD) (0 means no output)', timeout, 30000)
|
|
110
|
-
.option('--offline', '[💻🔗🧪] Limit network usage (implies --no-npm-install)', boolean, false)
|
|
111
|
+
.option('--offline [flag]', '[💻🔗🧪] Limit network usage (implies --no-npm-install)', boolean, false)
|
|
111
112
|
|
|
112
113
|
// Common to legacy and url
|
|
113
114
|
.option('--webapp <path>', '[💻🔗] Base folder of the web application (relative to cwd)', 'webapp')
|
|
@@ -118,13 +119,13 @@ function getCommand (cwd) {
|
|
|
118
119
|
.option('--screenshot [flag]', '[💻🔗] Take screenshots during the tests execution (if supported by the browser)', boolean, true)
|
|
119
120
|
.option('--no-screenshot', '[💻🔗] Disable screenshots')
|
|
120
121
|
.option('-st, --screenshot-timeout <timeout>', '[💻🔗] Maximum waiting time for browser screenshot', timeout, 5000)
|
|
121
|
-
.option('-so, --split-opa', '[💻🔗] Split OPA tests using QUnit modules', boolean, false)
|
|
122
|
+
.option('-so, --split-opa [flag]', '[💻🔗] Split OPA tests using QUnit modules', boolean, false)
|
|
122
123
|
.option('-rg, --report-generator <path...>', '[💻🔗] Report generator paths (relative to cwd or use $/ for provided ones)', ['$/report.js'])
|
|
123
124
|
.option('--progress-page <path>', '[💻🔗] Progress page path (relative to cwd or use $/ for provided ones)', '$/report/default.html')
|
|
124
125
|
|
|
125
126
|
.option('--coverage [flag]', '[💻🔗] Enable or disable code coverage', boolean)
|
|
126
127
|
.option('--no-coverage', '[💻🔗] Disable code coverage')
|
|
127
|
-
.option('-cs, --coverage-settings <path>', '[💻🔗] Path to a custom
|
|
128
|
+
.option('-cs, --coverage-settings <path>', '[💻🔗] Path to a custom .nycrc.json file providing settings for instrumentation (relative to cwd or use $/ for provided ones)', '$/.nycrc.json')
|
|
128
129
|
.option('-ctd, --coverage-temp-dir <path>', '[💻🔗] Directory to output raw coverage information to (relative to cwd)', '.nyc_output')
|
|
129
130
|
.option('-crd, --coverage-report-dir <path>', '[💻🔗] Directory to store the coverage report files (relative to cwd)', 'coverage')
|
|
130
131
|
.option('-cr, --coverage-reporters <reporter...>', '[💻🔗] List of nyc reporters to use (text is always used)', ['lcov', 'cobertura'])
|
|
@@ -137,7 +138,7 @@ function getCommand (cwd) {
|
|
|
137
138
|
|
|
138
139
|
// Specific to legacy (and might be used with url if pointing to local project)
|
|
139
140
|
.option('--ui5 <url>', '[💻] UI5 url', url, 'https://ui5.sap.com')
|
|
140
|
-
.option('--disable-ui5', '[💻] Disable UI5 mapping (also disable libs)', boolean, false)
|
|
141
|
+
.option('--disable-ui5 [flag]', '[💻] Disable UI5 mapping (also disable libs)', boolean, false)
|
|
141
142
|
.option('--libs <lib...>', '[💻] Library mapping (<relative>=<path> or <path>)', arrayOf(lib))
|
|
142
143
|
.option('--mappings <mapping...>', '[💻] Custom mapping (<match>=<file|url>(<config>))', arrayOf(mapping))
|
|
143
144
|
.option('--cache <path>', '[💻] Cache UI5 resources locally in the given folder (empty to disable)')
|
|
@@ -145,6 +146,10 @@ function getCommand (cwd) {
|
|
|
145
146
|
.option('--testsuite <path>', '[💻] Path of the testsuite file (relative to webapp, URL parameters are supported)', 'test/testsuite.qunit.html')
|
|
146
147
|
.option('-w, --watch [flag]', '[💻] Monitor the webapp folder and re-execute tests on change', boolean, false)
|
|
147
148
|
|
|
149
|
+
// Specific to url
|
|
150
|
+
.option('--start <command>', '[🔗] Start command (might be an NPM script or a shell command)', string)
|
|
151
|
+
.option('--start-timeout <timeout>', '[🔗] Maximum waiting time for the start command (based on when the first URL becomes available)', timeout, 5000)
|
|
152
|
+
|
|
148
153
|
// Specific to coverage in url mode (experimental)
|
|
149
154
|
.option('-cp, --coverage-proxy [flag]', `[🔗] ${EXPERIMENTAL_OPTION} use internal proxy to instrument remote files`, boolean, false)
|
|
150
155
|
.option('-cpi, --coverage-proxy-include <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to instrument for coverage`, regex, '.*')
|
package/src/npm.js
CHANGED
|
@@ -77,24 +77,31 @@ module.exports = {
|
|
|
77
77
|
resolveDependencyPath,
|
|
78
78
|
|
|
79
79
|
async resolvePackage (job, name) {
|
|
80
|
+
const output = getOutput(job)
|
|
80
81
|
let modulePath
|
|
81
82
|
let justInstalled = false
|
|
83
|
+
output.debug('npm', `resolving dependency path of package ${name}...`)
|
|
82
84
|
try {
|
|
83
85
|
modulePath = resolveDependencyPath(name)
|
|
84
86
|
} catch (e) {
|
|
87
|
+
output.debug('npm', e)
|
|
85
88
|
}
|
|
86
89
|
if (!modulePath) {
|
|
90
|
+
output.debug('npm', `finding dependency path of package ${name}...`);
|
|
87
91
|
[modulePath, justInstalled] = await findDependencyPath(job, name)
|
|
88
92
|
}
|
|
89
|
-
|
|
93
|
+
output.debug('npm', `opening installed package ${name}`)
|
|
90
94
|
const installedPackage = require(join(modulePath, 'package.json'))
|
|
91
95
|
const { version: installedVersion } = installedPackage
|
|
92
96
|
output.resolvedPackage(name, modulePath, installedVersion)
|
|
93
97
|
if (!justInstalled && !job.offline) {
|
|
98
|
+
output.debug('npm', `fetching latest version of package ${name}`)
|
|
94
99
|
const latestVersion = await npm(job, 'view', name, 'version')
|
|
95
100
|
if (latestVersion !== installedVersion) {
|
|
96
101
|
output.packageNotLatest(name, latestVersion)
|
|
97
102
|
}
|
|
103
|
+
} else {
|
|
104
|
+
output.debug('npm', `justInstalled=${justInstalled} offline=${job.offline}`)
|
|
98
105
|
}
|
|
99
106
|
return modulePath
|
|
100
107
|
}
|
package/src/output.js
CHANGED
|
@@ -171,16 +171,23 @@ function output (job, ...args) {
|
|
|
171
171
|
writeFileSync(
|
|
172
172
|
join(job.reportDir, 'output.txt'),
|
|
173
173
|
args.map(arg => {
|
|
174
|
-
if (typeof arg === 'object') {
|
|
175
|
-
return JSON.stringify(arg, undefined, 2)
|
|
176
|
-
}
|
|
177
174
|
if (arg === undefined) {
|
|
178
175
|
return 'undefined'
|
|
179
176
|
}
|
|
180
177
|
if (arg === null) {
|
|
181
178
|
return 'null'
|
|
182
179
|
}
|
|
183
|
-
|
|
180
|
+
if (arg instanceof Error) {
|
|
181
|
+
let error = `${arg.name} ${arg.message}`
|
|
182
|
+
if (arg.cause) {
|
|
183
|
+
error += `, cause : ${arg.cause.name} ${arg.cause.message}`
|
|
184
|
+
}
|
|
185
|
+
return error
|
|
186
|
+
}
|
|
187
|
+
if (typeof arg !== 'string') {
|
|
188
|
+
return JSON.stringify(arg, undefined, 2)
|
|
189
|
+
}
|
|
190
|
+
return arg
|
|
184
191
|
}).join(' ') + '\n',
|
|
185
192
|
{
|
|
186
193
|
encoding: 'utf-8',
|
|
@@ -279,7 +286,7 @@ function build (job) {
|
|
|
279
286
|
|
|
280
287
|
debug: (moduleSpecifier, ...args) => {
|
|
281
288
|
const [mainModule] = moduleSpecifier.split('/')
|
|
282
|
-
if (job.debugVerbose && (job.debugVerbose.includes(moduleSpecifier) || job.debugVerbose.includes(mainModule))) {
|
|
289
|
+
if (job.debugVerbose && (job.debugVerbose.includes('*') || job.debugVerbose.includes(moduleSpecifier) || job.debugVerbose.includes(mainModule))) {
|
|
283
290
|
wrap(() => {
|
|
284
291
|
console.log(`🐞${moduleSpecifier}`, ...args)
|
|
285
292
|
output(job, `🐞${moduleSpecifier}`, ...args)
|
|
@@ -324,6 +331,9 @@ function build (job) {
|
|
|
324
331
|
}),
|
|
325
332
|
|
|
326
333
|
reportOnJobProgress () {
|
|
334
|
+
if (this.reportIntervalId) {
|
|
335
|
+
return
|
|
336
|
+
}
|
|
327
337
|
if (interactive) {
|
|
328
338
|
this.reportIntervalId = setInterval(progress.bind(null, job), 250)
|
|
329
339
|
} else if (job.outputInterval && !inJest) {
|
package/src/start.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const { exec } = require('child_process')
|
|
2
|
+
const { stat, readFile } = require('fs/promises')
|
|
3
|
+
const { join } = require('path')
|
|
4
|
+
const { getOutput } = require('./output')
|
|
5
|
+
const psTreeNodeCb = require('ps-tree')
|
|
6
|
+
const { promisify } = require('util')
|
|
7
|
+
const psTree = promisify(psTreeNodeCb)
|
|
8
|
+
|
|
9
|
+
const $startedProcess = Symbol('startedProcess')
|
|
10
|
+
|
|
11
|
+
module.exports = async function start (job) {
|
|
12
|
+
let { start } = job
|
|
13
|
+
const output = getOutput(job)
|
|
14
|
+
const [command] = start.split(' ')
|
|
15
|
+
|
|
16
|
+
job.status = 'Executing start command'
|
|
17
|
+
|
|
18
|
+
// check if existing NPM script
|
|
19
|
+
const packagePath = join(job.cwd, 'package.json')
|
|
20
|
+
try {
|
|
21
|
+
const packageStat = await stat(packagePath)
|
|
22
|
+
if (packageStat.isFile()) {
|
|
23
|
+
output.debug('start', 'Found package.json in cwd')
|
|
24
|
+
const packageFile = JSON.parse(await readFile(packagePath, 'utf-8'))
|
|
25
|
+
if (packageFile.scripts[command]) {
|
|
26
|
+
output.debug('start', 'Found matching start script in package.json')
|
|
27
|
+
start = `npm run ${start}`
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
output.debug('start', 'Missing or invalid package.json in cwd', e)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let childProcessExited = false
|
|
35
|
+
output.debug('start', 'Starting command :', start)
|
|
36
|
+
const childProcess = exec(start, {
|
|
37
|
+
cwd: job.cwd,
|
|
38
|
+
windowsHide: true
|
|
39
|
+
})
|
|
40
|
+
childProcess.on('close', () => {
|
|
41
|
+
output.debug('start', 'start command process exited')
|
|
42
|
+
childProcessExited = true
|
|
43
|
+
})
|
|
44
|
+
output.monitor(childProcess)
|
|
45
|
+
job[$startedProcess] = childProcess
|
|
46
|
+
|
|
47
|
+
job.status = 'Waiting for URL to be reachable'
|
|
48
|
+
|
|
49
|
+
const [url] = job.url
|
|
50
|
+
|
|
51
|
+
const begin = Date.now()
|
|
52
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
|
53
|
+
while (!childProcessExited && Date.now() - begin <= job.startTimeout) {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(url)
|
|
56
|
+
output.debug('start', url, response.status)
|
|
57
|
+
if (response.status === 200) {
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
output.debug('start', url, e)
|
|
62
|
+
await new Promise(resolve => setTimeout(resolve, 250))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (childProcessExited) {
|
|
67
|
+
throw new Error(`Start command failed with exit code ${childProcess.exitCode}`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const stop = async () => {
|
|
71
|
+
output.debug('start', 'Getting child processes...')
|
|
72
|
+
const childProcesses = await psTree(childProcess.pid)
|
|
73
|
+
for (const child of childProcesses) {
|
|
74
|
+
output.debug('start', 'Terminating process', child.PID)
|
|
75
|
+
try {
|
|
76
|
+
process.kill(child.PID, 'SIGKILL')
|
|
77
|
+
} catch (e) {
|
|
78
|
+
output.debug('start', 'Failed to terminate process', child.PID, ':', e)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (Date.now() - begin > job.startTimeout) {
|
|
84
|
+
await stop()
|
|
85
|
+
throw new Error(`Timeout while waiting for ${url}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
stop
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/tests.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { start } = require('./browsers')
|
|
4
4
|
const { instrument } = require('./coverage')
|
|
5
|
-
const { recreateDir } = require('./tools')
|
|
6
5
|
const { globallyTimedOut } = require('./timeout')
|
|
7
6
|
const { save, generate } = require('./report')
|
|
8
7
|
const { getOutput } = require('./output')
|
|
@@ -12,7 +11,6 @@ const {
|
|
|
12
11
|
$proxifiedUrls
|
|
13
12
|
} = require('./symbols')
|
|
14
13
|
const { UTRError } = require('./error')
|
|
15
|
-
const { preload } = require('./ui5')
|
|
16
14
|
const parallelize = require('./parallelize')
|
|
17
15
|
|
|
18
16
|
function task (job, method) {
|
|
@@ -128,12 +126,6 @@ async function process (job) {
|
|
|
128
126
|
|
|
129
127
|
module.exports = {
|
|
130
128
|
async execute (job) {
|
|
131
|
-
await recreateDir(job.reportDir)
|
|
132
|
-
getOutput(job).version()
|
|
133
|
-
if (job.preload) {
|
|
134
|
-
await preload(job)
|
|
135
|
-
}
|
|
136
|
-
await probeBrowser(job)
|
|
137
129
|
if (job.mode !== 'url') {
|
|
138
130
|
job.url = [`http://localhost:${job.port}/${job.testsuite}`]
|
|
139
131
|
} else if (!job.browserCapabilities.scripts) {
|
|
File without changes
|