ui5-test-runner 3.0.0 → 3.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/package.json +1 -1
- package/src/capabilities/tests/scripts/coverage.html +32 -0
- package/src/capabilities/tests/scripts/iframe.html +18 -0
- package/src/capabilities/tests/scripts/index.js +8 -0
- package/src/coverage.js +20 -3
- package/src/coverage.spec.js +39 -0
- package/src/defaults/jsdom.js +11 -0
- package/src/defaults/puppeteer.js +1 -1
- package/src/defaults/report/default.html +1 -1
- package/src/defaults/report/main.js +12 -0
- package/src/inject/opa-iframe-coverage.js +23 -0
- package/src/inject/post.js +3 -2
- package/src/inject/qunit-hooks.js +3 -2
- package/src/inject/qunit-redirect.js +3 -2
- package/src/job.js +34 -1
- package/src/output.js +4 -0
- package/src/qunit-hooks.spec.js +1 -1
- package/src/reserve.js +1 -1
- package/src/tests.js +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<body>
|
|
4
|
+
<h1>Coverage in an iframe</h1>
|
|
5
|
+
<p>Checks if the coverage information can be hooked in the iframe</p>
|
|
6
|
+
<script>
|
|
7
|
+
// As generated by Istanbul
|
|
8
|
+
function cov_1y52ey2l1c() {
|
|
9
|
+
var global = new Function("return this")();
|
|
10
|
+
var gcv = "__coverage__";
|
|
11
|
+
var path = "coverage.html";
|
|
12
|
+
var coverageData = {
|
|
13
|
+
path: path,
|
|
14
|
+
_coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9",
|
|
15
|
+
hash: "55ef335c1997f49832eb59e4836d886c3923dbfb",
|
|
16
|
+
status: "ko"
|
|
17
|
+
};
|
|
18
|
+
var coverage = global[gcv] || (global[gcv] = {});
|
|
19
|
+
if (!coverage[path] || coverage[path].hash !== hash) {
|
|
20
|
+
coverage[path] = coverageData;
|
|
21
|
+
}
|
|
22
|
+
var actualCoverage = coverage[path];
|
|
23
|
+
{// @ts-ignore
|
|
24
|
+
cov_1y52ey2l1c = function () { return actualCoverage; };
|
|
25
|
+
}
|
|
26
|
+
return actualCoverage;
|
|
27
|
+
}
|
|
28
|
+
cov_1y52ey2l1c();
|
|
29
|
+
cov_1y52ey2l1c().status = "ok";
|
|
30
|
+
</script>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>IFrame Coverage</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<h1>IFrame Coverage</h1>
|
|
8
|
+
<p>Checks if the coverage information can be extracted from the iframe</p>
|
|
9
|
+
<iframe src="coverage.html"></iframe>
|
|
10
|
+
<script>
|
|
11
|
+
document.querySelector('iframe').addEventListener('load', () => {
|
|
12
|
+
const xhr = new XMLHttpRequest()
|
|
13
|
+
xhr.open('POST', '/_/log')
|
|
14
|
+
xhr.send(JSON.stringify(window.__coverage__ || {}))
|
|
15
|
+
})
|
|
16
|
+
</script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -47,4 +47,12 @@ module.exports = [{
|
|
|
47
47
|
url: 'https://ui5.sap.com/test-resources/sap/m/demokit/orderbrowser/webapp/test/unit/unitTests.qunit.html',
|
|
48
48
|
scripts: ['qunit-intercept.js', 'post.js', 'qunit-hooks.js'],
|
|
49
49
|
endpoint: qUnitEndpoints
|
|
50
|
+
}, {
|
|
51
|
+
label: 'Scripts (IFrame Coverage)',
|
|
52
|
+
for: capabilities => !!capabilities.scripts,
|
|
53
|
+
url: 'scripts/iframe.html',
|
|
54
|
+
scripts: ['opa-iframe-coverage.js'],
|
|
55
|
+
endpoint: ({ body }) => {
|
|
56
|
+
assert.strictEqual(body['coverage.html'].status, 'ok')
|
|
57
|
+
}
|
|
50
58
|
}]
|
package/src/coverage.js
CHANGED
|
@@ -9,6 +9,7 @@ const { getOutput } = require('./output')
|
|
|
9
9
|
const { resolvePackage } = require('./npm')
|
|
10
10
|
|
|
11
11
|
const $nycSettingsPath = Symbol('nycSettingsPath')
|
|
12
|
+
const $coverageFileIndex = Symbol('coverageFileIndex')
|
|
12
13
|
|
|
13
14
|
let nycScript
|
|
14
15
|
|
|
@@ -42,7 +43,6 @@ const customFileSystem = {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
async function instrument (job) {
|
|
45
|
-
job.status = 'Instrumenting'
|
|
46
46
|
if (!nycScript) {
|
|
47
47
|
const nyc = await resolvePackage(job, 'nyc')
|
|
48
48
|
nycScript = join(nyc, 'bin/nyc.js')
|
|
@@ -62,6 +62,19 @@ async function instrument (job) {
|
|
|
62
62
|
settings.exclude.push(join(job.reportDir, '**'))
|
|
63
63
|
settings.exclude.push(join(job.coverageReportDir, '**'))
|
|
64
64
|
await writeFile(job[$nycSettingsPath], JSON.stringify(settings))
|
|
65
|
+
if (job.mode === 'url') {
|
|
66
|
+
const port = job.port.toString()
|
|
67
|
+
const useLocal = job.url.some(url => {
|
|
68
|
+
// ignore host name since the machine might be exposed with any name
|
|
69
|
+
const parsedUrl = new URL(url)
|
|
70
|
+
return parsedUrl.port === port
|
|
71
|
+
})
|
|
72
|
+
if (!useLocal) {
|
|
73
|
+
getOutput(job).instrumentationSkipped()
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
job.status = 'Instrumenting'
|
|
65
78
|
await nyc(job, 'instrument', job.webapp, join(job.coverageTempDir, 'instrumented'), '--nycrc-path', job[$nycSettingsPath])
|
|
66
79
|
}
|
|
67
80
|
|
|
@@ -76,7 +89,11 @@ async function generateCoverageReport (job) {
|
|
|
76
89
|
module.exports = {
|
|
77
90
|
instrument: job => job.coverage && instrument(job),
|
|
78
91
|
async collect (job, url, coverageData) {
|
|
79
|
-
|
|
92
|
+
job[$coverageFileIndex] = (job[$coverageFileIndex] || 0) + 1
|
|
93
|
+
const coverageFileName = join(job.coverageTempDir, `${filename(url)}_${job[$coverageFileIndex]}.json`)
|
|
94
|
+
if (job.debugCoverage) {
|
|
95
|
+
getOutput(job).wrap(() => console.log('coverage', coverageFileName))
|
|
96
|
+
}
|
|
80
97
|
await writeFile(coverageFileName, JSON.stringify(coverageData))
|
|
81
98
|
},
|
|
82
99
|
generateCoverageReport: job => job.coverage && generateCoverageReport(job),
|
|
@@ -85,7 +102,7 @@ module.exports = {
|
|
|
85
102
|
match: /^\/(.*\.js)$/,
|
|
86
103
|
file: join(job.coverageTempDir, 'instrumented', '$1'),
|
|
87
104
|
'ignore-if-not-found': true,
|
|
88
|
-
'custom-file-system': customFileSystem
|
|
105
|
+
'custom-file-system': job.debugCoverageNoCustomFs ? undefined : customFileSystem
|
|
89
106
|
}]
|
|
90
107
|
: []
|
|
91
108
|
}
|
package/src/coverage.spec.js
CHANGED
|
@@ -3,6 +3,7 @@ const { fromObject } = require('./job')
|
|
|
3
3
|
const { instrument, generateCoverageReport, mappings } = require('./coverage')
|
|
4
4
|
const { stat } = require('fs/promises')
|
|
5
5
|
const { cleanDir, createDir } = require('./tools')
|
|
6
|
+
const { getOutput } = require('./output')
|
|
6
7
|
|
|
7
8
|
describe('src/coverage', () => {
|
|
8
9
|
const cwd = join(__dirname, '../test/project')
|
|
@@ -75,5 +76,43 @@ describe('src/coverage', () => {
|
|
|
75
76
|
const coverageMappings = mappings(job)
|
|
76
77
|
expect(coverageMappings.length).toStrictEqual(1)
|
|
77
78
|
})
|
|
79
|
+
|
|
80
|
+
describe('--url compatibility', () => {
|
|
81
|
+
let output
|
|
82
|
+
let instrumentationSkipped
|
|
83
|
+
|
|
84
|
+
beforeAll(() => {
|
|
85
|
+
output = getOutput(job)
|
|
86
|
+
instrumentationSkipped = jest.spyOn(output, 'instrumentationSkipped')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
instrumentationSkipped.mockReset()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
afterAll(() => {
|
|
94
|
+
instrumentationSkipped.mockRestore()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('does *not* instrument if the URL does not match current port', async () => {
|
|
98
|
+
Object.assign(job, {
|
|
99
|
+
mode: 'url',
|
|
100
|
+
port: 8080,
|
|
101
|
+
url: ['http://localhost:8081/whatever/test.html']
|
|
102
|
+
})
|
|
103
|
+
await instrument(job)
|
|
104
|
+
expect(instrumentationSkipped).toHaveBeenCalled()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('**does** instrument anyway if the URL matches current port', async () => {
|
|
108
|
+
Object.assign(job, {
|
|
109
|
+
mode: 'url',
|
|
110
|
+
port: 8080,
|
|
111
|
+
url: ['http://localhost:8080/whatever/test.html']
|
|
112
|
+
})
|
|
113
|
+
await instrument(job)
|
|
114
|
+
expect(instrumentationSkipped).not.toHaveBeenCalled()
|
|
115
|
+
})
|
|
116
|
+
})
|
|
78
117
|
})
|
|
79
118
|
})
|
package/src/defaults/jsdom.js
CHANGED
|
@@ -30,7 +30,17 @@ require('./browser')({
|
|
|
30
30
|
virtualConsole.on('info', (...args) => consoleWriter.append({ type: 'info', text: args.join(' ') }))
|
|
31
31
|
virtualConsole.on('log', (...args) => consoleWriter.append({ type: 'log', text: args.join(' ') }))
|
|
32
32
|
|
|
33
|
+
let mainWindow
|
|
34
|
+
|
|
33
35
|
const beforeParse = (window) => {
|
|
36
|
+
if (mainWindow === undefined) {
|
|
37
|
+
mainWindow = window
|
|
38
|
+
} else {
|
|
39
|
+
Object.defineProperty(window, 'parent', {
|
|
40
|
+
value: mainWindow,
|
|
41
|
+
writable: false
|
|
42
|
+
})
|
|
43
|
+
}
|
|
34
44
|
require('./jsdom/compatibility')({ window, networkWriter })
|
|
35
45
|
if (options.debug) {
|
|
36
46
|
require('./jsdom/debug')(window)
|
|
@@ -43,6 +53,7 @@ require('./browser')({
|
|
|
43
53
|
const origCreate = Window.createWindow.bind(Window)
|
|
44
54
|
Window.createWindow = (...args) => {
|
|
45
55
|
const window = origCreate(...args)
|
|
56
|
+
window._virtualConsole = virtualConsole
|
|
46
57
|
beforeParse(window)
|
|
47
58
|
return window
|
|
48
59
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<div {{if}}="!(qunitPage || qunitTest)">
|
|
12
12
|
<h1>{{ status || 'Test report' }}</h1>
|
|
13
13
|
<div {{if}}="end === undefined" class="elapsed">In progress since {{ elapsed(start) }}</div>
|
|
14
|
-
<div {{else}} class="elapsed">Duration : {{ elapsed(start, end) }}
|
|
14
|
+
<div {{else}} class="elapsed">Duration : {{ elapsed(start, end) }} <a href="#" id="download">📦</a></div>
|
|
15
15
|
<table style="visibility: {{ testPageUrls.length > 0 ? 'visible' : 'hidden' }};">
|
|
16
16
|
<tr>
|
|
17
17
|
<th> </th>
|
|
@@ -54,4 +54,16 @@ report.ready.then(update => {
|
|
|
54
54
|
})
|
|
55
55
|
}
|
|
56
56
|
hashChange(location.hash)
|
|
57
|
+
|
|
58
|
+
window.addEventListener('click', (event) => {
|
|
59
|
+
if (event.target.id === 'download') {
|
|
60
|
+
const link = this.document.createElement('a')
|
|
61
|
+
const blob = new Blob([JSON.stringify(job)], {
|
|
62
|
+
type: 'application/json'
|
|
63
|
+
})
|
|
64
|
+
link.setAttribute('href', URL.createObjectURL(blob))
|
|
65
|
+
link.setAttribute('download', 'ui5-test-runner-job.json')
|
|
66
|
+
link.click()
|
|
67
|
+
}
|
|
68
|
+
})
|
|
57
69
|
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const MODULE = 'ui5-test-runner/opa-iframe-coverage'
|
|
5
|
+
|
|
6
|
+
if (window[MODULE]) {
|
|
7
|
+
return // already installed
|
|
8
|
+
}
|
|
9
|
+
window[MODULE] = true
|
|
10
|
+
|
|
11
|
+
if (window !== window.top || window !== window.parent) {
|
|
12
|
+
// Inside an iframe
|
|
13
|
+
Object.defineProperty(window, '__coverage__', {
|
|
14
|
+
get () {
|
|
15
|
+
return window.top.__coverage__
|
|
16
|
+
},
|
|
17
|
+
set (value) {
|
|
18
|
+
window.top.__coverage__ = value
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
}())
|
package/src/inject/post.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const POST = 'ui5-test-runner/post'
|
|
5
|
+
if (window[POST]) {
|
|
5
6
|
return
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
|
|
71
72
|
window['ui5-test-runner/stringify'] = stringify
|
|
72
73
|
|
|
73
|
-
window[
|
|
74
|
+
window[POST] = function post (url, data) {
|
|
74
75
|
function request () {
|
|
75
76
|
return new Promise(function (resolve, reject) {
|
|
76
77
|
const xhr = new XMLHttpRequest()
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
(function () {
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const MODULE = 'ui5-test-runner/qunit-hooks'
|
|
6
|
+
if (window[MODULE]) {
|
|
6
7
|
return // already installed
|
|
7
8
|
}
|
|
8
|
-
window[
|
|
9
|
+
window[MODULE] = true
|
|
9
10
|
|
|
10
11
|
const post = window['ui5-test-runner/post']
|
|
11
12
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const MODULE = 'ui5-test-runner/qunit-redirect'
|
|
5
|
+
if (window[MODULE]) {
|
|
5
6
|
return // already installed
|
|
6
7
|
}
|
|
7
|
-
window[
|
|
8
|
+
window[MODULE] = true
|
|
8
9
|
|
|
9
10
|
/* global suite */
|
|
10
11
|
|
package/src/job.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { Command, Option, InvalidArgumentError } = require('commander')
|
|
4
4
|
const { statSync, accessSync, constants } = require('fs')
|
|
5
|
-
const { join, isAbsolute } = require('path')
|
|
5
|
+
const { dirname, join, isAbsolute } = require('path')
|
|
6
6
|
const { name, description, version } = require(join(__dirname, '../package.json'))
|
|
7
7
|
const { getOutput } = require('./output')
|
|
8
8
|
const { $valueSources } = require('./symbols')
|
|
@@ -137,6 +137,8 @@ function getCommand (cwd) {
|
|
|
137
137
|
.addOption(new Option('--debug-keep-report', DEBUG_OPTION, boolean).hideHelp())
|
|
138
138
|
.addOption(new Option('--debug-capabilities-test <name>', DEBUG_OPTION).hideHelp())
|
|
139
139
|
.addOption(new Option('--debug-capabilities-no-timeout', DEBUG_OPTION, boolean).hideHelp())
|
|
140
|
+
.addOption(new Option('--debug-coverage', DEBUG_OPTION, boolean).hideHelp())
|
|
141
|
+
.addOption(new Option('--debug-coverage-no-custom-fs', DEBUG_OPTION, boolean).hideHelp())
|
|
140
142
|
|
|
141
143
|
return command
|
|
142
144
|
}
|
|
@@ -242,6 +244,37 @@ function finalize (job) {
|
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
const output = getOutput(job)
|
|
247
|
+
|
|
248
|
+
if (job.coverage) {
|
|
249
|
+
function overrideIfNotSet (option, valueFromSettings) {
|
|
250
|
+
if (valueFromSettings && job[$valueSources][option] !== 'cli') {
|
|
251
|
+
if (job.debugCoverage) {
|
|
252
|
+
output.wrap(() => console.log(`${option} extracted from nyc settings : ${valueFromSettings}`))
|
|
253
|
+
}
|
|
254
|
+
job[option] = valueFromSettings
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function overrideDirIfNotSet (option, valueFromSettings) {
|
|
259
|
+
if (valueFromSettings && !isAbsolute(valueFromSettings)) {
|
|
260
|
+
valueFromSettings = join(dirname(job.coverageSettings), valueFromSettings)
|
|
261
|
+
}
|
|
262
|
+
overrideIfNotSet(option, valueFromSettings)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
checkAccess({ path: job.coverageSettings, file: true, label: 'coverage settings' })
|
|
266
|
+
|
|
267
|
+
let settings
|
|
268
|
+
try {
|
|
269
|
+
settings = require(job.coverageSettings)
|
|
270
|
+
} catch (e) {
|
|
271
|
+
throw new Error(`Unable to read ${job.coverageSettings} as JSON`)
|
|
272
|
+
}
|
|
273
|
+
overrideDirIfNotSet('coverageReportDir', settings['report-dir'])
|
|
274
|
+
overrideDirIfNotSet('coverageTempDir', settings['temp-dir'])
|
|
275
|
+
overrideIfNotSet('coverageReporters', settings.reporter)
|
|
276
|
+
}
|
|
277
|
+
|
|
245
278
|
job[$status] = 'Starting'
|
|
246
279
|
Object.defineProperty(job, 'status', {
|
|
247
280
|
get () {
|
package/src/output.js
CHANGED
|
@@ -408,6 +408,10 @@ function build (job) {
|
|
|
408
408
|
log(job, p80()`nyc ${args.map(arg => arg.toString()).join(' ')}`)
|
|
409
409
|
}),
|
|
410
410
|
|
|
411
|
+
instrumentationSkipped: wrap(() => {
|
|
412
|
+
log(job, p80()`Skipping nyc instrumentation (--url)`)
|
|
413
|
+
}),
|
|
414
|
+
|
|
411
415
|
endpointError: wrap(({ api, url, data, error }) => {
|
|
412
416
|
const p = p80()
|
|
413
417
|
log(job, p`┌──────────${pad.x('─')}┐`)
|
package/src/qunit-hooks.spec.js
CHANGED
|
@@ -604,7 +604,7 @@ describe('src/qunit-hooks', () => {
|
|
|
604
604
|
expect(job.failed).toStrictEqual(true)
|
|
605
605
|
})
|
|
606
606
|
|
|
607
|
-
describe
|
|
607
|
+
describe('fail OPA fast behavior', () => {
|
|
608
608
|
beforeEach(async () => {
|
|
609
609
|
job.failOpaFast = true
|
|
610
610
|
await testDone(job, url, {
|
package/src/reserve.js
CHANGED