ui5-test-runner 5.7.1 → 5.7.2
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 +6 -1
- package/package.json +9 -7
- package/src/capabilities/index.js +3 -0
- package/src/capabilities/tests/basic/index.js +0 -2
- package/src/capabilities/tests/basic/ui5.html +1 -1
- package/src/clean.js +29 -0
- package/src/defaults/happy-dom.js +123 -0
- package/src/defaults/jsdom/compatibility.js +2 -2
- package/src/defaults/jsdom.js +6 -0
- package/src/defaults/report/default.html +2 -0
- package/src/job.js +1 -1
- package/src/npm.js +35 -9
- package/src/output.js +5 -0
- package/src/report.js +8 -0
package/index.js
CHANGED
|
@@ -16,6 +16,8 @@ const { start } = require('./src/start')
|
|
|
16
16
|
const { executeIf } = require('./src/if')
|
|
17
17
|
const { batch } = require('./src/batch')
|
|
18
18
|
const { end } = require('./src/end')
|
|
19
|
+
const { checkLatest } = require('./src/npm')
|
|
20
|
+
const { cleanHandles } = require('./src/clean')
|
|
19
21
|
|
|
20
22
|
function send (message) {
|
|
21
23
|
if (process.send) {
|
|
@@ -50,11 +52,12 @@ async function main () {
|
|
|
50
52
|
job = fromCmdLine(process.cwd(), process.argv.slice(2))
|
|
51
53
|
output = getOutput(job)
|
|
52
54
|
await recreateDir(job.reportDir)
|
|
53
|
-
output.version()
|
|
55
|
+
const { name, version } = output.version()
|
|
54
56
|
if (job.batchMode) {
|
|
55
57
|
output.batchMode()
|
|
56
58
|
}
|
|
57
59
|
output.reportOnJobProgress()
|
|
60
|
+
checkLatest(job, name, version)
|
|
58
61
|
if (job.mode === 'capabilities') {
|
|
59
62
|
return capabilities(job)
|
|
60
63
|
}
|
|
@@ -149,6 +152,8 @@ async function main () {
|
|
|
149
152
|
if (startedCommand) {
|
|
150
153
|
await startedCommand.stop()
|
|
151
154
|
}
|
|
155
|
+
output.debug('main', 'terminated')
|
|
156
|
+
cleanHandles(job)
|
|
152
157
|
}
|
|
153
158
|
|
|
154
159
|
main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "5.7.
|
|
3
|
+
"version": "5.7.2",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"test:report": "node ./src/defaults/report.js ./test/report && reserve --config ./test/report/reserve.json",
|
|
23
23
|
"test:text-report": "node ./src/defaults/text-report.js ./test/report",
|
|
24
24
|
"build:doc": "node build/doc",
|
|
25
|
-
"clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio jsdom"
|
|
25
|
+
"clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio jsdom",
|
|
26
|
+
"update": "ncu -i --format group"
|
|
26
27
|
},
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
@@ -47,21 +48,22 @@
|
|
|
47
48
|
"commander": "^12.1.0",
|
|
48
49
|
"ps-tree": "^1.2.0",
|
|
49
50
|
"punybind": "^1.2.1",
|
|
50
|
-
"punyexpr": "1.
|
|
51
|
+
"punyexpr": "1.1.1",
|
|
51
52
|
"reserve": "2.2.0"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@openui5/types": "^1.
|
|
55
|
-
"@ui5/cli": "^4.0.
|
|
55
|
+
"@openui5/types": "^1.136.0",
|
|
56
|
+
"@ui5/cli": "^4.0.18",
|
|
56
57
|
"@ui5/middleware-code-coverage": "^2.0.1",
|
|
57
58
|
"dotenv": "^16.5.0",
|
|
58
59
|
"jest": "^29.7.0",
|
|
59
|
-
"nock": "^14.0.
|
|
60
|
+
"nock": "^14.0.5",
|
|
61
|
+
"npm-check-updates": "^18.0.1",
|
|
60
62
|
"nyc": "^17.1.0",
|
|
61
63
|
"rimraf": "^6.0.1",
|
|
62
64
|
"standard": "^17.1.2",
|
|
63
65
|
"typescript": "^5.8.3",
|
|
64
|
-
"ui5-tooling-transpile": "^3.
|
|
66
|
+
"ui5-tooling-transpile": "^3.8.0"
|
|
65
67
|
},
|
|
66
68
|
"optionalDependencies": {
|
|
67
69
|
"fsevents": "^2.3.3"
|
|
@@ -83,6 +83,9 @@ async function capabilities (job) {
|
|
|
83
83
|
if (job.logServer) {
|
|
84
84
|
server.on('redirected', output.redirected)
|
|
85
85
|
}
|
|
86
|
+
server.on('error', (error) => {
|
|
87
|
+
output.error('REserve error:', error)
|
|
88
|
+
})
|
|
86
89
|
await new Promise(resolve => server
|
|
87
90
|
.on('ready', ({ port }) => {
|
|
88
91
|
job.port = port
|
|
@@ -7,14 +7,12 @@ module.exports = [{
|
|
|
7
7
|
url: 'basic/index.html'
|
|
8
8
|
}, {
|
|
9
9
|
label: 'Loads a UI5 example',
|
|
10
|
-
for: capabilities => !capabilities.modules.includes('jsdom'), // does not work anymore on JSDOM
|
|
11
10
|
url: 'basic/ui5.html',
|
|
12
11
|
endpoint: ({ body }) => {
|
|
13
12
|
assert.strictEqual(body['sap.m.Button'], true)
|
|
14
13
|
}
|
|
15
14
|
}, {
|
|
16
15
|
label: 'Loads a UI5 inside an iframe',
|
|
17
|
-
for: capabilities => !capabilities.modules.includes('jsdom'), // does not work anymore on JSDOM
|
|
18
16
|
url: 'basic/iframe.html',
|
|
19
17
|
endpoint: ({ body }) => {
|
|
20
18
|
assert.strictEqual(body['sap.m.Button'], true)
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
sap.ui.getCore().attachInit(function() {
|
|
15
15
|
const xhr = new XMLHttpRequest()
|
|
16
16
|
xhr.open('POST', '/_/log')
|
|
17
|
-
if (parent !== window) {
|
|
17
|
+
if (parent !== window && parent.document.location.toString() !== 'about:blank') {
|
|
18
18
|
xhr.setRequestHeader('x-page-url', parent.document.location.toString())
|
|
19
19
|
}
|
|
20
20
|
xhr.send('{"sap.m.Button":' + !!sap.m.Button + '}')
|
package/src/clean.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { getOutput } = require('./output')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
cleanHandles (job) {
|
|
5
|
+
const output = getOutput(job)
|
|
6
|
+
const activeHandles = process._getActiveHandles ? process._getActiveHandles() : []
|
|
7
|
+
let displayWarning = true
|
|
8
|
+
for (const handle of activeHandles) {
|
|
9
|
+
const className = handle && handle.constructor && handle.constructor.name
|
|
10
|
+
output.debug('handle', 'active handle', className)
|
|
11
|
+
if (className === 'TLSSocket') {
|
|
12
|
+
if (displayWarning) {
|
|
13
|
+
displayWarning = false
|
|
14
|
+
output.detectedLeakOfHandles()
|
|
15
|
+
}
|
|
16
|
+
let info
|
|
17
|
+
if (handle._httpMessage) {
|
|
18
|
+
const { path, method, host, protocol } = handle._httpMessage
|
|
19
|
+
info = `${method} ${protocol}://${host}${path}`
|
|
20
|
+
} else {
|
|
21
|
+
const { localAddress, localPort, remoteAddress, remotePort } = handle
|
|
22
|
+
info = `from ${localAddress}:${localPort} to ${remoteAddress}:${remotePort}`
|
|
23
|
+
}
|
|
24
|
+
output.debug('handle', 'TLS socket', info)
|
|
25
|
+
handle.destroy()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = (attributes) => {
|
|
4
|
+
console.log(JSON.stringify(attributes, (key, value) => {
|
|
5
|
+
if (key === 'headers' && typeof value === 'object') {
|
|
6
|
+
const literal = {}
|
|
7
|
+
value.forEach((headerValue, headerName) => {
|
|
8
|
+
literal[headerName] = headerValue
|
|
9
|
+
})
|
|
10
|
+
return literal
|
|
11
|
+
}
|
|
12
|
+
return value
|
|
13
|
+
}, 2))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
require('./browser')({
|
|
17
|
+
metadata: {
|
|
18
|
+
name: 'happy-dom',
|
|
19
|
+
options: [
|
|
20
|
+
['--debug [flag]', 'Enable more traces', false]
|
|
21
|
+
],
|
|
22
|
+
capabilities: {
|
|
23
|
+
modules: ['happy-dom'],
|
|
24
|
+
scripts: true,
|
|
25
|
+
traces: ['multiplex']
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
async run ({
|
|
30
|
+
settings: { url, scripts, modules }
|
|
31
|
+
// options
|
|
32
|
+
}) {
|
|
33
|
+
const happyDom = require(modules['happy-dom'])
|
|
34
|
+
const { Browser } = happyDom
|
|
35
|
+
const browser = new Browser({
|
|
36
|
+
settings: {
|
|
37
|
+
fetch: {
|
|
38
|
+
interceptor: {
|
|
39
|
+
beforeAsyncRequest: async ({ request: { method, url, headers } }) => {
|
|
40
|
+
log({
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
channel: 'network',
|
|
43
|
+
type: 'request',
|
|
44
|
+
async: true,
|
|
45
|
+
request: { method, url, headers }
|
|
46
|
+
})
|
|
47
|
+
},
|
|
48
|
+
beforeSyncRequest: ({ request: { method, url, headers } }) => {
|
|
49
|
+
log({
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
channel: 'network',
|
|
52
|
+
type: 'request',
|
|
53
|
+
async: false,
|
|
54
|
+
request: { method, url, headers }
|
|
55
|
+
})
|
|
56
|
+
},
|
|
57
|
+
afterAsyncResponse: async ({ request: { method, url, headers }, response, window }) => {
|
|
58
|
+
const body = await response.text()
|
|
59
|
+
log({
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
channel: 'network',
|
|
62
|
+
type: 'response',
|
|
63
|
+
async: true,
|
|
64
|
+
request: { method, url, headers },
|
|
65
|
+
response: {
|
|
66
|
+
...response,
|
|
67
|
+
body
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
return new window.Response(body, {
|
|
71
|
+
status: response.status,
|
|
72
|
+
statusText: response.statusText,
|
|
73
|
+
headers: response.headers
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
afterSyncResponse: ({ request: { method, url, headers }, response }) => {
|
|
77
|
+
log({
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
channel: 'network',
|
|
80
|
+
type: 'response',
|
|
81
|
+
async: false,
|
|
82
|
+
request: { method, url, headers },
|
|
83
|
+
response: {
|
|
84
|
+
...response,
|
|
85
|
+
body: response.body.toString('utf8')
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
console: {
|
|
93
|
+
error: (...args) => log({
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
channel: 'console',
|
|
96
|
+
type: 'error',
|
|
97
|
+
message: args.join(' ')
|
|
98
|
+
}),
|
|
99
|
+
warn: (...args) => log({
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
channel: 'console',
|
|
102
|
+
type: 'warning',
|
|
103
|
+
message: args.join(' ')
|
|
104
|
+
}),
|
|
105
|
+
info: (...args) => log({
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
channel: 'console',
|
|
108
|
+
type: 'info',
|
|
109
|
+
message: args.join(' ')
|
|
110
|
+
}),
|
|
111
|
+
log: (...args) => log({
|
|
112
|
+
timestamp: new Date().toISOString(),
|
|
113
|
+
channel: 'console',
|
|
114
|
+
type: 'log',
|
|
115
|
+
message: args.join(' ')
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
const page = browser.newPage()
|
|
120
|
+
scripts.forEach(script => page.mainFrame.window.eval(script))
|
|
121
|
+
page.goto(url)
|
|
122
|
+
}
|
|
123
|
+
})
|
|
@@ -115,8 +115,8 @@ function fixCaseSensitiveSelectors ({ Document }) {
|
|
|
115
115
|
const { querySelector, querySelectorAll } = Document.prototype
|
|
116
116
|
Object.assign(Document.prototype, {
|
|
117
117
|
querySelector (selectors) {
|
|
118
|
-
const result = querySelector.call(this, selectors)
|
|
119
|
-
if (result
|
|
118
|
+
const result = querySelector.call(this, selectors)
|
|
119
|
+
if (result === null && selectors.match(uppercaseTag)) {
|
|
120
120
|
console.log(JSON.stringify({
|
|
121
121
|
timestamp: new Date().toISOString(),
|
|
122
122
|
channel: 'debug',
|
package/src/defaults/jsdom.js
CHANGED
|
@@ -23,6 +23,12 @@ require('./browser')({
|
|
|
23
23
|
const { JSDOM, VirtualConsole } = jsdom
|
|
24
24
|
|
|
25
25
|
const virtualConsole = new VirtualConsole()
|
|
26
|
+
virtualConsole.on('jsdomError', (...args) => console.log(JSON.stringify({
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
channel: 'console',
|
|
29
|
+
type: 'jsdomError',
|
|
30
|
+
message: args.join(' ')
|
|
31
|
+
})))
|
|
26
32
|
virtualConsole.on('error', (...args) => console.log(JSON.stringify({
|
|
27
33
|
timestamp: new Date().toISOString(),
|
|
28
34
|
channel: 'console',
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
<head>
|
|
3
3
|
<title>ui5-test-runner</title>
|
|
4
4
|
<link rel="stylesheet" href="/_/report/styles.css">
|
|
5
|
+
<script>window.module = window.module || {}</script>
|
|
5
6
|
<script src="/_/punyexpr.js"></script>
|
|
7
|
+
<script>window.punyexpr = module.exports.punyexpr</script>
|
|
6
8
|
<script src="/_/punybind.js"></script>
|
|
7
9
|
<script src="/_/report/common.js"></script>
|
|
8
10
|
<script src="/_/report/main.js"></script>
|
package/src/job.js
CHANGED
|
@@ -150,7 +150,7 @@ function getCommand (cwd) {
|
|
|
150
150
|
.option('--start-timeout <timeout>', '[💻🔗] Maximum waiting time for the start command (based on when the first URL becomes available)', timeout, 5000)
|
|
151
151
|
|
|
152
152
|
.option('--end <script>', '[💻🔗] End script (will receive path to `job.js`)', string)
|
|
153
|
-
.option('--end-timeout <timeout>', '[💻🔗] Maximum waiting time for the end script', timeout,
|
|
153
|
+
.option('--end-timeout <timeout>', '[💻🔗] Maximum waiting time for the end script', timeout, 15000)
|
|
154
154
|
|
|
155
155
|
// Specific to legacy (and might be used with url if pointing to local project)
|
|
156
156
|
.option('--ui5 <url>', '[💻📡] UI5 url', url, 'https://ui5.sap.com')
|
package/src/npm.js
CHANGED
|
@@ -73,11 +73,41 @@ async function findDependencyPath (job, name) {
|
|
|
73
73
|
return [globalPath, justInstalled]
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const noop = () => {}
|
|
77
|
+
|
|
78
|
+
function getSafeJobAndOutput (nullableJob) {
|
|
79
|
+
if (nullableJob) {
|
|
80
|
+
return { job: nullableJob, output: getOutput(nullableJob) }
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
job: {},
|
|
84
|
+
output: {
|
|
85
|
+
debug: noop,
|
|
86
|
+
resolvedPackage: noop,
|
|
87
|
+
packageNotLatest: noop
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function checkLatest (nullableJob, name, installedVersion) {
|
|
93
|
+
const { job, output } = getSafeJobAndOutput(nullableJob)
|
|
94
|
+
if (!job.offline) {
|
|
95
|
+
output.debug('npm', `fetching latest version of package ${name}`)
|
|
96
|
+
const latestVersion = await npm(job, 'view', name, 'version')
|
|
97
|
+
if (latestVersion !== installedVersion) {
|
|
98
|
+
output.packageNotLatest(name, latestVersion)
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
output.debug('npm', `offline=${job.offline}`)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
module.exports = {
|
|
77
106
|
resolveDependencyPath,
|
|
107
|
+
checkLatest,
|
|
78
108
|
|
|
79
|
-
async resolvePackage (
|
|
80
|
-
const output =
|
|
109
|
+
async resolvePackage (nullableJob, name) {
|
|
110
|
+
const { job, output } = getSafeJobAndOutput(nullableJob)
|
|
81
111
|
let modulePath
|
|
82
112
|
let justInstalled = false
|
|
83
113
|
output.debug('npm', `resolving dependency path of package ${name}...`)
|
|
@@ -94,14 +124,10 @@ module.exports = {
|
|
|
94
124
|
const installedPackage = require(join(modulePath, 'package.json'))
|
|
95
125
|
const { version: installedVersion } = installedPackage
|
|
96
126
|
output.resolvedPackage(name, modulePath, installedVersion)
|
|
97
|
-
if (!justInstalled
|
|
98
|
-
|
|
99
|
-
const latestVersion = await npm(job, 'view', name, 'version')
|
|
100
|
-
if (latestVersion !== installedVersion) {
|
|
101
|
-
output.packageNotLatest(name, latestVersion)
|
|
102
|
-
}
|
|
127
|
+
if (!justInstalled) {
|
|
128
|
+
checkLatest(job, name, installedVersion)
|
|
103
129
|
} else {
|
|
104
|
-
output.debug('npm', `justInstalled=${justInstalled}
|
|
130
|
+
output.debug('npm', `justInstalled=${justInstalled}`)
|
|
105
131
|
}
|
|
106
132
|
return modulePath
|
|
107
133
|
}
|
package/src/output.js
CHANGED
|
@@ -270,6 +270,7 @@ function build (job) {
|
|
|
270
270
|
if (job.debugDevMode) {
|
|
271
271
|
log(job, p80()`⚠️ Development mode ⚠️`)
|
|
272
272
|
}
|
|
273
|
+
return { name, version }
|
|
273
274
|
},
|
|
274
275
|
|
|
275
276
|
serving: url => {
|
|
@@ -384,6 +385,10 @@ function build (job) {
|
|
|
384
385
|
wrap(() => log(job, `⚠️ [PKGVRS] latest version of ${name} is ${latestVersion}`))()
|
|
385
386
|
},
|
|
386
387
|
|
|
388
|
+
detectedLeakOfHandles () {
|
|
389
|
+
wrap(() => log(job, '⚠️ [HDLEAK] leaking Node.js handle(s) detected. This may cause issues with the shutdown'))()
|
|
390
|
+
},
|
|
391
|
+
|
|
387
392
|
browserStart (url) {
|
|
388
393
|
const text = p80()`${getElapsed(job)} >> ${pad.lt(url)} [${filename(url)}]`
|
|
389
394
|
if (interactive) {
|
package/src/report.js
CHANGED
|
@@ -53,9 +53,14 @@ module.exports = {
|
|
|
53
53
|
job.end = new Date()
|
|
54
54
|
job.failed = !!job.failed
|
|
55
55
|
job.testPageHashes = job.testPageUrls.map(url => filename(url))
|
|
56
|
+
output.debug('report', 'saving job...')
|
|
56
57
|
await save(job)
|
|
58
|
+
output.debug('report', 'job saved.')
|
|
59
|
+
output.debug('report', 'generating text report...')
|
|
57
60
|
await generateTextReport(job)
|
|
61
|
+
output.debug('report', 'text report generated.')
|
|
58
62
|
const promises = job.reportGenerator.map(generator => {
|
|
63
|
+
output.debug('report', 'launching', generator, '...')
|
|
59
64
|
const { promise, resolve } = allocPromise()
|
|
60
65
|
const childProcess = fork(
|
|
61
66
|
generator,
|
|
@@ -70,13 +75,16 @@ module.exports = {
|
|
|
70
75
|
)
|
|
71
76
|
const buffers = output.monitor(childProcess, false)
|
|
72
77
|
childProcess.on('close', exitCode => {
|
|
78
|
+
output.debug('report', generator, 'ended with exit code', exitCode)
|
|
73
79
|
if (exitCode !== 0) {
|
|
74
80
|
output.reportGeneratorFailed(generator, exitCode, buffers)
|
|
75
81
|
}
|
|
76
82
|
resolve()
|
|
83
|
+
output.debug('report', generator, 'resolved')
|
|
77
84
|
})
|
|
78
85
|
return promise
|
|
79
86
|
})
|
|
87
|
+
output.debug('report', 'generators count:', promises.length)
|
|
80
88
|
await Promise.all(promises)
|
|
81
89
|
job.status = 'Reports generated'
|
|
82
90
|
}
|