ui5-test-runner 3.3.5 → 4.1.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 -16
- package/package.json +17 -18
- package/src/browsers.js +9 -1
- package/src/capabilities/index.js +6 -6
- package/src/capabilities/tests/screenshot/index.html +13 -3
- package/src/capabilities/tests/screenshot/index.js +11 -5
- package/src/capabilities/tests/traces/index.js +1 -1
- package/src/coverage.js +51 -42
- package/src/defaults/puppeteer.js +10 -20
- package/src/defaults/report/progress.js +11 -0
- package/src/endpoints.js +8 -0
- package/src/get-job-progress.js +9 -0
- package/src/inject/opa-iframe-coverage.js +0 -1
- package/src/inject/post.js +4 -4
- package/src/inject/qunit-hooks.js +18 -2
- package/src/inject/qunit-intercept.js +6 -0
- package/src/inject/ui5-coverage.js +6 -0
- package/src/job-mode.js +0 -1
- package/src/job.js +10 -10
- package/src/options.js +4 -0
- package/src/output.js +56 -24
- package/src/qunit-hooks.js +43 -29
- package/src/symbols.js +2 -1
- package/src/tests.js +6 -1
- package/src/add-test-pages.spec.js +0 -95
- package/src/browser.spec.js +0 -724
- package/src/cors.spec.js +0 -41
- package/src/coverage.spec.js +0 -120
- package/src/csv-reader.spec.js +0 -42
- package/src/csv-writer.spec.js +0 -77
- package/src/error.spec.js +0 -17
- package/src/get-job-progress.spec.js +0 -175
- package/src/inject/post.spec.js +0 -147
- package/src/job.spec.js +0 -418
- package/src/npm.spec.js +0 -98
- package/src/options.spec.js +0 -141
- package/src/qunit-hooks.spec.js +0 -747
- package/src/simulate.spec.js +0 -547
- package/src/timeout.spec.js +0 -39
- package/src/tools.spec.js +0 -90
- package/src/unhandled.spec.js +0 -63
package/README.md
CHANGED
|
@@ -17,10 +17,9 @@ A self-sufficient test runner for UI5 applications enabling parallel execution o
|
|
|
17
17
|
|
|
18
18
|
## 📚 [Documentation](https://arnaudbuchholz.github.io/ui5-test-runner/)
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
## 💿 How to install
|
|
22
21
|
|
|
23
|
-
* Works with [Node.js](https://nodejs.org/en/download/) >=
|
|
22
|
+
* Works with [Node.js](https://nodejs.org/en/download/) >= 18
|
|
24
23
|
* Local installation
|
|
25
24
|
* `npm install --save-dev ui5-test-runner`
|
|
26
25
|
* Trigger either with `npx ui5-test-runner` or through an npm script invoking `ui5-test-runner`
|
|
@@ -30,25 +29,14 @@ A self-sufficient test runner for UI5 applications enabling parallel execution o
|
|
|
30
29
|
|
|
31
30
|
**NOTE** : additional packages might be needed during the execution (`puppeteer`, `selenium-webdriver`, `nyc`...) . If they are found installed **locally** in the tested project, they are used. Otherwise, they are installed **globally**.
|
|
32
31
|
|
|
33
|
-
## 🖥️ How to demo
|
|
34
|
-
|
|
35
|
-
* Clone the project [training-ui5con18-opa](https://github.com/ArnaudBuchholz/training-ui5con18-opa) and run `npm install`
|
|
36
|
-
* Use `npm run karma` to test with the karma runner
|
|
37
|
-
* *Serving the application (a.k.a. legacy mode)*
|
|
38
|
-
* `npx ui5-test-runner --port 8081 --ui5 https://ui5.sap.com/1.109.0/ --cache .ui5 --keep-alive`
|
|
39
|
-
* Follow the progress of the test executions using http://localhost:8081/_/progress.html
|
|
40
|
-
* When the tests are completed, check the code coverage with http://localhost:8081/_/coverage/lcov-report/index.html
|
|
41
|
-
* *Serving the application with `@ui5/cli`*
|
|
42
|
-
* Use `npm start` to serve the application with `@ui5/cli`
|
|
43
|
-
* `npx ui5-test-runner --port 8081 --url http://localhost:8080/test/testsuite.qunit.html --keep-alive`
|
|
44
|
-
* Follow the progress of the test executions using http://localhost:8081/_/progress.html
|
|
45
|
-
|
|
46
|
-
|
|
47
32
|
## ⚖️ License
|
|
48
33
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArnaudBuchholz%2Fui5-test-runner?ref=badge_large)
|
|
49
34
|
|
|
50
35
|
## ⚠️ Breaking changes
|
|
51
36
|
|
|
37
|
+
### v4
|
|
38
|
+
* Dropping support of Node.js 16
|
|
39
|
+
|
|
52
40
|
### v3
|
|
53
41
|
* Dropping support of Node.js 14
|
|
54
42
|
|
|
@@ -58,3 +46,8 @@ A self-sufficient test runner for UI5 applications enabling parallel execution o
|
|
|
58
46
|
* Dependencies are installed **on demand**
|
|
59
47
|
* Browser instantiation command evolved in an **incompatible way** (see [documentation](https://arnaudbuchholz.github.io/ui5-test-runner/browser.html)).
|
|
60
48
|
* Output is different (report, traces)
|
|
49
|
+
|
|
50
|
+
## ✒ Contributors
|
|
51
|
+
|
|
52
|
+
* [Marian Zeis](https://github.com/marianfoo): Documentation page revamp [PR #54](https://github.com/ArnaudBuchholz/ui5-test-runner/pull/54)
|
|
53
|
+
* [Raj Singh](https://github.com/rajxsingh): Basic HTTP Authentication in Puppeteer [PR #71](https://github.com/ArnaudBuchholz/ui5-test-runner/pull/71)
|
package/package.json
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"files": [
|
|
7
|
-
"defaults/*",
|
|
8
|
-
"src/**/*",
|
|
9
|
-
"*.js"
|
|
10
|
-
],
|
|
11
6
|
"bin": {
|
|
12
7
|
"ui5-test-runner": "./index.js"
|
|
13
8
|
},
|
|
14
9
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
10
|
+
"node": ">=18"
|
|
16
11
|
},
|
|
17
12
|
"scripts": {
|
|
18
13
|
"lint": "standard --fix",
|
|
19
14
|
"test": "npm run test:unit && npm run test:browsers && npm run test:samples",
|
|
20
15
|
"test:browsers": "npm run test:integration:jsdom && npm run test:integration:puppeteer && npm run test:integration:selenium-webdriver-chrome && npm run test:integration:playwright",
|
|
21
|
-
"test:samples": "npm run test:samples:js && npm run test:samples:ts",
|
|
16
|
+
"test:samples": "npm run test:samples:js && npm run test:samples:ts && npm run test:auth-sample",
|
|
22
17
|
"test:samples:js": "npm run test:sample:js:legacy && npm run test:sample:js:coverage:legacy && npm run test:sample:js:legacy-remote && npm run test:sample:js:coverage:legacy-remote && npm run test:sample:js:remote && npm run test:sample:js:coverage:remote",
|
|
23
18
|
"test:samples:ts": "npm run test:sample:ts:remote && npm run test:sample:ts:coverage:remote",
|
|
19
|
+
"test:auth-sample": "npm run test:auth-sample:remote",
|
|
24
20
|
"test:coverall": "rimraf .nyc_output && jest --coverageDirectory .nyc_output --coverageReporters json && nyc --silent --no-clean npm run test:integration:jsdom && nyc --silent --no-clean npm run test:integration:puppeteer && nyc --silent --no-clean npm run test:integration:selenium-webdriver-chrome && nyc --silent --no-clean npm run test:integration:playwright && nyc merge .nyc_output .nyc_output/final/coverage.json && nyc report --temp-dir .nyc_output/final/ --report-dir coverage --branches 80 --functions 80 --lines 80 --statements 80",
|
|
25
21
|
"test:unit": "jest",
|
|
26
22
|
"test:unit:debug": "jest --runInBand",
|
|
@@ -41,7 +37,10 @@
|
|
|
41
37
|
"serve:sample:ts": "cd ./test/sample.ts && node ui5.cjs serve",
|
|
42
38
|
"test:sample:ts:coverage:remote": "start-server-and-test 'npm run serve:sample:ts:coverage' http://localhost:8080 'node . --url http://localhost:8080/test/testsuite.qunit.html --coverage --coverage-check-statements 67'",
|
|
43
39
|
"serve:sample:ts:coverage": "cd ./test/sample.ts && node ui5.cjs serve --config ui5-coverage.yaml",
|
|
44
|
-
"
|
|
40
|
+
"test:auth-sample:remote": "start-server-and-test 'npm run serve:auth-sample' http://localhost:8080 'node . --url http://localhost:8080/test/testsuite.qunit.html --browser $/puppeteer.js --browser-args --basic-auth-username testUsername --browser-args --basic-auth-password testPassword'",
|
|
41
|
+
"serve:auth-sample": "cd ./test/auth_sample.js && reserve --config ./reserve.json",
|
|
42
|
+
"build:doc": "node build/doc",
|
|
43
|
+
"clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio"
|
|
45
44
|
},
|
|
46
45
|
"repository": {
|
|
47
46
|
"type": "git",
|
|
@@ -63,23 +62,23 @@
|
|
|
63
62
|
},
|
|
64
63
|
"homepage": "https://github.com/ArnaudBuchholz/ui5-test-runner#readme",
|
|
65
64
|
"dependencies": {
|
|
66
|
-
"commander": "^
|
|
65
|
+
"commander": "^12.0.0",
|
|
67
66
|
"mime": "^3.0.0",
|
|
68
67
|
"punybind": "^1.2.1",
|
|
69
68
|
"punyexpr": "^1.0.4",
|
|
70
|
-
"reserve": "^1.15.
|
|
69
|
+
"reserve": "^1.15.6"
|
|
71
70
|
},
|
|
72
71
|
"devDependencies": {
|
|
73
|
-
"@openui5/types": "^1.
|
|
74
|
-
"@ui5/cli": "^3.
|
|
75
|
-
"@ui5/middleware-code-coverage": "^1.1.
|
|
72
|
+
"@openui5/types": "^1.120.7",
|
|
73
|
+
"@ui5/cli": "^3.9.1",
|
|
74
|
+
"@ui5/middleware-code-coverage": "^1.1.1",
|
|
76
75
|
"jest": "^29.7.0",
|
|
77
|
-
"nock": "^13.
|
|
76
|
+
"nock": "^13.5.1",
|
|
78
77
|
"nyc": "^15.1.0",
|
|
79
78
|
"standard": "^17.1.0",
|
|
80
|
-
"start-server-and-test": "^2.0.
|
|
81
|
-
"typescript": "^5.
|
|
82
|
-
"ui5-tooling-transpile": "^3.
|
|
79
|
+
"start-server-and-test": "^2.0.3",
|
|
80
|
+
"typescript": "^5.3.3",
|
|
81
|
+
"ui5-tooling-transpile": "^3.3.3"
|
|
83
82
|
},
|
|
84
83
|
"optionalDependencies": {
|
|
85
84
|
"fsevents": "^2.3.3"
|
package/src/browsers.js
CHANGED
|
@@ -214,13 +214,16 @@ async function screenshot (job, url, filename) {
|
|
|
214
214
|
if (!job.browserCapabilities.screenshot) {
|
|
215
215
|
throw UTRError.BROWSER_SCREENSHOT_NOT_SUPPORTED()
|
|
216
216
|
}
|
|
217
|
+
const output = getOutput(job)
|
|
218
|
+
const id = ++lastScreenshotId
|
|
217
219
|
try {
|
|
218
220
|
const { childProcess, reportDir } = job[$browsers][url]
|
|
219
221
|
const absoluteFilename = join(reportDir, filename + job.browserCapabilities.screenshot)
|
|
220
222
|
if (childProcess.connected) {
|
|
221
|
-
|
|
223
|
+
output.debug('screenshot', id, url, absoluteFilename)
|
|
222
224
|
const { promise, resolve, reject } = allocPromise()
|
|
223
225
|
screenshots[id] = resolve
|
|
226
|
+
output.debug('screenshot', id, 'sending command')
|
|
224
227
|
childProcess.send({
|
|
225
228
|
id,
|
|
226
229
|
command: 'screenshot',
|
|
@@ -229,15 +232,20 @@ async function screenshot (job, url, filename) {
|
|
|
229
232
|
const timeoutId = setTimeout(() => {
|
|
230
233
|
reject(UTRError.BROWSER_SCREENSHOT_TIMEOUT())
|
|
231
234
|
}, job.screenshotTimeout)
|
|
235
|
+
output.debug('screenshot', id, 'command sent, waiting for answer')
|
|
232
236
|
await promise
|
|
237
|
+
output.debug('screenshot', id, 'answer received')
|
|
233
238
|
clearTimeout(timeoutId)
|
|
234
239
|
const result = await stat(absoluteFilename)
|
|
240
|
+
output.debug('screenshot', id, 'file size :', result.size)
|
|
235
241
|
if (!result.isFile() || result.size === 0) {
|
|
236
242
|
throw new Error('File expected')
|
|
237
243
|
}
|
|
244
|
+
output.debug('screenshot', id, 'done')
|
|
238
245
|
return absoluteFilename
|
|
239
246
|
}
|
|
240
247
|
} catch (e) {
|
|
248
|
+
output.debug('screenshot', id, e.message)
|
|
241
249
|
if (e.code === UTRError.BROWSER_SCREENSHOT_TIMEOUT_CODE) {
|
|
242
250
|
throw e
|
|
243
251
|
}
|
|
@@ -4,7 +4,6 @@ const { check, serve, body } = require('reserve')
|
|
|
4
4
|
const { probe, start, stop } = require('../browsers')
|
|
5
5
|
const { join } = require('path')
|
|
6
6
|
const { getOutput } = require('../output')
|
|
7
|
-
const EventEmitter = require('events')
|
|
8
7
|
const { performance } = require('perf_hooks')
|
|
9
8
|
const { cleanDir, allocPromise, filename } = require('../tools')
|
|
10
9
|
const { $browsers } = require('../symbols')
|
|
@@ -60,13 +59,16 @@ async function capabilities (job) {
|
|
|
60
59
|
const { referer, 'x-page-url': xPageUrl } = request.headers
|
|
61
60
|
const listenerIndex = (xPageUrl || referer).match(/\blistener=(\d+)/)[1]
|
|
62
61
|
const listener = listeners[listenerIndex]
|
|
63
|
-
listener
|
|
62
|
+
await listener({
|
|
64
63
|
endpoint,
|
|
65
64
|
body: JSON.parse(await body(request))
|
|
66
65
|
})
|
|
67
66
|
response.writeHead(200)
|
|
68
67
|
response.end()
|
|
69
68
|
}
|
|
69
|
+
}, {
|
|
70
|
+
match: '^/inject/(.*)',
|
|
71
|
+
file: join(__dirname, '../inject/$1')
|
|
70
72
|
}, {
|
|
71
73
|
match: '^/(.*)',
|
|
72
74
|
file: join(__dirname, '$1')
|
|
@@ -105,8 +107,6 @@ async function capabilities (job) {
|
|
|
105
107
|
const { label, url, scripts, endpoint = () => { } } = filteredTests.shift()
|
|
106
108
|
|
|
107
109
|
const listenerIndex = listeners.length
|
|
108
|
-
const listener = new EventEmitter()
|
|
109
|
-
listeners.push(listener)
|
|
110
110
|
let pageUrl
|
|
111
111
|
if (url.startsWith('http')) {
|
|
112
112
|
pageUrl = url
|
|
@@ -151,7 +151,7 @@ async function capabilities (job) {
|
|
|
151
151
|
const context = {
|
|
152
152
|
job
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
listeners[listenerIndex] = async data => {
|
|
155
155
|
try {
|
|
156
156
|
if (await endpoint.call(context, data, pageUrl) !== false) {
|
|
157
157
|
done()
|
|
@@ -159,7 +159,7 @@ async function capabilities (job) {
|
|
|
159
159
|
} catch (e) {
|
|
160
160
|
done(e)
|
|
161
161
|
}
|
|
162
|
-
}
|
|
162
|
+
}
|
|
163
163
|
|
|
164
164
|
start(job, pageUrl, scripts)
|
|
165
165
|
.catch(reason => done(reason))
|
|
@@ -3,11 +3,21 @@
|
|
|
3
3
|
<body>
|
|
4
4
|
<H1>screenshot</H1>
|
|
5
5
|
<p>Checks if the browser supports screenshot</p>
|
|
6
|
+
<input type="text" id="first" value="first">
|
|
7
|
+
<input type="text" id="second" value="second">
|
|
6
8
|
<span style="font-size: 32rem;">😉</span>
|
|
9
|
+
<script src="/inject/post.js"></script>
|
|
7
10
|
<script>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
document.getElementById('first').focus()
|
|
12
|
+
const post = window['ui5-test-runner/post']
|
|
13
|
+
post('/_/log', { step:'screenshot' })
|
|
14
|
+
.then(() => {
|
|
15
|
+
const current = document.activeElement
|
|
16
|
+
return post('/_/log', {
|
|
17
|
+
step: 'focus',
|
|
18
|
+
current: current ? current.id : ''
|
|
19
|
+
})
|
|
20
|
+
})
|
|
11
21
|
</script>
|
|
12
22
|
</body>
|
|
13
23
|
</html>
|
|
@@ -8,11 +8,17 @@ module.exports = {
|
|
|
8
8
|
label: 'Screenshot',
|
|
9
9
|
for: capabilities => !!capabilities.screenshot,
|
|
10
10
|
url: 'screenshot/index.html',
|
|
11
|
-
endpoint: async function (
|
|
11
|
+
endpoint: async function ({ body: { step, current } }, url) {
|
|
12
12
|
const { job } = this
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
if (step === 'screenshot') {
|
|
14
|
+
const fileName = await screenshot(job, url, 'screenshot')
|
|
15
|
+
const fileInfo = await stat(fileName)
|
|
16
|
+
assert.ok(fileInfo.isFile(), 'The file was generated')
|
|
17
|
+
assert.ok(fileInfo.size > 1024, 'The file contains something')
|
|
18
|
+
return false
|
|
19
|
+
} else {
|
|
20
|
+
assert.strictEqual(current, 'first')
|
|
21
|
+
return true
|
|
22
|
+
}
|
|
17
23
|
}
|
|
18
24
|
}
|
package/src/coverage.js
CHANGED
|
@@ -86,8 +86,31 @@ async function instrument (job) {
|
|
|
86
86
|
await nyc(job, 'instrument', job.webapp, join(job.coverageTempDir, 'instrumented'), '--nycrc-path', job[$nycSettingsPath])
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
async function getReadableSource (job, pathOrUrl) {
|
|
90
|
+
if (isAbsolute(pathOrUrl)) {
|
|
91
|
+
try {
|
|
92
|
+
await access(pathOrUrl, constants.R_OK)
|
|
93
|
+
return pathOrUrl
|
|
94
|
+
} catch (e) {}
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const filePath = join(job.webapp, pathOrUrl)
|
|
98
|
+
await access(filePath, constants.R_OK)
|
|
99
|
+
return filePath
|
|
100
|
+
} catch (e) {}
|
|
101
|
+
try {
|
|
102
|
+
// Assuming all files are coming from the same server
|
|
103
|
+
const { origin } = new URL(job.testPageUrls[0])
|
|
104
|
+
const filePath = join(job.coverageTempDir, 'sources', pathOrUrl)
|
|
105
|
+
await download(origin + pathOrUrl, filePath)
|
|
106
|
+
return filePath
|
|
107
|
+
} catch (e) {}
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
async function generateCoverageReport (job) {
|
|
90
111
|
job.status = 'Generating coverage report'
|
|
112
|
+
const output = getOutput(job)
|
|
113
|
+
output.debug('coverage', 'Generating coverage report...')
|
|
91
114
|
await cleanDir(job.coverageReportDir)
|
|
92
115
|
const coverageMergedDir = join(job.coverageTempDir, 'merged')
|
|
93
116
|
await createDir(coverageMergedDir)
|
|
@@ -95,25 +118,17 @@ async function generateCoverageReport (job) {
|
|
|
95
118
|
await nyc(job, 'merge', job.coverageTempDir, coverageFilename)
|
|
96
119
|
if (job[$coverageRemote] && !job.coverageProxy) {
|
|
97
120
|
job.status = 'Checking remote source files'
|
|
98
|
-
|
|
99
|
-
const { origin } = new URL(job.testPageUrls[0])
|
|
100
|
-
const sourcesBasePath = join(job.coverageTempDir, 'sources')
|
|
121
|
+
output.debug('coverage', 'Checking remote source files...')
|
|
101
122
|
const coverageData = require(coverageFilename)
|
|
102
123
|
const filenames = Object.keys(coverageData)
|
|
103
124
|
let changes = 0
|
|
104
125
|
for (const filename of filenames) {
|
|
105
126
|
const fileData = coverageData[filename]
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
continue
|
|
111
|
-
} catch (e) {}
|
|
127
|
+
const filePath = await getReadableSource(job, fileData.path)
|
|
128
|
+
if (filePath && filePath !== fileData.path) {
|
|
129
|
+
fileData.path = filePath
|
|
130
|
+
++changes
|
|
112
131
|
}
|
|
113
|
-
const filePath = join(sourcesBasePath, path)
|
|
114
|
-
fileData.path = filePath
|
|
115
|
-
await download(origin + path, filePath)
|
|
116
|
-
++changes
|
|
117
132
|
}
|
|
118
133
|
if (changes > 0) {
|
|
119
134
|
await writeFile(coverageFilename, JSON.stringify(coverageData))
|
|
@@ -151,9 +166,7 @@ module.exports = {
|
|
|
151
166
|
async collect (job, url, coverageData) {
|
|
152
167
|
job[$coverageFileIndex] = (job[$coverageFileIndex] || 0) + 1
|
|
153
168
|
const coverageFileName = join(job.coverageTempDir, `${filename(url)}_${job[$coverageFileIndex]}.json`)
|
|
154
|
-
|
|
155
|
-
getOutput(job).wrap(() => console.log('coverage', coverageFileName))
|
|
156
|
-
}
|
|
169
|
+
getOutput(job).debug('coverage', `saved coverage in '${coverageFileName}'`)
|
|
157
170
|
await writeFile(coverageFileName, JSON.stringify(coverageData))
|
|
158
171
|
},
|
|
159
172
|
generateCoverageReport: job => job.coverage ? generateCoverageReport(job) : Promise.resolve(),
|
|
@@ -175,8 +188,8 @@ module.exports = {
|
|
|
175
188
|
}
|
|
176
189
|
if (job.mode === 'url' && job.coverageProxy) {
|
|
177
190
|
await setupNyc(job)
|
|
191
|
+
// Assuming all files are coming from the same server
|
|
178
192
|
const { origin } = new URL(job.url[0])
|
|
179
|
-
const sourcesBasePath = join(job.coverageTempDir, 'sources')
|
|
180
193
|
const { createInstrumenter } = require(join(await nycInstallationPath, 'node_modules/istanbul-lib-instrument'))
|
|
181
194
|
const instrumenter = createInstrumenter({
|
|
182
195
|
produceSourceMap: true,
|
|
@@ -188,34 +201,30 @@ module.exports = {
|
|
|
188
201
|
return [{
|
|
189
202
|
match: /(.*\.js)(\?.*)?$/,
|
|
190
203
|
custom: async (request, response, url) => {
|
|
191
|
-
if (!url.match(job.coverageProxyInclude) || url.match(job.coverageProxyExclude)) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
return // Ignore
|
|
204
|
+
if (!url.match(job.coverageProxyInclude) || url.match(/\bresources\b/) || url.match(job.coverageProxyExclude)) {
|
|
205
|
+
getOutput(job).debug('coverage', 'coverage_proxy ignore', url)
|
|
206
|
+
return
|
|
196
207
|
}
|
|
197
|
-
const
|
|
208
|
+
const instrumentedSourcePath = join(instrumentedBasePath, url)
|
|
198
209
|
try {
|
|
199
|
-
await access(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
getOutput(job).wrap(() => console.log('coverage_proxy instrument', url))
|
|
207
|
-
}
|
|
208
|
-
sources[url] = await download(origin + url, sourcePath)
|
|
209
|
-
}
|
|
210
|
-
} catch (statusCode) {
|
|
211
|
-
return statusCode
|
|
212
|
-
}
|
|
210
|
+
await access(instrumentedSourcePath, constants.R_OK)
|
|
211
|
+
return
|
|
212
|
+
} catch (e) {}
|
|
213
|
+
const instrumenting = sources[url]
|
|
214
|
+
if (instrumenting) {
|
|
215
|
+
await instrumenting
|
|
216
|
+
return // ok
|
|
213
217
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
sources[url] = (async () => {
|
|
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
|
+
})()
|
|
219
228
|
}
|
|
220
229
|
},
|
|
221
230
|
instrumentedMapping,
|
|
@@ -11,27 +11,13 @@ require('./browser')({
|
|
|
11
11
|
['-w, --viewport-width <width>', 'Viewport width', 1920],
|
|
12
12
|
['-h, --viewport-height <height>', 'Viewport height', 1080],
|
|
13
13
|
['-l, --language <lang...>', 'Language(s)', ['en-US']],
|
|
14
|
-
['-u, --unsecure', 'Disable security features', false]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// screenshot: '.png',
|
|
20
|
-
// scripts: true,
|
|
21
|
-
// traces: ['console', 'network']
|
|
22
|
-
// }
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
// TODO remove when Node16 is no more supported
|
|
26
|
-
async capabilities () {
|
|
27
|
-
const version = process.version.match(/^v(\d+\.\d+)/)[1]
|
|
28
|
-
let screenshot
|
|
29
|
-
if (!version.startsWith('16')) {
|
|
30
|
-
screenshot = '.png'
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
14
|
+
['-u, --unsecure', 'Disable security features', false],
|
|
15
|
+
['--basic-auth-username <username>', 'Username for basic authentication', ''],
|
|
16
|
+
['--basic-auth-password <password>', 'Password for basic authentication', '']
|
|
17
|
+
],
|
|
18
|
+
capabilities: {
|
|
33
19
|
modules: ['puppeteer'],
|
|
34
|
-
screenshot,
|
|
20
|
+
screenshot: '.png',
|
|
35
21
|
scripts: true,
|
|
36
22
|
traces: ['console', 'network']
|
|
37
23
|
}
|
|
@@ -94,6 +80,10 @@ require('./browser')({
|
|
|
94
80
|
await page.setBypassCSP(true)
|
|
95
81
|
}
|
|
96
82
|
|
|
83
|
+
if (options.basicAuthUsername || options.basicAuthPassword) {
|
|
84
|
+
await page.authenticate({ username: options.basicAuthUsername, password: options.basicAuthPassword })
|
|
85
|
+
}
|
|
86
|
+
|
|
97
87
|
page
|
|
98
88
|
.on('console', message => consoleWriter.append({
|
|
99
89
|
type: message.type(),
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
report.ready.then(update => {
|
|
5
5
|
let lastState = {}
|
|
6
6
|
|
|
7
|
+
async function retry () {
|
|
8
|
+
try {
|
|
9
|
+
await fetch('/_/progress', { method: 'INFO' })
|
|
10
|
+
location.hash = ''
|
|
11
|
+
refresh()
|
|
12
|
+
} catch (e) {
|
|
13
|
+
setTimeout(retry, 250)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
async function refresh () {
|
|
8
18
|
const [, page, test] = location.hash.match(/#?([^-]*)(?:-(.*))?/)
|
|
9
19
|
let url = '/_/progress'
|
|
@@ -22,6 +32,7 @@
|
|
|
22
32
|
...lastState,
|
|
23
33
|
disconnected: true
|
|
24
34
|
})
|
|
35
|
+
retry()
|
|
25
36
|
return
|
|
26
37
|
}
|
|
27
38
|
if (test) {
|
package/src/endpoints.js
CHANGED
|
@@ -141,6 +141,14 @@ module.exports = job => {
|
|
|
141
141
|
match: '^/_/punyexpr.js',
|
|
142
142
|
file: punyexprBinPath
|
|
143
143
|
}, {
|
|
144
|
+
// Endpoint to retry on progress
|
|
145
|
+
method: 'INFO',
|
|
146
|
+
match: '^/_/progress',
|
|
147
|
+
custom: (request, response) => {
|
|
148
|
+
response.writeHead(204)
|
|
149
|
+
response.end()
|
|
150
|
+
}
|
|
151
|
+
}, {
|
|
144
152
|
// Endpoint to follow progress
|
|
145
153
|
match: '^/_/progress(?:\\?page=([^&]*)(?:&test=([^&]*))?)?',
|
|
146
154
|
custom: (request, response, pageId, testId) => getJobProgress(job, request, response, pageId, testId)
|
package/src/get-job-progress.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { $proxifiedUrls } = require('./symbols')
|
|
4
|
+
|
|
3
5
|
const send = (response, obj) => {
|
|
4
6
|
let json
|
|
5
7
|
if (typeof obj !== 'string') {
|
|
@@ -60,6 +62,13 @@ module.exports = {
|
|
|
60
62
|
...job,
|
|
61
63
|
status: job.status
|
|
62
64
|
}, (key, value) => {
|
|
65
|
+
if (key === 'qunitPages' && job[$proxifiedUrls]) {
|
|
66
|
+
const unproxifiedQunitPages = {}
|
|
67
|
+
for (const url of Object.keys(job.qunitPages)) {
|
|
68
|
+
unproxifiedQunitPages[job[$proxifiedUrls][url] || url] = job.qunitPages[url]
|
|
69
|
+
}
|
|
70
|
+
return unproxifiedQunitPages
|
|
71
|
+
}
|
|
63
72
|
if (key === 'modules') {
|
|
64
73
|
return undefined
|
|
65
74
|
}
|
package/src/inject/post.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
if (window[
|
|
6
|
-
return
|
|
4
|
+
const MODULE = 'ui5-test-runner/post'
|
|
5
|
+
if (window[MODULE]) {
|
|
6
|
+
return // already installed
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const base = window['ui5-test-runner/base-host'] || ''
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
|
|
72
72
|
window['ui5-test-runner/stringify'] = stringify
|
|
73
73
|
|
|
74
|
-
window[
|
|
74
|
+
window[MODULE] = function post (url, data) {
|
|
75
75
|
function request () {
|
|
76
76
|
return new Promise(function (resolve, reject) {
|
|
77
77
|
const xhr = new XMLHttpRequest()
|
|
@@ -18,18 +18,34 @@
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function getModules () {
|
|
22
|
+
if (QUnit.config && QUnit.config.modules) {
|
|
23
|
+
return QUnit.config.modules.map(({ name, tests }) => ({
|
|
24
|
+
name,
|
|
25
|
+
tests: tests.map(({ name, testId, skip }) => ({ name, testId, skip }))
|
|
26
|
+
}))
|
|
27
|
+
}
|
|
28
|
+
return []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function extend (details) {
|
|
32
|
+
details.isOpa = isOpa()
|
|
33
|
+
details.modules = getModules()
|
|
34
|
+
return details
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
QUnit.begin(function (details) {
|
|
22
38
|
details.isOpa = isOpa()
|
|
23
39
|
return post('QUnit/begin', details)
|
|
24
40
|
})
|
|
25
41
|
|
|
26
42
|
QUnit.testStart(function (details) {
|
|
27
|
-
return post('QUnit/testStart', details)
|
|
43
|
+
return post('QUnit/testStart', extend(details))
|
|
28
44
|
})
|
|
29
45
|
|
|
30
46
|
QUnit.log(function (log) {
|
|
31
47
|
let ready = false
|
|
32
|
-
post('QUnit/log', log)
|
|
48
|
+
post('QUnit/log', extend(log))
|
|
33
49
|
.then(undefined, function () {
|
|
34
50
|
console.error('Failed to POST to QUnit/log (no timestamp)', log)
|
|
35
51
|
})
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
(function () {
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
+
const MODULE = 'ui5-test-runner/qunit-intercept'
|
|
6
|
+
if (window[MODULE]) {
|
|
7
|
+
return // already installed
|
|
8
|
+
}
|
|
9
|
+
window[MODULE] = true
|
|
10
|
+
|
|
5
11
|
const callbacks = {}
|
|
6
12
|
const mock = new Proxy({}, {
|
|
7
13
|
get: function (instance, property) {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
const MODULE = 'ui5-test-runner/ui5-coverage'
|
|
5
|
+
if (window[MODULE]) {
|
|
6
|
+
return // already installed
|
|
7
|
+
}
|
|
8
|
+
window[MODULE] = true
|
|
9
|
+
|
|
4
10
|
// inspired from ui5/resources/sap/ui/qunit/qunit-coverage-istanbul-dbg.js
|
|
5
11
|
|
|
6
12
|
function appendUrlParameter (url) {
|