ui5-test-runner 1.1.5 → 2.0.0
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 +32 -188
- package/index.js +47 -16
- package/package.json +28 -10
- package/src/add-test-pages.js +35 -0
- package/src/add-test-pages.spec.js +95 -0
- package/src/browser.spec.js +724 -0
- package/src/browsers.js +220 -59
- package/src/capabilities/index.js +194 -0
- package/src/capabilities/tests/basic/iframe.html +8 -0
- package/src/capabilities/tests/basic/index.html +12 -0
- package/src/capabilities/tests/basic/index.js +20 -0
- package/src/capabilities/tests/basic/ui5.html +24 -0
- package/src/capabilities/tests/dynamic-include/index.js +21 -0
- package/src/capabilities/tests/dynamic-include/mix.html +11 -0
- package/src/capabilities/tests/dynamic-include/one.html +11 -0
- package/src/capabilities/tests/dynamic-include/post.js +3 -0
- package/src/capabilities/tests/dynamic-include/test.js +1 -0
- package/src/capabilities/tests/dynamic-include/two.html +11 -0
- package/src/capabilities/tests/index.js +16 -0
- package/src/capabilities/tests/local-storage/index.html +16 -0
- package/src/capabilities/tests/local-storage/index.js +21 -0
- package/src/capabilities/tests/screenshot/index.html +13 -0
- package/src/capabilities/tests/screenshot/index.js +18 -0
- package/src/capabilities/tests/scripts/index.js +50 -0
- package/src/capabilities/tests/scripts/qunit.html +22 -0
- package/src/capabilities/tests/scripts/testsuite.html +10 -0
- package/src/capabilities/tests/scripts/testsuite.js +8 -0
- package/src/capabilities/tests/timeout/index.html +21 -0
- package/src/capabilities/tests/timeout/index.js +19 -0
- package/src/capabilities/tests/traces/index.html +18 -0
- package/src/capabilities/tests/traces/index.js +81 -0
- package/src/cors.js +1 -1
- package/src/cors.spec.js +41 -0
- package/src/coverage.js +30 -18
- package/src/coverage.spec.js +79 -0
- package/src/csv-reader.js +36 -0
- package/src/csv-reader.spec.js +42 -0
- package/src/csv-writer.js +52 -0
- package/src/csv-writer.spec.js +77 -0
- package/src/defaults/browser.js +144 -0
- package/src/defaults/jsdom/compatibility.js +95 -0
- package/src/defaults/jsdom/debug.js +23 -0
- package/src/defaults/jsdom/resource-loader.js +43 -0
- package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +39 -0
- package/src/defaults/jsdom.js +64 -0
- package/src/defaults/junit-xml-report.js +64 -0
- package/src/defaults/puppeteer.js +111 -0
- package/src/defaults/report/common.js +38 -0
- package/src/defaults/report/default.html +84 -0
- package/src/defaults/report/main.js +44 -0
- package/src/defaults/report/progress.js +49 -0
- package/src/defaults/report/styles.css +66 -0
- package/src/defaults/report.js +69 -0
- package/src/defaults/selenium-webdriver/chrome.js +38 -0
- package/src/defaults/selenium-webdriver/edge.js +25 -0
- package/src/defaults/selenium-webdriver/firefox.js +31 -0
- package/src/defaults/selenium-webdriver.js +138 -0
- package/src/endpoints.js +70 -124
- package/src/error.js +52 -0
- package/src/error.spec.js +17 -0
- package/src/get-job-progress.js +69 -0
- package/src/get-job-progress.spec.js +175 -0
- package/src/inject/post.js +96 -0
- package/src/inject/post.spec.js +147 -0
- package/src/inject/qunit-hooks.js +6 -21
- package/src/inject/qunit-intercept.js +30 -0
- package/src/inject/qunit-redirect.js +15 -7
- package/src/job-mode.js +45 -0
- package/src/job.js +254 -108
- package/src/job.spec.js +413 -0
- package/src/npm.js +73 -0
- package/src/npm.spec.js +98 -0
- package/src/options.js +73 -0
- package/src/options.spec.js +125 -0
- package/src/output.js +450 -131
- package/src/qunit-hooks.js +116 -0
- package/src/qunit-hooks.spec.js +687 -0
- package/src/report.js +42 -0
- package/src/reserve.js +3 -4
- package/src/simulate.spec.js +437 -0
- package/src/symbols.js +8 -0
- package/src/tests.js +127 -84
- package/src/timeout.spec.js +39 -0
- package/src/tools.js +111 -4
- package/src/tools.spec.js +90 -0
- package/src/ui5.js +3 -3
- package/src/unhandled.js +6 -6
- package/src/unhandled.spec.js +63 -0
- package/defaults/chromium.js +0 -62
- package/src/progress.html +0 -71
- package/src/proxies.js +0 -8
- package/src/report.html +0 -202
- /package/{defaults → src/defaults}/nyc.json +0 -0
package/src/tests.js
CHANGED
|
@@ -1,106 +1,149 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { start } = require('./browsers')
|
|
4
|
-
const { instrument
|
|
5
|
-
const {
|
|
6
|
-
const { join } = require('path')
|
|
7
|
-
const { copyFile, writeFile } = require('fs').promises
|
|
3
|
+
const { probe, start } = require('./browsers')
|
|
4
|
+
const { instrument } = require('./coverage')
|
|
5
|
+
const { recreateDir } = require('./tools')
|
|
8
6
|
const { globallyTimedOut } = require('./timeout')
|
|
9
|
-
const
|
|
7
|
+
const { save, generate } = require('./report')
|
|
8
|
+
const { getOutput } = require('./output')
|
|
9
|
+
const {
|
|
10
|
+
$probeUrlsStarted,
|
|
11
|
+
$probeUrlsCompleted,
|
|
12
|
+
$testPagesStarted,
|
|
13
|
+
$testPagesCompleted
|
|
14
|
+
} = require('./symbols')
|
|
15
|
+
const { UTRError } = require('./error')
|
|
10
16
|
|
|
11
|
-
async function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
job.start = new Date()
|
|
24
|
-
await instrument(job)
|
|
25
|
-
await recreateDir(job.tstReportDir)
|
|
26
|
-
await saveJob(job)
|
|
27
|
-
job.status = 'Extracting test pages'
|
|
28
|
-
job.testPageUrls = []
|
|
29
|
-
await start(job, '/' + job.testsuite)
|
|
30
|
-
if (job.testPageUrls.length === 0) {
|
|
31
|
-
output.noTestPageFound()
|
|
32
|
-
job.failed = true
|
|
33
|
-
return Promise.resolve()
|
|
34
|
-
}
|
|
35
|
-
job.testPagesStarted = 0
|
|
36
|
-
job.testPagesCompleted = 0
|
|
37
|
-
job.testPages = {}
|
|
38
|
-
job.status = 'Executing test pages'
|
|
39
|
-
delete job.failed
|
|
40
|
-
const promises = []
|
|
41
|
-
for (let i = 0; i < Math.min(job.parallel, job.testPageUrls.length); ++i) {
|
|
42
|
-
promises.push(runTestPage(job))
|
|
43
|
-
}
|
|
44
|
-
return Promise.all(promises)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function runTestPage (job) {
|
|
48
|
-
const { length } = job.testPageUrls
|
|
49
|
-
if (job.testPagesCompleted === length) {
|
|
50
|
-
return generateReport(job)
|
|
17
|
+
async function run (task, job) {
|
|
18
|
+
const {
|
|
19
|
+
urlsMember,
|
|
20
|
+
startedMember,
|
|
21
|
+
completedMember,
|
|
22
|
+
method
|
|
23
|
+
} = task
|
|
24
|
+
const output = getOutput(job)
|
|
25
|
+
const urls = job[urlsMember]
|
|
26
|
+
const { length } = urls
|
|
27
|
+
if (job[completedMember] === length) {
|
|
28
|
+
return
|
|
51
29
|
}
|
|
52
|
-
if (job
|
|
30
|
+
if (job[startedMember] === length) {
|
|
53
31
|
return
|
|
54
32
|
}
|
|
55
|
-
const index = job
|
|
56
|
-
const url =
|
|
33
|
+
const index = job[startedMember]++
|
|
34
|
+
const url = urls[index]
|
|
57
35
|
if (globallyTimedOut(job)) {
|
|
58
36
|
output.globalTimeout(url)
|
|
37
|
+
job.failed = true
|
|
59
38
|
} else if (job.failFast && job.failed) {
|
|
60
39
|
output.failFast(url)
|
|
61
40
|
} else {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
41
|
+
try {
|
|
42
|
+
await method(job, url)
|
|
43
|
+
} catch (error) {
|
|
44
|
+
job.failed = true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
++job[completedMember]
|
|
48
|
+
return run(task, job)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function probeUrl (job, url) {
|
|
52
|
+
const output = getOutput(job)
|
|
53
|
+
try {
|
|
54
|
+
let scripts
|
|
55
|
+
if (job.mode === 'url' && job.browserCapabilities.scripts) {
|
|
56
|
+
scripts = [
|
|
57
|
+
'post.js',
|
|
58
|
+
'qunit-redirect.js'
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
await start(job, url, scripts)
|
|
62
|
+
} catch (error) {
|
|
63
|
+
output.startFailed(url, error)
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function runTestPage (job, url) {
|
|
69
|
+
const output = getOutput(job)
|
|
70
|
+
try {
|
|
71
|
+
let scripts
|
|
72
|
+
if (job.mode === 'url' && job.browserCapabilities.scripts) {
|
|
73
|
+
scripts = [
|
|
74
|
+
'post.js',
|
|
75
|
+
'qunit-intercept.js',
|
|
76
|
+
'qunit-hooks.js'
|
|
77
|
+
]
|
|
67
78
|
}
|
|
79
|
+
await start(job, url, scripts)
|
|
80
|
+
} catch (error) {
|
|
81
|
+
output.startFailed(url, error)
|
|
82
|
+
throw error
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parallelize (task, job) {
|
|
87
|
+
const {
|
|
88
|
+
urlsMember,
|
|
89
|
+
completedMember,
|
|
90
|
+
startedMember
|
|
91
|
+
} = task
|
|
92
|
+
job[startedMember] = 0
|
|
93
|
+
job[completedMember] = 0
|
|
94
|
+
const max = Math.min(job.parallel, job[urlsMember].length)
|
|
95
|
+
const promises = []
|
|
96
|
+
for (let i = 0; i < max; ++i) {
|
|
97
|
+
promises.push(run(task, job))
|
|
68
98
|
}
|
|
69
|
-
|
|
70
|
-
return runTestPage(job)
|
|
99
|
+
return Promise.all(promises)
|
|
71
100
|
}
|
|
72
101
|
|
|
73
|
-
async function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
job.failed
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
async function process (job) {
|
|
103
|
+
const output = getOutput(job)
|
|
104
|
+
job.start = new Date()
|
|
105
|
+
delete job.failed
|
|
106
|
+
await instrument(job)
|
|
107
|
+
await save(job)
|
|
108
|
+
job.testPageUrls = []
|
|
109
|
+
|
|
110
|
+
job.status = 'Probing urls'
|
|
111
|
+
await parallelize({
|
|
112
|
+
urlsMember: 'url',
|
|
113
|
+
startedMember: $probeUrlsStarted,
|
|
114
|
+
completedMember: $probeUrlsCompleted,
|
|
115
|
+
method: probeUrl
|
|
116
|
+
}, job)
|
|
117
|
+
|
|
118
|
+
/* istanbul ignore else */
|
|
119
|
+
if (!job.debugProbeOnly) {
|
|
120
|
+
if (job.testPageUrls.length !== 0) {
|
|
121
|
+
job.status = 'Executing test pages'
|
|
122
|
+
await parallelize({
|
|
123
|
+
urlsMember: 'testPageUrls',
|
|
124
|
+
startedMember: $testPagesStarted,
|
|
125
|
+
completedMember: $testPagesCompleted,
|
|
126
|
+
method: runTestPage
|
|
127
|
+
}, job)
|
|
128
|
+
} else if (Object.keys(job.qunitPages || []).length === 0) {
|
|
129
|
+
output.noTestPageFound()
|
|
130
|
+
job.failed = true
|
|
92
131
|
}
|
|
93
132
|
}
|
|
94
|
-
|
|
95
|
-
await
|
|
96
|
-
await copyFile(join(__dirname, 'report.html'), join(job.tstReportDir, 'report.html'))
|
|
97
|
-
await generateCoverageReport(job)
|
|
98
|
-
output.timeSpent(job.start)
|
|
99
|
-
job.status = 'Done'
|
|
133
|
+
|
|
134
|
+
await generate(job)
|
|
100
135
|
}
|
|
101
136
|
|
|
102
|
-
module.exports =
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
module.exports = {
|
|
138
|
+
async execute (job) {
|
|
139
|
+
await recreateDir(job.reportDir)
|
|
140
|
+
getOutput(job).version()
|
|
141
|
+
await probe(job)
|
|
142
|
+
if (job.mode !== 'url') {
|
|
143
|
+
job.url = [`http://localhost:${job.port}/${job.testsuite}`]
|
|
144
|
+
} else if (!job.browserCapabilities.scripts) {
|
|
145
|
+
throw UTRError.BROWSER_MISS_SCRIPTS_CAPABILITY()
|
|
146
|
+
}
|
|
147
|
+
return process(job)
|
|
105
148
|
}
|
|
106
149
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { getPageTimeout, globallyTimedOut } = require('./timeout')
|
|
2
|
+
|
|
3
|
+
describe('src/timeout', () => {
|
|
4
|
+
describe('getPageTimeout', () => {
|
|
5
|
+
it('returns 0 when no timeout is defined', () => {
|
|
6
|
+
expect(getPageTimeout({})).toStrictEqual(0)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('returns pageTimeout when no globalTimeout is defined', () => {
|
|
10
|
+
expect(getPageTimeout({ pageTimeout: 123 })).toStrictEqual(123)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('returns pageTimeout if smaller than globalTimeout', () => {
|
|
14
|
+
expect(getPageTimeout({ pageTimeout: 123, globalTimeout: 1000, start: new Date() })).toStrictEqual(123)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('returns reminder of the globalTimeout', () => {
|
|
18
|
+
expect(getPageTimeout({ globalTimeout: 10, start: new Date() })).not.toStrictEqual(0)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('returns reminder of the globalTimeout if smaller than pageTimeout', () => {
|
|
22
|
+
expect(getPageTimeout({ pageTimeout: 123, globalTimeout: 10, start: new Date() })).not.toStrictEqual(123)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('globallyTimedOut', () => {
|
|
27
|
+
it('returns false if no globalTimeout', () => {
|
|
28
|
+
expect(globallyTimedOut({})).toStrictEqual(false)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('returns false if not globally timed out', () => {
|
|
32
|
+
expect(globallyTimedOut({ globalTimeout: 1000, start: new Date() })).toStrictEqual(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('returns true if globally timed out', () => {
|
|
36
|
+
expect(globallyTimedOut({ globalTimeout: 10, start: new Date(Date.now() - 1000) })).toStrictEqual(true)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
})
|
package/src/tools.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fsPromises = require('fs').promises
|
|
4
4
|
const { mkdir, stat } = fsPromises
|
|
5
|
+
const { createHash } = require('crypto')
|
|
5
6
|
|
|
6
7
|
let rm
|
|
7
8
|
/* istanbul ignore next */ // Hard to test both in the same run
|
|
@@ -13,6 +14,19 @@ if (process.version > 'v14.14') {
|
|
|
13
14
|
|
|
14
15
|
const recursive = { recursive: true }
|
|
15
16
|
|
|
17
|
+
const stripUrlHash = url => url.split('#')[0]
|
|
18
|
+
|
|
19
|
+
const filename = url => {
|
|
20
|
+
const hash = createHash('shake256', {
|
|
21
|
+
outputLength: 8
|
|
22
|
+
})
|
|
23
|
+
hash.update(stripUrlHash(url))
|
|
24
|
+
return hash.digest('base64')
|
|
25
|
+
.replace(/=/g, '')
|
|
26
|
+
.replace(/\+/g, '_')
|
|
27
|
+
.replace(/\//g, '$')
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
const cleanDir = async dir => {
|
|
17
31
|
try {
|
|
18
32
|
await stat(dir)
|
|
@@ -22,12 +36,105 @@ const cleanDir = async dir => {
|
|
|
22
36
|
}
|
|
23
37
|
}
|
|
24
38
|
|
|
39
|
+
const $op = Symbol('pad.op')
|
|
40
|
+
const $x = Symbol('pad.x')
|
|
41
|
+
const $lt = Symbol('pad.lt')
|
|
42
|
+
const $w = Symbol('pad.w')
|
|
43
|
+
function pad (width) {
|
|
44
|
+
if (!width) {
|
|
45
|
+
width = process.stdout.columns || 80
|
|
46
|
+
}
|
|
47
|
+
const ops = {
|
|
48
|
+
[$x] (widthLeft) {
|
|
49
|
+
return ''.padStart(widthLeft, this.text)
|
|
50
|
+
},
|
|
51
|
+
[$lt] (widthLeft) {
|
|
52
|
+
const { text, padding } = this
|
|
53
|
+
if (text.length <= widthLeft) {
|
|
54
|
+
return text.padEnd(widthLeft, padding)
|
|
55
|
+
}
|
|
56
|
+
return '...' + text.substring(text.length - widthLeft + 3)
|
|
57
|
+
},
|
|
58
|
+
[$w] (widthLeft, result, opIndex) {
|
|
59
|
+
const { text } = this
|
|
60
|
+
if (text.length < widthLeft && !text.includes('\n')) {
|
|
61
|
+
return text.padEnd(widthLeft, ' ')
|
|
62
|
+
}
|
|
63
|
+
const lines = []
|
|
64
|
+
text.split(/\r?\n/).forEach(line => {
|
|
65
|
+
if (line.length <= widthLeft) {
|
|
66
|
+
lines.push(line.padEnd(widthLeft, ' '))
|
|
67
|
+
} else {
|
|
68
|
+
for (let offset = 0; offset < line.length; offset += widthLeft - 1) {
|
|
69
|
+
const part = line.slice(offset, offset + widthLeft - 1)
|
|
70
|
+
if (part.length < widthLeft - 1) {
|
|
71
|
+
lines.push(part.padEnd(widthLeft, ' '))
|
|
72
|
+
} else {
|
|
73
|
+
lines.push(`${part}↵`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
const before = result.slice(0, opIndex).join('')
|
|
79
|
+
const after = result.slice(opIndex + 1).join('')
|
|
80
|
+
return lines.join(after + '\n' + before)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return (strings, ...values) => {
|
|
84
|
+
const result = []
|
|
85
|
+
let op
|
|
86
|
+
let opIndex
|
|
87
|
+
const length = strings.reduce((total, string, index) => {
|
|
88
|
+
result.push(string)
|
|
89
|
+
total += string.length
|
|
90
|
+
let value = values[index]
|
|
91
|
+
if (value === null || value === undefined) {
|
|
92
|
+
return total
|
|
93
|
+
}
|
|
94
|
+
if (value[$op]) {
|
|
95
|
+
if (opIndex !== undefined) {
|
|
96
|
+
throw new Error('Only one operator is allowed')
|
|
97
|
+
}
|
|
98
|
+
op = value
|
|
99
|
+
opIndex = result.length
|
|
100
|
+
result.push(value)
|
|
101
|
+
} else {
|
|
102
|
+
if (typeof value !== 'string') {
|
|
103
|
+
value = value.toString()
|
|
104
|
+
}
|
|
105
|
+
result.push(value)
|
|
106
|
+
total += value.length
|
|
107
|
+
}
|
|
108
|
+
return total
|
|
109
|
+
}, 0)
|
|
110
|
+
if (op !== undefined) {
|
|
111
|
+
const widthLeft = width - length
|
|
112
|
+
result[opIndex] = ops[op[$op]].call(op, widthLeft, result, opIndex)
|
|
113
|
+
}
|
|
114
|
+
return result.join('')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pad.x = (text) => ({ [$op]: $x, text })
|
|
119
|
+
pad.lt = (text, padding = ' ') => ({ [$op]: $lt, text, padding })
|
|
120
|
+
pad.w = (text) => ({ [$op]: $w, text })
|
|
121
|
+
|
|
25
122
|
module.exports = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.replace(/%([0-9a-z]{2})/ig, (match, hexa) => `_${hexa}_`),
|
|
123
|
+
stripUrlHash,
|
|
124
|
+
filename,
|
|
29
125
|
cleanDir,
|
|
30
126
|
createDir: dir => mkdir(dir, recursive),
|
|
31
127
|
recreateDir: dir => cleanDir(dir).then(() => mkdir(dir, recursive)),
|
|
32
|
-
|
|
128
|
+
extractPageUrl: headers => headers['x-page-url'],
|
|
129
|
+
allocPromise () {
|
|
130
|
+
let resolve
|
|
131
|
+
let reject
|
|
132
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
133
|
+
resolve = _resolve
|
|
134
|
+
reject = _reject
|
|
135
|
+
})
|
|
136
|
+
return { promise, resolve, reject }
|
|
137
|
+
},
|
|
138
|
+
noop () {},
|
|
139
|
+
pad
|
|
33
140
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const { filename, noop, pad } = require('./tools')
|
|
2
|
+
|
|
3
|
+
describe('src/tools', () => {
|
|
4
|
+
describe('filename', () => {
|
|
5
|
+
const baseUrl = 'http://localhost:8085/test.html'
|
|
6
|
+
|
|
7
|
+
it('generates unique IDs for different search param', () => {
|
|
8
|
+
const id1 = filename(baseUrl + '?param1')
|
|
9
|
+
const id2 = filename(baseUrl + '?param2')
|
|
10
|
+
expect(id1).not.toBe(id2)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('generates similar IDs for different hashes', () => {
|
|
14
|
+
const id1 = filename(baseUrl + '#param1')
|
|
15
|
+
const id2 = filename(baseUrl + '#param2')
|
|
16
|
+
expect(id1).toBe(id2)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('noop', () => {
|
|
21
|
+
it('is an empty function', () => {
|
|
22
|
+
expect(noop()).toBeUndefined()
|
|
23
|
+
expect(() => noop()).not.toThrow()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('pad', () => {
|
|
28
|
+
it('works if no operator is given', () => {
|
|
29
|
+
expect(pad(20)`abc`).toStrictEqual('abc')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('fails if several operators are given', () => {
|
|
33
|
+
expect(() => pad()`${pad.x('-')}${pad.x('+')}`).toThrow()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('extends a string (begin)', () => {
|
|
37
|
+
expect(pad(20)`${pad.x('-')}12`).toStrictEqual('------------------12')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('extends a string (middle)', () => {
|
|
41
|
+
expect(pad(20)`1${pad.x('-')}2`).toStrictEqual('1------------------2')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('extends a string (multiple interpolation)', () => {
|
|
45
|
+
expect(pad(20)`1${'3'}${pad.x('-')}${'4'}2`).toStrictEqual('13----------------42')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('extends a string (end)', () => {
|
|
49
|
+
expect(pad(20)`12${pad.x('-')}`).toStrictEqual('12------------------')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('extends a string (multiple chars)', () => {
|
|
53
|
+
expect(pad(20)`1${pad.x('-+')}2`).toStrictEqual('1-+-+-+-+-+-+-+-+-+2')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('left trims a string', () => {
|
|
57
|
+
expect(pad(20)`1${pad.lt('abcdefghijklmnopqrstuvwxyz')}2`).toStrictEqual('1...lmnopqrstuvwxyz2')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('left trims a small string', () => {
|
|
61
|
+
expect(pad(20)`1${pad.lt('abcdef')}2`).toStrictEqual('1abcdef 2')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('wraps text (small enough)', () => {
|
|
65
|
+
expect(pad(20)`1${pad.w('abcdef')}2`).toStrictEqual('1abcdef 2')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('wraps multiline', () => {
|
|
69
|
+
expect(pad(20)`1${pad.w('a\nb')}2`)
|
|
70
|
+
.toStrictEqual(`1a 2
|
|
71
|
+
1b 2`)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('wraps multiline / long text', () => {
|
|
75
|
+
expect(pad(20)`1${pad.w('first line\nsecond longer line to wrap\nfits exactly width')}2`)
|
|
76
|
+
.toStrictEqual(`1first line 2
|
|
77
|
+
1second longer lin↵2
|
|
78
|
+
1e to wrap 2
|
|
79
|
+
1fits exactly width2`)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('wraps multiline / long text (DOS carriage return)', () => {
|
|
83
|
+
expect(pad(20)`1${pad.w('first line\r\nsecond longer line to wrap\r\nfits exactly width')}2`)
|
|
84
|
+
.toStrictEqual(`1first line 2
|
|
85
|
+
1second longer lin↵2
|
|
86
|
+
1e to wrap 2
|
|
87
|
+
1fits exactly width2`)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
})
|
package/src/ui5.js
CHANGED
|
@@ -4,12 +4,12 @@ const { dirname, join } = require('path')
|
|
|
4
4
|
const { createWriteStream } = require('fs')
|
|
5
5
|
const { mkdir, unlink } = require('fs').promises
|
|
6
6
|
const { capture } = require('reserve')
|
|
7
|
-
const
|
|
7
|
+
const { getOutput } = require('./output')
|
|
8
8
|
|
|
9
9
|
module.exports = job => {
|
|
10
10
|
const [, hostName] = /https?:\/\/([^/]*)/.exec(job.ui5)
|
|
11
11
|
const [, version] = /(\d+\.\d+\.\d+)?$/.exec(job.ui5)
|
|
12
|
-
const cacheBase = join(job.
|
|
12
|
+
const cacheBase = join(job.cache || '', hostName.replace(':', '_'), version || '')
|
|
13
13
|
const match = /\/((?:test-)?resources\/.*)/
|
|
14
14
|
const ifCacheEnabled = (request, url, match) => job.cache ? match : false
|
|
15
15
|
const uncachable = {}
|
|
@@ -59,7 +59,7 @@ module.exports = job => {
|
|
|
59
59
|
file.end()
|
|
60
60
|
uncachable[path] = true
|
|
61
61
|
if (response.statusCode !== 404) {
|
|
62
|
-
|
|
62
|
+
getOutput(job).failedToCacheUI5resource(path, response.statusCode)
|
|
63
63
|
}
|
|
64
64
|
return unlink(cachePath)
|
|
65
65
|
})
|
package/src/unhandled.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { extractPageUrl, noop } = require('./tools')
|
|
4
4
|
const { join } = require('path')
|
|
5
5
|
const { writeFile } = require('fs')
|
|
6
|
-
const
|
|
6
|
+
const { getOutput } = require('./output')
|
|
7
7
|
|
|
8
8
|
module.exports = job => {
|
|
9
|
-
const unhandled = join(job.
|
|
9
|
+
const unhandled = join(job.reportDir, 'unhandled.txt')
|
|
10
10
|
let outputUnhandled = true
|
|
11
11
|
return [{
|
|
12
12
|
custom: ({ headers, method, url }) => {
|
|
@@ -20,12 +20,12 @@ module.exports = job => {
|
|
|
20
20
|
status = 500
|
|
21
21
|
}
|
|
22
22
|
if (outputUnhandled) {
|
|
23
|
-
|
|
23
|
+
getOutput(job).unhandled()
|
|
24
24
|
outputUnhandled = false
|
|
25
25
|
}
|
|
26
|
-
writeFile(unhandled, `${
|
|
26
|
+
writeFile(unhandled, `${extractPageUrl(headers)} ${status} ${method} ${url}\n`, {
|
|
27
27
|
flag: 'a'
|
|
28
|
-
},
|
|
28
|
+
}, noop)
|
|
29
29
|
return status
|
|
30
30
|
}
|
|
31
31
|
}]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { join } = require('path')
|
|
2
|
+
const { getOutput } = require('./output')
|
|
3
|
+
const jobFactory = require('./job')
|
|
4
|
+
const mappingFactory = require('./unhandled')
|
|
5
|
+
|
|
6
|
+
const cwd = join(__dirname, '../test/project')
|
|
7
|
+
|
|
8
|
+
describe('src/unhandled', () => {
|
|
9
|
+
let unhandled
|
|
10
|
+
let output
|
|
11
|
+
let unhandledCall
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
const job = jobFactory.fromCmdLine(cwd, [])
|
|
15
|
+
output = getOutput(job)
|
|
16
|
+
unhandledCall = 0
|
|
17
|
+
output.unhandled = () => {
|
|
18
|
+
++unhandledCall
|
|
19
|
+
}
|
|
20
|
+
unhandled = mappingFactory(job)[0].custom
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const expectedIgnores = [
|
|
24
|
+
'favicon.ico',
|
|
25
|
+
'component-preload.js',
|
|
26
|
+
'button-dbg.js',
|
|
27
|
+
'i18n_en.properties'
|
|
28
|
+
]
|
|
29
|
+
expectedIgnores.forEach(url => {
|
|
30
|
+
it(`does not log known GET patterns (${url})`, () => {
|
|
31
|
+
expect(unhandled({ method: 'GET', url })).toStrictEqual(404)
|
|
32
|
+
expect(unhandledCall).toStrictEqual(0)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const expectedWarnings = [
|
|
37
|
+
'any-mock-data-file.json',
|
|
38
|
+
'sourceFile.js'
|
|
39
|
+
]
|
|
40
|
+
expectedWarnings.forEach(url => {
|
|
41
|
+
it(`warns about 404 GET (${url})`, () => {
|
|
42
|
+
expect(unhandled({ method: 'GET', url, headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(404)
|
|
43
|
+
expect(unhandledCall).toStrictEqual(1)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
it('Warns only once', () => {
|
|
47
|
+
expectedWarnings.forEach(url => {
|
|
48
|
+
expect(unhandled({ method: 'GET', url, headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(404)
|
|
49
|
+
})
|
|
50
|
+
expect(unhandledCall).toStrictEqual(1)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('logs errors for any other verb', () => {
|
|
54
|
+
expect(unhandled({ method: 'POST', url: '/any_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
|
|
55
|
+
expect(unhandledCall).toStrictEqual(1)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('logs errors only once', () => {
|
|
59
|
+
expect(unhandled({ method: 'POST', url: '/any_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
|
|
60
|
+
expect(unhandled({ method: 'POST', url: '/any_other_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
|
|
61
|
+
expect(unhandledCall).toStrictEqual(1)
|
|
62
|
+
})
|
|
63
|
+
})
|
package/defaults/chromium.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const puppeteer = require('puppeteer')
|
|
4
|
-
const { join } = require('path')
|
|
5
|
-
const { createWriteStream } = require('fs')
|
|
6
|
-
|
|
7
|
-
const [url, reportDir] = process.argv.slice(2).filter(arg => !arg.startsWith('--'))
|
|
8
|
-
const headless = !process.argv.some(arg => arg === '--visible')
|
|
9
|
-
|
|
10
|
-
let browser
|
|
11
|
-
let page
|
|
12
|
-
let consoleStream
|
|
13
|
-
|
|
14
|
-
if (reportDir) {
|
|
15
|
-
consoleStream = createWriteStream(join(reportDir, 'console.txt'), 'utf-8')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
process.on('message', async message => {
|
|
19
|
-
try {
|
|
20
|
-
if (message.command === 'stop') {
|
|
21
|
-
await browser.close()
|
|
22
|
-
process.exit(0)
|
|
23
|
-
} else if (message.command === 'screenshot') {
|
|
24
|
-
if (reportDir && page) {
|
|
25
|
-
await page.screenshot({ path: join(reportDir, message.filename) })
|
|
26
|
-
process.send(message)
|
|
27
|
-
}
|
|
28
|
-
} else if (message.command === 'capabilities') {
|
|
29
|
-
process.send({
|
|
30
|
-
command: 'capabilities',
|
|
31
|
-
screenshot: true,
|
|
32
|
-
consoleLog: true
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
} catch (e) {
|
|
36
|
-
console.error(e)
|
|
37
|
-
process.exit(-2)
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
async function main () {
|
|
42
|
-
browser = await puppeteer.launch({
|
|
43
|
-
headless,
|
|
44
|
-
defaultViewport: null,
|
|
45
|
-
args: [
|
|
46
|
-
url,
|
|
47
|
-
'--start-maximized',
|
|
48
|
-
'--no-sandbox',
|
|
49
|
-
'--disable-gpu',
|
|
50
|
-
'--disable-extensions'
|
|
51
|
-
]
|
|
52
|
-
})
|
|
53
|
-
page = (await browser.pages())[0]
|
|
54
|
-
if (consoleStream) {
|
|
55
|
-
page.on('console', message => consoleStream.write(`${message.type().substr(0, 3).toUpperCase()} ${message.text()}\n`))
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
main().catch(e => {
|
|
60
|
-
console.error(e)
|
|
61
|
-
process.exit(-1)
|
|
62
|
-
})
|