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/endpoints.js
CHANGED
|
@@ -2,86 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
const { join } = require('path')
|
|
4
4
|
const { body } = require('reserve')
|
|
5
|
-
const {
|
|
6
|
-
const { writeFile } = require('fs').promises
|
|
7
|
-
const { extractUrl, filename } = require('./tools')
|
|
5
|
+
const { extractPageUrl } = require('./tools')
|
|
8
6
|
const { Request, Response } = require('reserve')
|
|
9
|
-
const
|
|
7
|
+
const { getOutput } = require('./output')
|
|
8
|
+
const { begin, testStart, log, testDone, done } = require('./qunit-hooks')
|
|
9
|
+
const { addTestPages } = require('./add-test-pages')
|
|
10
|
+
const { getJobProgress } = require('./get-job-progress')
|
|
11
|
+
const { readFile } = require('fs/promises')
|
|
12
|
+
const { TextEncoder } = require('util')
|
|
10
13
|
|
|
11
14
|
module.exports = job => {
|
|
12
|
-
async function endpointImpl (implementation, request) {
|
|
13
|
-
const url =
|
|
15
|
+
async function endpointImpl (api, implementation, request) {
|
|
16
|
+
const url = extractPageUrl(request.headers)
|
|
14
17
|
const data = JSON.parse(await body(request))
|
|
15
|
-
if (job.parallel === -1) {
|
|
16
|
-
output.endpoint(url, data)
|
|
17
|
-
}
|
|
18
18
|
try {
|
|
19
19
|
await implementation.call(this, url, data)
|
|
20
|
-
} catch (
|
|
21
|
-
|
|
20
|
+
} catch (error) {
|
|
21
|
+
getOutput(job).endpointError({ api, url, data, error })
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function synchronousEndpoint (implementation) {
|
|
25
|
+
function synchronousEndpoint (api, implementation) {
|
|
26
26
|
return async function (request, response) {
|
|
27
|
-
await endpointImpl(implementation, request)
|
|
27
|
+
await endpointImpl(api, implementation, request)
|
|
28
28
|
response.writeHead(200)
|
|
29
29
|
response.end()
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function endpoint (implementation) {
|
|
33
|
+
function endpoint (api, implementation) {
|
|
34
34
|
return async function (request, response) {
|
|
35
35
|
response.writeHead(200)
|
|
36
36
|
response.end()
|
|
37
|
-
await endpointImpl(implementation, request)
|
|
37
|
+
await endpointImpl(api, implementation, request)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
async function getInjects (...names) {
|
|
42
|
+
return '\n;\n' + (await Promise.all(names.map(name => readFile(join(__dirname, 'inject', `${name}.js`)))))
|
|
43
|
+
.map(buffer => buffer.toString())
|
|
44
|
+
.join('\n;\n')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function contentLength (content) {
|
|
48
|
+
return new TextEncoder().encode(content).length
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function sendScript (response, content) {
|
|
52
|
+
response.writeHead(200, {
|
|
53
|
+
'content-type': 'text/javascript',
|
|
54
|
+
'content-length': contentLength(content),
|
|
55
|
+
'cache-control': 'no-store'
|
|
56
|
+
})
|
|
57
|
+
response.end(content)
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
return job.parallel
|
|
53
61
|
? [{
|
|
54
62
|
// Substitute qunit-redirect to extract test pages
|
|
55
63
|
match: '/resources/sap/ui/qunit/qunit-redirect.js',
|
|
56
|
-
|
|
64
|
+
custom: async (request, response) => {
|
|
65
|
+
sendScript(response, await getInjects('post', 'qunit-redirect'))
|
|
66
|
+
}
|
|
57
67
|
}, {
|
|
58
68
|
// Endpoint to receive test pages
|
|
59
69
|
match: '^/_/addTestPages',
|
|
60
|
-
custom: endpoint(
|
|
61
|
-
let testPageUrls
|
|
62
|
-
if (job.pageFilter) {
|
|
63
|
-
const filter = new RegExp(job.pageFilter)
|
|
64
|
-
testPageUrls = data.filter(name => name.match(filter))
|
|
65
|
-
} else {
|
|
66
|
-
testPageUrls = data
|
|
67
|
-
}
|
|
68
|
-
if (job.pageParams) {
|
|
69
|
-
testPageUrls = testPageUrls.map(url => {
|
|
70
|
-
if (url.includes('?')) {
|
|
71
|
-
return url + '&' + job.pageParams
|
|
72
|
-
}
|
|
73
|
-
return url + '?' + job.pageParams
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
const pages = testPageUrls.reduce((mapping, page) => {
|
|
77
|
-
mapping[page] = filename(page)
|
|
78
|
-
return mapping
|
|
79
|
-
}, {})
|
|
80
|
-
const pagesFileName = join(job.tstReportDir, 'pages.json')
|
|
81
|
-
await writeFile(pagesFileName, JSON.stringify(pages))
|
|
82
|
-
job.testPageUrls = Object.keys(pages) // filter out duplicates
|
|
83
|
-
stop(job, url)
|
|
84
|
-
})
|
|
70
|
+
custom: endpoint('addTestPages', (url, pages) => addTestPages(job, url, pages))
|
|
85
71
|
}, {
|
|
86
72
|
// QUnit hooks
|
|
87
73
|
match: '^/_/qunit-hooks.js',
|
|
@@ -96,13 +82,11 @@ module.exports = job => {
|
|
|
96
82
|
const ui5Request = new Request('GET', request.url)
|
|
97
83
|
ui5Request.internal = true
|
|
98
84
|
const ui5Response = new Response()
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.configuration.dispatch(ui5Request, ui5Response),
|
|
103
|
-
this.configuration.dispatch(hooksRequest, hooksResponse)
|
|
85
|
+
const [inject] = await Promise.all([
|
|
86
|
+
getInjects('post', 'qunit-hooks'),
|
|
87
|
+
this.configuration.dispatch(ui5Request, ui5Response)
|
|
104
88
|
])
|
|
105
|
-
const hooksLength =
|
|
89
|
+
const hooksLength = contentLength(inject)
|
|
106
90
|
const ui5Length = parseInt(ui5Response.headers['content-length'], 10)
|
|
107
91
|
response.writeHead(ui5Response.statusCode, {
|
|
108
92
|
...ui5Response.headers,
|
|
@@ -110,94 +94,56 @@ module.exports = job => {
|
|
|
110
94
|
'cache-control': 'no-store' // for debugging purpose
|
|
111
95
|
})
|
|
112
96
|
response.write(ui5Response.toString())
|
|
113
|
-
response.end(
|
|
97
|
+
response.end(inject)
|
|
114
98
|
}
|
|
115
99
|
}, {
|
|
116
100
|
// Endpoint to receive QUnit.begin
|
|
117
101
|
match: '^/_/QUnit/begin',
|
|
118
|
-
custom: endpoint((url, details) =>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
passed: 0,
|
|
124
|
-
tests: {},
|
|
125
|
-
order: []
|
|
126
|
-
}
|
|
127
|
-
details.modules.forEach(module => {
|
|
128
|
-
module.tests.forEach(test => getPageTest(page, test.testId))
|
|
129
|
-
})
|
|
130
|
-
job.testPages[url] = page
|
|
131
|
-
})
|
|
102
|
+
custom: endpoint('QUnit/begin', (url, details) => begin(job, url, details))
|
|
103
|
+
}, {
|
|
104
|
+
// Endpoint to receive QUnit.testStart
|
|
105
|
+
match: '^/_/QUnit/testStart',
|
|
106
|
+
custom: endpoint('QUnit/testStart', (url, details) => testStart(job, url, details))
|
|
132
107
|
}, {
|
|
133
108
|
// Endpoint to receive QUnit.log
|
|
134
109
|
match: '^/_/QUnit/log',
|
|
135
|
-
custom: synchronousEndpoint(async (url, report) =>
|
|
136
|
-
const page = job.testPages[url]
|
|
137
|
-
if (page.isOpa) {
|
|
138
|
-
const { testId, runtime } = report
|
|
139
|
-
getPageTest(page, testId).timestamps.push(runtime)
|
|
140
|
-
await screenshot(job, url, `${testId}-${runtime}.png`)
|
|
141
|
-
}
|
|
142
|
-
})
|
|
110
|
+
custom: synchronousEndpoint('QUnit/log', async (url, report) => log(job, url, report))
|
|
143
111
|
}, {
|
|
144
112
|
// Endpoint to receive QUnit.testDone
|
|
145
113
|
match: '^/_/QUnit/testDone',
|
|
146
|
-
custom: synchronousEndpoint(async (url, report) =>
|
|
147
|
-
const page = job.testPages[url]
|
|
148
|
-
const { testId } = report
|
|
149
|
-
if (report.failed) {
|
|
150
|
-
await screenshot(job, url, `${testId}.png`)
|
|
151
|
-
job.failed = true
|
|
152
|
-
++page.failed
|
|
153
|
-
} else {
|
|
154
|
-
++page.passed
|
|
155
|
-
}
|
|
156
|
-
getPageTest(page, testId).report = report
|
|
157
|
-
})
|
|
114
|
+
custom: synchronousEndpoint('QUnit/testDone', async (url, report) => testDone(job, url, report))
|
|
158
115
|
}, {
|
|
159
116
|
// Endpoint to receive QUnit.done
|
|
160
117
|
match: '^/_/QUnit/done',
|
|
161
|
-
custom: endpoint(async (url, report) =>
|
|
162
|
-
const page = job.testPages[url]
|
|
163
|
-
if (page) {
|
|
164
|
-
await screenshot(job, url, 'screenshot.png')
|
|
165
|
-
if (report.__coverage__) {
|
|
166
|
-
const coverageFileName = join(job.covTempDir, `${filename(url)}.json`)
|
|
167
|
-
await writeFile(coverageFileName, JSON.stringify(report.__coverage__))
|
|
168
|
-
delete report.__coverage__
|
|
169
|
-
}
|
|
170
|
-
page.report = report
|
|
171
|
-
}
|
|
172
|
-
stop(job, url)
|
|
173
|
-
})
|
|
118
|
+
custom: endpoint('QUnit/done', async (url, report) => done(job, url, report))
|
|
174
119
|
}, {
|
|
175
120
|
// UI to follow progress
|
|
176
121
|
match: '^/_/progress.html',
|
|
177
|
-
file:
|
|
122
|
+
file: job.progressPage
|
|
123
|
+
}, {
|
|
124
|
+
// Report 'main' substituted for progress
|
|
125
|
+
match: '^/_/report/main.js',
|
|
126
|
+
file: join(__dirname, 'defaults/report/progress.js')
|
|
127
|
+
}, {
|
|
128
|
+
// Other report resources
|
|
129
|
+
match: '^/_/report/(.*)',
|
|
130
|
+
file: join(__dirname, 'defaults/report/$1')
|
|
131
|
+
}, {
|
|
132
|
+
// punybind
|
|
133
|
+
match: '^/_/punybind.js',
|
|
134
|
+
file: join(__dirname, '../node_modules/punybind/dist/punybind.js')
|
|
135
|
+
}, {
|
|
136
|
+
// punyexpr
|
|
137
|
+
match: '^/_/punyexpr.js',
|
|
138
|
+
file: join(__dirname, '../node_modules/punyexpr/dist/punyexpr.js')
|
|
178
139
|
}, {
|
|
179
140
|
// Endpoint to follow progress
|
|
180
|
-
match: '^/_/progress',
|
|
181
|
-
custom:
|
|
182
|
-
const json = JSON.stringify(job, (key, value) => {
|
|
183
|
-
if (((key === 'tests' || key === 'browsers') && typeof value === 'object') ||
|
|
184
|
-
(key === 'order' && Array.isArray(value))
|
|
185
|
-
) {
|
|
186
|
-
return undefined // Filter out
|
|
187
|
-
}
|
|
188
|
-
return value
|
|
189
|
-
})
|
|
190
|
-
const length = (new TextEncoder().encode(json)).length
|
|
191
|
-
response.writeHead(200, {
|
|
192
|
-
'Content-Type': 'application/json',
|
|
193
|
-
'Content-Length': length
|
|
194
|
-
})
|
|
195
|
-
response.end(json)
|
|
196
|
-
}
|
|
141
|
+
match: '^/_/progress(?:\\?page=([^&]*)(?:&test=([^&]*))?)?',
|
|
142
|
+
custom: (request, response, pageId, testId) => getJobProgress(job, request, response, pageId, testId)
|
|
197
143
|
}, {
|
|
198
144
|
// Endpoint to coverage files
|
|
199
145
|
match: '^/_/coverage/(.*)',
|
|
200
|
-
file: join(job.
|
|
146
|
+
file: join(job.coverageReportDir, '$1')
|
|
201
147
|
}, {
|
|
202
148
|
// Endpoint to report
|
|
203
149
|
match: '^/_/report.html',
|
|
@@ -205,7 +151,7 @@ module.exports = job => {
|
|
|
205
151
|
}, {
|
|
206
152
|
// Endpoint to report files
|
|
207
153
|
match: '^/_/(.*)',
|
|
208
|
-
file: join(job.
|
|
154
|
+
file: join(job.reportDir, '$1')
|
|
209
155
|
}]
|
|
210
156
|
: []
|
|
211
157
|
}
|
package/src/error.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class UTRError extends Error {
|
|
2
|
+
get code () { return this._code }
|
|
3
|
+
|
|
4
|
+
constructor (error, details) {
|
|
5
|
+
super()
|
|
6
|
+
this.name = `UTRError:${error.name}`
|
|
7
|
+
this._code = error.code
|
|
8
|
+
if (details) {
|
|
9
|
+
this.message = details
|
|
10
|
+
} else {
|
|
11
|
+
this.message = error.name
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const errors = [{
|
|
17
|
+
name: 'GENERIC'
|
|
18
|
+
}, {
|
|
19
|
+
name: 'NPM_FAILED'
|
|
20
|
+
}, {
|
|
21
|
+
name: 'MISSING_OR_INVALID_BROWSER_CAPABILITIES'
|
|
22
|
+
}, {
|
|
23
|
+
name: 'BROWSER_PROBE_FAILED'
|
|
24
|
+
}, {
|
|
25
|
+
name: 'BROWSER_FAILED'
|
|
26
|
+
}, {
|
|
27
|
+
name: 'BROWSER_SCREENSHOT_FAILED'
|
|
28
|
+
}, {
|
|
29
|
+
name: 'BROWSER_SCREENSHOT_TIMEOUT'
|
|
30
|
+
}, {
|
|
31
|
+
name: 'BROWSER_SCREENSHOT_NOT_SUPPORTED'
|
|
32
|
+
}, {
|
|
33
|
+
name: 'QUNIT_ERROR'
|
|
34
|
+
}, {
|
|
35
|
+
name: 'MODE_INCOMPATIBLE_OPTION'
|
|
36
|
+
}, {
|
|
37
|
+
name: 'BROWSER_MISS_SCRIPTS_CAPABILITY'
|
|
38
|
+
}, {
|
|
39
|
+
name: 'NPM_DEPENDENCY_NOT_FOUND'
|
|
40
|
+
}]
|
|
41
|
+
|
|
42
|
+
errors.forEach((error, index) => {
|
|
43
|
+
error.code = -1 - index
|
|
44
|
+
UTRError[`${error.name}_CODE`] = error.code
|
|
45
|
+
UTRError[error.name] = function (details = '') {
|
|
46
|
+
return new UTRError(error, details)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
UTRError
|
|
52
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { UTRError } = require('./error')
|
|
2
|
+
|
|
3
|
+
describe('src/errors', () => {
|
|
4
|
+
it('generates a fully documented error', () => {
|
|
5
|
+
const error = UTRError.NPM_FAILED('test')
|
|
6
|
+
expect(error.name).toStrictEqual('UTRError:NPM_FAILED')
|
|
7
|
+
expect(error.code).toBeLessThan(0)
|
|
8
|
+
expect(error.message).toStrictEqual('test')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('generates a generic error', () => {
|
|
12
|
+
const error = UTRError.NPM_FAILED()
|
|
13
|
+
expect(error.name).toStrictEqual('UTRError:NPM_FAILED')
|
|
14
|
+
expect(error.code).toBeLessThan(0)
|
|
15
|
+
expect(error.message).toStrictEqual('NPM_FAILED')
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const send = (response, obj) => {
|
|
4
|
+
let json
|
|
5
|
+
if (typeof obj !== 'string') {
|
|
6
|
+
json = JSON.stringify(obj, undefined, 2)
|
|
7
|
+
} else {
|
|
8
|
+
json = obj
|
|
9
|
+
}
|
|
10
|
+
const length = (new TextEncoder().encode(json)).length
|
|
11
|
+
response.writeHead(200, {
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
'Content-Length': length
|
|
14
|
+
})
|
|
15
|
+
response.end(json)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const notFound = response => {
|
|
19
|
+
response.writeHead(404)
|
|
20
|
+
response.end()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
async getJobProgress (job, request, response, pageId, testId) {
|
|
25
|
+
if (pageId) {
|
|
26
|
+
const url = Object.keys(job.qunitPages).find(pageUrl => job.qunitPages[pageUrl].id === pageId)
|
|
27
|
+
if (!url) {
|
|
28
|
+
return notFound(response)
|
|
29
|
+
}
|
|
30
|
+
const qunitPage = { url, ...job.qunitPages[url] }
|
|
31
|
+
if (!testId) {
|
|
32
|
+
return send(response, JSON.stringify(qunitPage, (key, value) => {
|
|
33
|
+
if (key === 'logs') {
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
}, 2))
|
|
38
|
+
}
|
|
39
|
+
let test
|
|
40
|
+
let moduleName
|
|
41
|
+
qunitPage.modules.every(module => module.tests.every(candidate => {
|
|
42
|
+
if (candidate.testId === testId) {
|
|
43
|
+
moduleName = module.name
|
|
44
|
+
test = candidate
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
return true
|
|
48
|
+
}))
|
|
49
|
+
if (!test) {
|
|
50
|
+
return notFound(response)
|
|
51
|
+
}
|
|
52
|
+
return send(response, {
|
|
53
|
+
url,
|
|
54
|
+
pageId,
|
|
55
|
+
module: moduleName,
|
|
56
|
+
...test
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
send(response, JSON.stringify({
|
|
60
|
+
...job,
|
|
61
|
+
status: job.status
|
|
62
|
+
}, (key, value) => {
|
|
63
|
+
if (key === 'modules') {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
return value
|
|
67
|
+
}, 2))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const { getJobProgress } = require('./get-job-progress')
|
|
2
|
+
const { Response } = require('reserve')
|
|
3
|
+
|
|
4
|
+
describe('src/get-job-progress', () => {
|
|
5
|
+
const url = 'https://localhost:8080/tests/unitTests.qunit.html'
|
|
6
|
+
const id = 'abcdef'
|
|
7
|
+
const firstTestId = '8ef4ee8b'
|
|
8
|
+
|
|
9
|
+
const job = {
|
|
10
|
+
cwd: '.',
|
|
11
|
+
port: 8080,
|
|
12
|
+
ui5: 'https://ui5.sap.com',
|
|
13
|
+
testPageUrls: [url],
|
|
14
|
+
qunitPages: {
|
|
15
|
+
[url]: {
|
|
16
|
+
id,
|
|
17
|
+
start: '2023-01-09T12:49:34.255Z',
|
|
18
|
+
failed: 0,
|
|
19
|
+
passed: 2,
|
|
20
|
+
count: 3,
|
|
21
|
+
modules: [{
|
|
22
|
+
name: 'module',
|
|
23
|
+
tests: [{
|
|
24
|
+
name: 'First test',
|
|
25
|
+
testId: firstTestId,
|
|
26
|
+
logs: ['whatever it contains'],
|
|
27
|
+
report: {
|
|
28
|
+
skipped: false,
|
|
29
|
+
todo: false,
|
|
30
|
+
failed: 0,
|
|
31
|
+
passed: 1,
|
|
32
|
+
total: 1,
|
|
33
|
+
runtime: 3
|
|
34
|
+
}
|
|
35
|
+
}, {
|
|
36
|
+
name: 'Second test',
|
|
37
|
+
testId: '35d36b9d',
|
|
38
|
+
logs: [],
|
|
39
|
+
report: {
|
|
40
|
+
skipped: false,
|
|
41
|
+
todo: false,
|
|
42
|
+
failed: 0,
|
|
43
|
+
passed: 1,
|
|
44
|
+
total: 1,
|
|
45
|
+
runtime: 5
|
|
46
|
+
}
|
|
47
|
+
}, {
|
|
48
|
+
name: 'Third test',
|
|
49
|
+
testId: 'e32c4c98',
|
|
50
|
+
logs: [],
|
|
51
|
+
report: {
|
|
52
|
+
skipped: false,
|
|
53
|
+
todo: false,
|
|
54
|
+
failed: 1,
|
|
55
|
+
passed: 0,
|
|
56
|
+
total: 1,
|
|
57
|
+
runtime: 3
|
|
58
|
+
}
|
|
59
|
+
}]
|
|
60
|
+
}]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
it('returns job without modules', async () => {
|
|
66
|
+
const response = new Response()
|
|
67
|
+
await getJobProgress(job, {}, response)
|
|
68
|
+
await response.waitForFinish()
|
|
69
|
+
const json = JSON.parse(response.toString())
|
|
70
|
+
expect(json).toStrictEqual({
|
|
71
|
+
cwd: '.',
|
|
72
|
+
port: 8080,
|
|
73
|
+
ui5: 'https://ui5.sap.com',
|
|
74
|
+
testPageUrls: [url],
|
|
75
|
+
qunitPages: {
|
|
76
|
+
[url]: {
|
|
77
|
+
id,
|
|
78
|
+
start: '2023-01-09T12:49:34.255Z',
|
|
79
|
+
failed: 0,
|
|
80
|
+
passed: 2,
|
|
81
|
+
count: 3
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('returns page without logs from page id', async () => {
|
|
88
|
+
const response = new Response()
|
|
89
|
+
await getJobProgress(job, {}, response, id)
|
|
90
|
+
await response.waitForFinish()
|
|
91
|
+
const json = JSON.parse(response.toString())
|
|
92
|
+
expect(json).toStrictEqual({
|
|
93
|
+
url,
|
|
94
|
+
id,
|
|
95
|
+
start: '2023-01-09T12:49:34.255Z',
|
|
96
|
+
failed: 0,
|
|
97
|
+
passed: 2,
|
|
98
|
+
count: 3,
|
|
99
|
+
modules: [{
|
|
100
|
+
name: 'module',
|
|
101
|
+
tests: [{
|
|
102
|
+
name: 'First test',
|
|
103
|
+
testId: '8ef4ee8b',
|
|
104
|
+
report: {
|
|
105
|
+
skipped: false,
|
|
106
|
+
todo: false,
|
|
107
|
+
failed: 0,
|
|
108
|
+
passed: 1,
|
|
109
|
+
total: 1,
|
|
110
|
+
runtime: 3
|
|
111
|
+
}
|
|
112
|
+
}, {
|
|
113
|
+
name: 'Second test',
|
|
114
|
+
testId: '35d36b9d',
|
|
115
|
+
report: {
|
|
116
|
+
skipped: false,
|
|
117
|
+
todo: false,
|
|
118
|
+
failed: 0,
|
|
119
|
+
passed: 1,
|
|
120
|
+
total: 1,
|
|
121
|
+
runtime: 5
|
|
122
|
+
}
|
|
123
|
+
}, {
|
|
124
|
+
name: 'Third test',
|
|
125
|
+
testId: 'e32c4c98',
|
|
126
|
+
report: {
|
|
127
|
+
skipped: false,
|
|
128
|
+
todo: false,
|
|
129
|
+
failed: 1,
|
|
130
|
+
passed: 0,
|
|
131
|
+
total: 1,
|
|
132
|
+
runtime: 3
|
|
133
|
+
}
|
|
134
|
+
}]
|
|
135
|
+
}]
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('returns 404 if page id is unknown', async () => {
|
|
140
|
+
const response = new Response()
|
|
141
|
+
await getJobProgress(job, {}, response, 'unknown')
|
|
142
|
+
await response.waitForFinish()
|
|
143
|
+
expect(response.statusCode).toBe(404)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('returns test page and test id', async () => {
|
|
147
|
+
const response = new Response()
|
|
148
|
+
await getJobProgress(job, {}, response, id, firstTestId)
|
|
149
|
+
await response.waitForFinish()
|
|
150
|
+
const json = JSON.parse(response.toString())
|
|
151
|
+
expect(json).toStrictEqual({
|
|
152
|
+
url,
|
|
153
|
+
pageId: id,
|
|
154
|
+
module: 'module',
|
|
155
|
+
name: 'First test',
|
|
156
|
+
testId: firstTestId,
|
|
157
|
+
logs: ['whatever it contains'],
|
|
158
|
+
report: {
|
|
159
|
+
skipped: false,
|
|
160
|
+
todo: false,
|
|
161
|
+
failed: 0,
|
|
162
|
+
passed: 1,
|
|
163
|
+
total: 1,
|
|
164
|
+
runtime: 3
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('returns 404 if test id is unknown', async () => {
|
|
170
|
+
const response = new Response()
|
|
171
|
+
await getJobProgress(job, {}, response, id, 'unknown')
|
|
172
|
+
await response.waitForFinish()
|
|
173
|
+
expect(response.statusCode).toBe(404)
|
|
174
|
+
})
|
|
175
|
+
})
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
if (window['ui5-test-runner/post']) {
|
|
5
|
+
return
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const base = window['ui5-test-runner/base-host'] || ''
|
|
9
|
+
|
|
10
|
+
let lastPost = Promise.resolve()
|
|
11
|
+
let UI5Object
|
|
12
|
+
|
|
13
|
+
function stringify (data) {
|
|
14
|
+
const objects = []
|
|
15
|
+
const referenced = []
|
|
16
|
+
if (!UI5Object && window.sap && window.sap.ui && window.sap.ui.base) {
|
|
17
|
+
UI5Object = window.sap.ui.base.Object
|
|
18
|
+
}
|
|
19
|
+
const ui5Summary = obj => {
|
|
20
|
+
const id = obj.getId && obj.getId()
|
|
21
|
+
const className = obj.getMetadata().getName()
|
|
22
|
+
return {
|
|
23
|
+
'ui5:class': className,
|
|
24
|
+
'ui5:id': id
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const simple = JSON.stringify(data, function (key, value) {
|
|
28
|
+
if (typeof value === 'object' && value) {
|
|
29
|
+
if (UI5Object && value instanceof UI5Object) {
|
|
30
|
+
return ui5Summary(value)
|
|
31
|
+
}
|
|
32
|
+
const id = objects.indexOf(value)
|
|
33
|
+
if (id !== -1) {
|
|
34
|
+
referenced[id] = true
|
|
35
|
+
return null // Skip error and check all references
|
|
36
|
+
}
|
|
37
|
+
objects.push(value)
|
|
38
|
+
}
|
|
39
|
+
return value
|
|
40
|
+
})
|
|
41
|
+
if (referenced.length === 0) {
|
|
42
|
+
return simple
|
|
43
|
+
}
|
|
44
|
+
const stringified = []
|
|
45
|
+
return JSON.stringify(data, function (key, value) {
|
|
46
|
+
if (typeof value === 'object' && value) {
|
|
47
|
+
if (UI5Object && value instanceof UI5Object) {
|
|
48
|
+
return ui5Summary(value)
|
|
49
|
+
}
|
|
50
|
+
const id = objects.indexOf(value)
|
|
51
|
+
if (referenced[id]) {
|
|
52
|
+
if (stringified[id]) {
|
|
53
|
+
return { 'circular:ref': id }
|
|
54
|
+
}
|
|
55
|
+
stringified[id] = true
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
return {
|
|
58
|
+
'circular:id': id,
|
|
59
|
+
'circular:array': [].concat(value) // 'new' object
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return Object.assign({
|
|
63
|
+
'circular:id': id
|
|
64
|
+
}, value)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return value
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
window['ui5-test-runner/stringify'] = stringify
|
|
72
|
+
|
|
73
|
+
window['ui5-test-runner/post'] = function post (url, data) {
|
|
74
|
+
function request () {
|
|
75
|
+
return new Promise(function (resolve, reject) {
|
|
76
|
+
const xhr = new XMLHttpRequest()
|
|
77
|
+
xhr.addEventListener('load', () => {
|
|
78
|
+
resolve(xhr.responseText)
|
|
79
|
+
})
|
|
80
|
+
xhr.addEventListener('error', () => {
|
|
81
|
+
reject(xhr.statusText)
|
|
82
|
+
})
|
|
83
|
+
xhr.open('POST', base + '/_/' + url)
|
|
84
|
+
xhr.setRequestHeader('x-page-url', location)
|
|
85
|
+
xhr.send(stringify(data))
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
lastPost = lastPost
|
|
89
|
+
.then(undefined, function (reason) {
|
|
90
|
+
console.error('Failed to POST to ' + url + '\nreason: ' + reason.toString())
|
|
91
|
+
throw new Error('failed')
|
|
92
|
+
})
|
|
93
|
+
.then(request)
|
|
94
|
+
return lastPost
|
|
95
|
+
}
|
|
96
|
+
}())
|