ui5-test-runner 4.5.1 → 5.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 +9 -12
- package/package.json +3 -3
- package/src/browsers.js +3 -2
- package/src/coverage.js +152 -38
- package/src/defaults/junit-xml-report.js +2 -1
- package/src/defaults/puppeteer.js +15 -3
- package/src/defaults/scan-ui5.js +20 -0
- package/src/defaults/selenium-webdriver/edge.js +1 -2
- package/src/defaults/selenium-webdriver/firefox.js +2 -3
- package/src/defaults/selenium-webdriver.js +1 -1
- package/src/defaults/webdriverio.js +89 -0
- package/src/job.js +6 -3
- package/src/output.js +11 -3
- package/src/reserve.js +2 -1
package/README.md
CHANGED
|
@@ -34,18 +34,15 @@ A self-sufficient test runner for UI5 applications enabling parallel execution o
|
|
|
34
34
|
|
|
35
35
|
## ⚠️ Breaking changes
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* Dependencies are installed **on demand**
|
|
47
|
-
* Browser instantiation command evolved in an **incompatible way** (see [documentation](https://arnaudbuchholz.github.io/ui5-test-runner/browser.html)).
|
|
48
|
-
* Output is different (report, traces)
|
|
37
|
+
| Version | Reason |
|
|
38
|
+
|-|-|
|
|
39
|
+
| **5**.0.0 | • Some coverage reports now includes **all** files, leading to a potential decrease of coverage |
|
|
40
|
+
| **4**.0.0 | • Drop support of Node.js 16 |
|
|
41
|
+
| **3**.0.0 | • Drop support of Node.js 14 |
|
|
42
|
+
| **2**.0.0 | • Command line **parameters** as well as configuration file **syntax** have changed |
|
|
43
|
+
|| • Dependencies are installed **on demand** |
|
|
44
|
+
|| • Browser instantiation command evolved in an **incompatible way** (see [documentation](https://arnaudbuchholz.github.io/ui5-test-runner/browser.html)) |
|
|
45
|
+
|| • Output is different (report, traces) |
|
|
49
46
|
|
|
50
47
|
## ✒ Contributors
|
|
51
48
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@openui5/types": "^1.123.0",
|
|
54
|
-
"@ui5/cli": "^3.
|
|
54
|
+
"@ui5/cli": "^3.10.0",
|
|
55
55
|
"@ui5/middleware-code-coverage": "^1.1.1",
|
|
56
56
|
"dotenv": "^16.4.5",
|
|
57
57
|
"jest": "^29.7.0",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"standard": "^17.1.0",
|
|
62
62
|
"start-server-and-test": "^2.0.3",
|
|
63
63
|
"typescript": "^5.4.5",
|
|
64
|
-
"ui5-tooling-transpile": "^3.
|
|
64
|
+
"ui5-tooling-transpile": "^3.4.0"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
67
|
"fsevents": "^2.3.3"
|
package/src/browsers.js
CHANGED
|
@@ -116,8 +116,9 @@ async function start (job, url, scripts = []) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
if (resolvedScripts.length) {
|
|
119
|
-
resolvedScripts.unshift(`
|
|
120
|
-
|
|
119
|
+
resolvedScripts.unshift(`(function () {
|
|
120
|
+
window['ui5-test-runner/base-host'] = 'http://localhost:${job.port}'
|
|
121
|
+
}())`)
|
|
121
122
|
}
|
|
122
123
|
const progress = newProgress(job, url)
|
|
123
124
|
const pageBrowser = {
|
package/src/coverage.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { join, dirname, isAbsolute } = require('path')
|
|
3
|
+
const { join, dirname, isAbsolute, relative, sep } = require('path')
|
|
4
4
|
const { fork } = require('child_process')
|
|
5
5
|
const { cleanDir, createDir, filename, download, allocPromise } = require('./tools')
|
|
6
6
|
const { readdir, readFile, stat, writeFile, access, constants } = require('fs').promises
|
|
7
7
|
const { Readable } = require('stream')
|
|
8
|
-
const { getOutput } = require('./output')
|
|
8
|
+
const { getOutput, newProgress } = require('./output')
|
|
9
9
|
const { resolvePackage } = require('./npm')
|
|
10
10
|
const { promisify } = require('util')
|
|
11
11
|
const { UTRError } = require('./error')
|
|
@@ -64,7 +64,7 @@ async function instrument (job) {
|
|
|
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())
|
|
67
|
-
settings.cwd = job.
|
|
67
|
+
settings.cwd = job.webapp
|
|
68
68
|
if (!settings.exclude) {
|
|
69
69
|
settings.exclude = []
|
|
70
70
|
}
|
|
@@ -75,6 +75,7 @@ async function instrument (job) {
|
|
|
75
75
|
settings.exclude.push(join(job.reportDir, '**'))
|
|
76
76
|
settings.exclude.push(join(job.coverageReportDir, '**'))
|
|
77
77
|
await writeFile(job[$nycSettingsPath], JSON.stringify(settings))
|
|
78
|
+
job.nycSettings = settings
|
|
78
79
|
if (job.mode === 'url') {
|
|
79
80
|
if (!job[$remoteOnLegacy]) {
|
|
80
81
|
job[$coverageRemote] = true
|
|
@@ -86,6 +87,92 @@ async function instrument (job) {
|
|
|
86
87
|
await nyc(job, 'instrument', job.webapp, join(job.coverageTempDir, 'instrumented'), '--nycrc-path', job[$nycSettingsPath])
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
function getUrlOrigin (job) {
|
|
91
|
+
const { origin } = new URL(job.url[0])
|
|
92
|
+
if (job.url.some(url => new URL(url).origin !== origin)) {
|
|
93
|
+
getOutput(job).assumingOneOrigin()
|
|
94
|
+
}
|
|
95
|
+
return origin
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function buildAllIndex (job) {
|
|
99
|
+
async function scanFs (path, onFolder, onFile) {
|
|
100
|
+
const items = await readdir(path)
|
|
101
|
+
await onFolder(items.length)
|
|
102
|
+
for (const item of items) {
|
|
103
|
+
const itemPath = join(path, item)
|
|
104
|
+
const itemStat = await stat(itemPath)
|
|
105
|
+
if (itemStat.isDirectory()) {
|
|
106
|
+
await scanFs(itemPath, onFolder, onFile)
|
|
107
|
+
} else {
|
|
108
|
+
await onFile(itemPath, (await readFile(itemPath)).toString())
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const output = getOutput(job)
|
|
114
|
+
output.debug('coverage', 'Build index for all files...')
|
|
115
|
+
const progress = newProgress(job, 'Build index for all files', 1, 0)
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const index = []
|
|
119
|
+
let scan
|
|
120
|
+
let start
|
|
121
|
+
if (job.mode === 'legacy' || job[$remoteOnLegacy]) {
|
|
122
|
+
scan = scanFs
|
|
123
|
+
start = join(job.coverageTempDir, 'instrumented')
|
|
124
|
+
} else {
|
|
125
|
+
scan = require(job.coverageRemoteScanner)
|
|
126
|
+
start = getUrlOrigin(job)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await scan(
|
|
130
|
+
start,
|
|
131
|
+
count => {
|
|
132
|
+
progress.total += count
|
|
133
|
+
++progress.count
|
|
134
|
+
},
|
|
135
|
+
async (file, source) => {
|
|
136
|
+
if (file.endsWith('.js') || file.endsWith('.ts')) {
|
|
137
|
+
output.debug('coverage', file)
|
|
138
|
+
try {
|
|
139
|
+
const coverageData = source
|
|
140
|
+
.match(/coverageData\s*=\s*({[^;]*});/)[1]
|
|
141
|
+
.replace(/([^"])(\w+):/g, (_, before, name) => `${before}"${name}":`)
|
|
142
|
+
const [, coveragePath] = coverageData.match(/"path"\s*:\s*"([^"]+)"/)
|
|
143
|
+
const UNDEFINED = '__undefined__'
|
|
144
|
+
const validatedCoverageData = JSON.stringify(
|
|
145
|
+
JSON.parse(coverageData.replace(/\bundefined\b/g, `"${UNDEFINED}"`)),
|
|
146
|
+
(key, value) => {
|
|
147
|
+
if (value === UNDEFINED) {
|
|
148
|
+
return undefined
|
|
149
|
+
}
|
|
150
|
+
return value
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
index.push(`"${coveragePath}": ${validatedCoverageData}`)
|
|
154
|
+
} catch (e) {
|
|
155
|
+
output.debug('coverage', `Error when extracting all coverage for ${file}`, e)
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
output.debug('coverage', `Ignore all coverage for ${file}`)
|
|
159
|
+
}
|
|
160
|
+
++progress.count
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
if (index.length === 0) {
|
|
164
|
+
output.noInfoForAllCoverage()
|
|
165
|
+
} else {
|
|
166
|
+
await writeFile(join(job.coverageTempDir, 'all-index.json'), `{${index.join(',')}}`)
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
output.genericError(e)
|
|
170
|
+
output.noInfoForAllCoverage()
|
|
171
|
+
} finally {
|
|
172
|
+
progress.done()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
89
176
|
async function getReadableSource (job, pathOrUrl) {
|
|
90
177
|
if (isAbsolute(pathOrUrl)) {
|
|
91
178
|
try {
|
|
@@ -99,16 +186,59 @@ async function getReadableSource (job, pathOrUrl) {
|
|
|
99
186
|
return filePath
|
|
100
187
|
} catch (e) {}
|
|
101
188
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
189
|
+
const origin = getUrlOrigin(job)
|
|
190
|
+
if (!job.coverageSourceDir) {
|
|
191
|
+
job.coverageSourceDir = join(job.coverageTempDir, 'sources')
|
|
192
|
+
}
|
|
193
|
+
const filePath = join(job.coverageSourceDir, pathOrUrl)
|
|
105
194
|
await download(origin + pathOrUrl, filePath)
|
|
106
195
|
return filePath
|
|
107
196
|
} catch (e) {}
|
|
108
197
|
}
|
|
109
198
|
|
|
199
|
+
async function checkAllSourcesAreAvailable (job, coverageFilename) {
|
|
200
|
+
const output = getOutput(job)
|
|
201
|
+
job.status = 'Checking remote source files'
|
|
202
|
+
output.debug('coverage', 'Checking remote source files...')
|
|
203
|
+
const coverageData = require(coverageFilename)
|
|
204
|
+
const filenames = Object.keys(coverageData)
|
|
205
|
+
let changes = 0
|
|
206
|
+
let basePath
|
|
207
|
+
for (const filename of filenames) {
|
|
208
|
+
const fileData = coverageData[filename]
|
|
209
|
+
const filePath = await getReadableSource(job, fileData.path)
|
|
210
|
+
if (!filePath) {
|
|
211
|
+
// TODO this will compromise coverage report generation
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
if (filePath && filePath !== fileData.path) {
|
|
215
|
+
fileData.path = filePath
|
|
216
|
+
++changes
|
|
217
|
+
}
|
|
218
|
+
const fileFolder = dirname(filePath)
|
|
219
|
+
if (basePath === undefined) {
|
|
220
|
+
basePath = fileFolder
|
|
221
|
+
} else {
|
|
222
|
+
const diff = relative(basePath, fileFolder).split(sep)
|
|
223
|
+
while (diff.shift() === '..') {
|
|
224
|
+
basePath = dirname(basePath)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (basePath !== job.nycSettings.cwd) {
|
|
229
|
+
job.nycSettings.cwd = basePath
|
|
230
|
+
await writeFile(job[$nycSettingsPath], JSON.stringify(job.nycSettings))
|
|
231
|
+
}
|
|
232
|
+
if (changes > 0) {
|
|
233
|
+
await writeFile(coverageFilename, JSON.stringify(coverageData))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
110
237
|
async function generateCoverageReport (job) {
|
|
111
238
|
job.status = 'Generating coverage report'
|
|
239
|
+
if (job.nycSettings.all) {
|
|
240
|
+
await buildAllIndex(job)
|
|
241
|
+
}
|
|
112
242
|
const output = getOutput(job)
|
|
113
243
|
output.debug('coverage', 'Generating coverage report...')
|
|
114
244
|
await cleanDir(job.coverageReportDir)
|
|
@@ -116,23 +246,8 @@ async function generateCoverageReport (job) {
|
|
|
116
246
|
await createDir(coverageMergedDir)
|
|
117
247
|
const coverageFilename = join(coverageMergedDir, 'coverage.json')
|
|
118
248
|
await nyc(job, 'merge', job.coverageTempDir, coverageFilename)
|
|
119
|
-
if (job[$coverageRemote]
|
|
120
|
-
job
|
|
121
|
-
output.debug('coverage', 'Checking remote source files...')
|
|
122
|
-
const coverageData = require(coverageFilename)
|
|
123
|
-
const filenames = Object.keys(coverageData)
|
|
124
|
-
let changes = 0
|
|
125
|
-
for (const filename of filenames) {
|
|
126
|
-
const fileData = coverageData[filename]
|
|
127
|
-
const filePath = await getReadableSource(job, fileData.path)
|
|
128
|
-
if (filePath && filePath !== fileData.path) {
|
|
129
|
-
fileData.path = filePath
|
|
130
|
-
++changes
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (changes > 0) {
|
|
134
|
-
await writeFile(coverageFilename, JSON.stringify(coverageData))
|
|
135
|
-
}
|
|
249
|
+
if (job[$coverageRemote]) {
|
|
250
|
+
await checkAllSourcesAreAvailable(job, coverageFilename)
|
|
136
251
|
}
|
|
137
252
|
const reporters = job.coverageReporters.map(reporter => `--reporter=${reporter}`)
|
|
138
253
|
if (!job.coverageReporters.includes('text')) {
|
|
@@ -201,7 +316,7 @@ module.exports = {
|
|
|
201
316
|
return [{
|
|
202
317
|
match: /(.*\.js)(\?.*)?$/,
|
|
203
318
|
custom: async (request, response, url) => {
|
|
204
|
-
if (!url.match(job.coverageProxyInclude) || url.match(
|
|
319
|
+
if (!url.match(job.coverageProxyInclude) || url.match(job.coverageProxyExclude)) {
|
|
205
320
|
getOutput(job).debug('coverage', 'coverage_proxy ignore', url)
|
|
206
321
|
return
|
|
207
322
|
}
|
|
@@ -210,21 +325,20 @@ module.exports = {
|
|
|
210
325
|
await access(instrumentedSourcePath, constants.R_OK)
|
|
211
326
|
return
|
|
212
327
|
} catch (e) {}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
328
|
+
if (!sources[url]) {
|
|
329
|
+
sources[url] = (async () => {
|
|
330
|
+
const sourcePath = await getReadableSource(job, url)
|
|
331
|
+
getOutput(job).debug('coverage', 'coverage_proxy instrument', url, sourcePath)
|
|
332
|
+
if (sourcePath) {
|
|
333
|
+
const source = (await readFile(sourcePath)).toString()
|
|
334
|
+
const instrumentedSource = await instrument(source, sourcePath)
|
|
335
|
+
await createDir(dirname(instrumentedSourcePath))
|
|
336
|
+
await writeFile(instrumentedSourcePath, instrumentedSource)
|
|
337
|
+
delete sources[url]
|
|
338
|
+
}
|
|
339
|
+
})()
|
|
217
340
|
}
|
|
218
|
-
sources[url]
|
|
219
|
-
const sourcePath = await getReadableSource(job, url)
|
|
220
|
-
getOutput(job).debug('coverage', 'coverage_proxy instrument', url, sourcePath)
|
|
221
|
-
if (sourcePath) {
|
|
222
|
-
const source = (await readFile(sourcePath)).toString()
|
|
223
|
-
const instrumentedSource = await instrument(source, sourcePath)
|
|
224
|
-
await createDir(dirname(instrumentedSourcePath))
|
|
225
|
-
await writeFile(instrumentedSourcePath, instrumentedSource)
|
|
226
|
-
}
|
|
227
|
-
})()
|
|
341
|
+
await sources[url]
|
|
228
342
|
}
|
|
229
343
|
},
|
|
230
344
|
instrumentedMapping,
|
|
@@ -39,7 +39,8 @@ async function main () {
|
|
|
39
39
|
time = (new Date(test.end) - new Date(test.start)) / 1000
|
|
40
40
|
}
|
|
41
41
|
o(` <testcase
|
|
42
|
-
name="${xmlEscape(test.name)}"
|
|
42
|
+
name="${xmlEscape(test.name)}"
|
|
43
|
+
classname="${xmlEscape(module.name)}" ${
|
|
43
44
|
time === undefined
|
|
44
45
|
? ''
|
|
45
46
|
: `
|
|
@@ -8,17 +8,22 @@ require('./browser')({
|
|
|
8
8
|
name: 'puppeteer',
|
|
9
9
|
options: [
|
|
10
10
|
['--visible [flag]', 'Show the browser', false],
|
|
11
|
+
['--firefox [flag]', 'Use firefox instead of chrome', false],
|
|
12
|
+
['--binary <binary>', 'Binary path'],
|
|
11
13
|
['-w, --viewport-width <width>', 'Viewport width', 1920],
|
|
12
14
|
['-h, --viewport-height <height>', 'Viewport height', 1080],
|
|
13
15
|
['-l, --language <lang...>', 'Language(s)', ['en-US']],
|
|
14
16
|
['-u, --unsecure', 'Disable security features', false],
|
|
15
17
|
['--basic-auth-username <username>', 'Username for basic authentication', ''],
|
|
16
18
|
['--basic-auth-password <password>', 'Password for basic authentication', '']
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async capabilities ({ settings, options }) {
|
|
23
|
+
return {
|
|
19
24
|
modules: ['puppeteer'],
|
|
20
25
|
screenshot: '.png',
|
|
21
|
-
scripts:
|
|
26
|
+
scripts: !options.firefox,
|
|
22
27
|
traces: ['console', 'network']
|
|
23
28
|
}
|
|
24
29
|
},
|
|
@@ -68,7 +73,14 @@ require('./browser')({
|
|
|
68
73
|
)
|
|
69
74
|
}
|
|
70
75
|
|
|
76
|
+
let product
|
|
77
|
+
if (options.firefox) {
|
|
78
|
+
product = 'firefox'
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
browser = await puppeteer.launch({
|
|
82
|
+
product,
|
|
83
|
+
executablePath: options.binary,
|
|
72
84
|
headless: options.visible ? false : 'new',
|
|
73
85
|
defaultViewport: null,
|
|
74
86
|
args
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = async function scanUI5 (url, onFolder, onFile) {
|
|
2
|
+
if (url.match(/\/((?:test-)?resources\/.*)/)) {
|
|
3
|
+
return // ignore UI5 resources
|
|
4
|
+
}
|
|
5
|
+
const html = await (await fetch(url)).text()
|
|
6
|
+
const items = [...html.matchAll(/<a href="([^"]+)" class="icon/ig)]
|
|
7
|
+
.map(([_, item]) => item)
|
|
8
|
+
.filter(item => item.endsWith('/') || item.endsWith('.js') || item.endsWith('.ts'))
|
|
9
|
+
await onFolder(items.length)
|
|
10
|
+
for (const item of items) {
|
|
11
|
+
const itemUrl = new URL(item, url).toString()
|
|
12
|
+
if (item.endsWith('/')) {
|
|
13
|
+
await scanUI5(itemUrl, onFolder, onFile)
|
|
14
|
+
} else if (item.endsWith('.ts')) {
|
|
15
|
+
await onFile(itemUrl, await (await fetch(itemUrl.replace(/\.ts$/, '.js'))).text())
|
|
16
|
+
} else {
|
|
17
|
+
await onFile(itemUrl, await (await fetch(itemUrl + '?instrument=true')).text())
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -4,8 +4,7 @@ module.exports = async ({
|
|
|
4
4
|
seleniumWebdriver,
|
|
5
5
|
settings,
|
|
6
6
|
options,
|
|
7
|
-
loggingPreferences
|
|
8
|
-
$capabilities
|
|
7
|
+
loggingPreferences
|
|
9
8
|
}) => {
|
|
10
9
|
const { Browser, Builder } = seleniumWebdriver
|
|
11
10
|
const edge = require(join(settings.modules['selenium-webdriver'], 'edge'))
|
|
@@ -4,15 +4,14 @@ module.exports = async ({
|
|
|
4
4
|
seleniumWebdriver,
|
|
5
5
|
settings,
|
|
6
6
|
options,
|
|
7
|
-
loggingPreferences
|
|
8
|
-
$capabilities
|
|
7
|
+
loggingPreferences
|
|
9
8
|
}) => {
|
|
10
9
|
const { Browser, Builder } = seleniumWebdriver
|
|
11
10
|
const firefox = require(join(settings.modules['selenium-webdriver'], 'firefox'))
|
|
12
11
|
|
|
13
12
|
const firefoxOptions = new firefox.Options()
|
|
14
13
|
if (!options.visible) {
|
|
15
|
-
firefoxOptions.headless
|
|
14
|
+
firefoxOptions.addArguments('-headless')
|
|
16
15
|
}
|
|
17
16
|
firefoxOptions.setLoggingPrefs(loggingPreferences)
|
|
18
17
|
if (options.binary) {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { InvalidArgumentError } = require('commander')
|
|
4
|
+
|
|
5
|
+
let browserio
|
|
6
|
+
|
|
7
|
+
function browser (value) {
|
|
8
|
+
if (value === undefined) {
|
|
9
|
+
return 'chrome'
|
|
10
|
+
}
|
|
11
|
+
if (!['chrome', 'firefox'].includes(value)) {
|
|
12
|
+
throw new InvalidArgumentError('Browser name')
|
|
13
|
+
}
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
require('./browser')({
|
|
18
|
+
metadata: {
|
|
19
|
+
name: 'webdriverio',
|
|
20
|
+
options: [
|
|
21
|
+
['--visible [flag]', 'Show the browser', false],
|
|
22
|
+
['-b, --browser <name>', 'Browser driver', browser, 'chrome'],
|
|
23
|
+
['--binary <binary>', 'Binary path']
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async capabilities ({ settings, options }) {
|
|
28
|
+
return {
|
|
29
|
+
modules: ['webdriverio'],
|
|
30
|
+
screenshot: '.png',
|
|
31
|
+
scripts: true,
|
|
32
|
+
traces: []
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
async screenshot ({ filename }) {
|
|
37
|
+
if (browserio) {
|
|
38
|
+
await browserio.saveScreenshot(filename)
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async beforeExit () {
|
|
44
|
+
if (browserio) {
|
|
45
|
+
await browserio.deleteSession()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async run ({
|
|
50
|
+
settings: { url, scripts, modules },
|
|
51
|
+
options,
|
|
52
|
+
consoleWriter,
|
|
53
|
+
networkWriter
|
|
54
|
+
}) {
|
|
55
|
+
const { remote } = require(modules.webdriverio)
|
|
56
|
+
|
|
57
|
+
const [browserOptions, args] = {
|
|
58
|
+
chrome: [
|
|
59
|
+
'goog:chromeOptions',
|
|
60
|
+
options.visible ? [] : ['--headless=new', '--log-level=3', '--disable-gpu']
|
|
61
|
+
],
|
|
62
|
+
firefox: [
|
|
63
|
+
'moz:firefoxOptions',
|
|
64
|
+
options.visible ? [] : ['-headless']
|
|
65
|
+
]
|
|
66
|
+
}[options.browser]
|
|
67
|
+
|
|
68
|
+
browserio = await remote({
|
|
69
|
+
capabilities: {
|
|
70
|
+
browserName: options.browser,
|
|
71
|
+
webSocketUrl: true,
|
|
72
|
+
[browserOptions]: {
|
|
73
|
+
args,
|
|
74
|
+
binary: options.binary
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if (scripts && scripts.length) {
|
|
80
|
+
for (const script of scripts) {
|
|
81
|
+
await browserio.scriptAddPreloadScript({
|
|
82
|
+
functionDeclaration: `() => ${script}`
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await browserio.url(url)
|
|
88
|
+
}
|
|
89
|
+
})
|
package/src/job.js
CHANGED
|
@@ -132,6 +132,7 @@ function getCommand (cwd) {
|
|
|
132
132
|
.option('-ccf, --coverage-check-functions <percent>', '[💻🔗] What % of functions must be covered', percent, 0)
|
|
133
133
|
.option('-ccl, --coverage-check-lines <percent>', '[💻🔗] What % of lines must be covered', percent, 0)
|
|
134
134
|
.option('-ccs, --coverage-check-statements <percent>', '[💻🔗] What % of statements must be covered', percent, 0)
|
|
135
|
+
.option('-crs, --coverage-remote-scanner <path>', '[💻🔗] Scan for files when all coverage is requested', '$/scan-ui5.js')
|
|
135
136
|
.option('-s, --serve-only [flag]', '[💻🔗] Serve only', boolean, false)
|
|
136
137
|
|
|
137
138
|
// Specific to legacy (and might be used with url if pointing to local project)
|
|
@@ -146,8 +147,8 @@ function getCommand (cwd) {
|
|
|
146
147
|
|
|
147
148
|
// Specific to coverage in url mode (experimental)
|
|
148
149
|
.option('-cp, --coverage-proxy [flag]', `[🔗] ${EXPERIMENTAL_OPTION} use internal proxy to instrument remote files`, boolean, false)
|
|
149
|
-
.option('-cpi, --coverage-proxy-include <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to instrument for coverage`, regex,
|
|
150
|
-
.option('-cpe, --coverage-proxy-exclude <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to ignore for coverage`, regex,
|
|
150
|
+
.option('-cpi, --coverage-proxy-include <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to instrument for coverage`, regex, '.*')
|
|
151
|
+
.option('-cpe, --coverage-proxy-exclude <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to ignore for coverage`, regex, '/((test-)?resources|tests?)/')
|
|
151
152
|
|
|
152
153
|
.addOption(new Option('--debug-probe-only', DEBUG_OPTION, boolean).hideHelp())
|
|
153
154
|
.addOption(new Option('--debug-keep-browser-open', DEBUG_OPTION, boolean).hideHelp())
|
|
@@ -219,7 +220,7 @@ function finalize (job) {
|
|
|
219
220
|
function updateToAbsolute (member, from = job.cwd) {
|
|
220
221
|
job[member] = toAbsolute(job[member], from)
|
|
221
222
|
}
|
|
222
|
-
'browser,coverageSettings,progressPage'
|
|
223
|
+
'browser,coverageSettings,coverageRemoteScanner,progressPage'
|
|
223
224
|
.split(',')
|
|
224
225
|
.forEach(setting => { job[setting] = checkDefault(job[setting]) })
|
|
225
226
|
updateToAbsolute('cwd', job.initialCwd)
|
|
@@ -299,6 +300,8 @@ function finalize (job) {
|
|
|
299
300
|
overrideDirIfNotSet('coverageReportDir', settings['report-dir'])
|
|
300
301
|
overrideDirIfNotSet('coverageTempDir', settings['temp-dir'])
|
|
301
302
|
overrideIfNotSet('coverageReporters', settings.reporter)
|
|
303
|
+
|
|
304
|
+
checkAccess({ path: job.coverageRemoteScanner, label: 'coverage remote scanner', file: true })
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
if (job.mode === 'url') {
|
package/src/output.js
CHANGED
|
@@ -364,7 +364,7 @@ function build (job) {
|
|
|
364
364
|
},
|
|
365
365
|
|
|
366
366
|
packageNotLatest (name, latestVersion) {
|
|
367
|
-
wrap(() => log(job,
|
|
367
|
+
wrap(() => log(job, `⚠️ [PKGVRS] latest version of ${name} is ${latestVersion}`))()
|
|
368
368
|
},
|
|
369
369
|
|
|
370
370
|
browserStart (url) {
|
|
@@ -472,7 +472,15 @@ function build (job) {
|
|
|
472
472
|
}),
|
|
473
473
|
|
|
474
474
|
instrumentationSkipped: wrap(() => {
|
|
475
|
-
log(job, p80()
|
|
475
|
+
log(job, p80()`⚠️ [SKPNYC] Skipping nyc instrumentation (--url)`)
|
|
476
|
+
}),
|
|
477
|
+
|
|
478
|
+
assumingOneOrigin: wrap(() => {
|
|
479
|
+
log(job, p80()`⚠️ [COVORG] Considering only one origin`)
|
|
480
|
+
}),
|
|
481
|
+
|
|
482
|
+
noInfoForAllCoverage: wrap(() => {
|
|
483
|
+
log(job, p80()`⚠️ [COVALL] Unable to process all coverage, report might be incomplete`)
|
|
476
484
|
}),
|
|
477
485
|
|
|
478
486
|
endpointError: wrap(({ api, url, data, error }) => {
|
|
@@ -548,7 +556,7 @@ function build (job) {
|
|
|
548
556
|
}),
|
|
549
557
|
|
|
550
558
|
unhandled: wrap(() => {
|
|
551
|
-
warn(job, p80()
|
|
559
|
+
warn(job, p80()`⚠️ [UNHAND] Some requests are not handled properly, check the unhandled.txt report for more info`)
|
|
552
560
|
}),
|
|
553
561
|
|
|
554
562
|
reportGeneratorFailed: wrap((generator, exitCode, buffers) => {
|
package/src/reserve.js
CHANGED
|
@@ -13,7 +13,8 @@ module.exports = async job => check({
|
|
|
13
13
|
...job.mappings ?? [],
|
|
14
14
|
...job.serveOnly ? [] : endpoints(job),
|
|
15
15
|
...ui5(job),
|
|
16
|
-
...await coverage(job),
|
|
16
|
+
...job.serveOnly ? [] : await coverage(job),
|
|
17
|
+
{
|
|
17
18
|
// Project mapping
|
|
18
19
|
match: /^\/(.*)/,
|
|
19
20
|
file: join(job.webapp, '$1'),
|