ui5-test-runner 1.1.4 → 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 +30 -12
- 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 -2
- 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/report.html +0 -202
- /package/{defaults → src/defaults}/nyc.json +0 -0
package/src/browsers.js
CHANGED
|
@@ -2,55 +2,179 @@
|
|
|
2
2
|
|
|
3
3
|
const { fork } = require('child_process')
|
|
4
4
|
const { join } = require('path')
|
|
5
|
-
const {
|
|
5
|
+
const { writeFile, readFile, open, stat, unlink } = require('fs/promises')
|
|
6
|
+
const { recreateDir, filename, allocPromise } = require('./tools')
|
|
6
7
|
const { getPageTimeout } = require('./timeout')
|
|
7
|
-
const
|
|
8
|
+
const { getOutput } = require('./output')
|
|
9
|
+
const { resolvePackage } = require('./npm')
|
|
10
|
+
const { UTRError } = require('./error')
|
|
11
|
+
const { $browsers } = require('./symbols')
|
|
8
12
|
|
|
9
13
|
let lastScreenshotId = 0
|
|
10
14
|
const screenshots = {}
|
|
11
15
|
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
async function instantiate (job, config) {
|
|
17
|
+
const { dir, url } = config
|
|
18
|
+
await recreateDir(dir)
|
|
19
|
+
const browserConfig = {
|
|
20
|
+
capabilities: job.browserCapabilities,
|
|
21
|
+
modules: job.browserModules,
|
|
22
|
+
...config,
|
|
23
|
+
args: job.browserArgs
|
|
24
|
+
}
|
|
25
|
+
const browserConfigPath = join(dir, 'browser.json')
|
|
26
|
+
await writeFile(browserConfigPath, JSON.stringify(browserConfig, undefined, 2))
|
|
27
|
+
const stdoutFilename = join(dir, 'stdout.txt')
|
|
28
|
+
const stderrFilename = join(dir, 'stderr.txt')
|
|
29
|
+
const stdout = await open(stdoutFilename, 'w')
|
|
30
|
+
const stderr = await open(stderrFilename, 'w')
|
|
31
|
+
const childProcess = fork(job.browser, [browserConfigPath], {
|
|
32
|
+
stdio: [0, stdout, stderr, 'ipc']
|
|
33
|
+
})
|
|
34
|
+
const { promise, resolve } = allocPromise()
|
|
35
|
+
childProcess.on('close', async code => {
|
|
36
|
+
await stdout.close()
|
|
37
|
+
await stderr.close()
|
|
38
|
+
if (code !== 0) {
|
|
39
|
+
getOutput(job).browserFailed(url, code, dir)
|
|
40
|
+
}
|
|
41
|
+
resolve(code)
|
|
42
|
+
})
|
|
43
|
+
childProcess.closed = promise
|
|
44
|
+
childProcess.stdoutFilename = stdoutFilename
|
|
45
|
+
childProcess.stderrFilename = stderrFilename
|
|
46
|
+
return childProcess
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function probe (job) {
|
|
50
|
+
if (job.browserCapabilities) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const output = getOutput(job)
|
|
54
|
+
job.status = 'Probing browser instantiation command'
|
|
55
|
+
|
|
56
|
+
async function execute (folder) {
|
|
57
|
+
const dir = join(job.reportDir, folder)
|
|
58
|
+
const capabilities = join(dir, 'capabilities.json')
|
|
59
|
+
const childProcess = await instantiate(job, {
|
|
60
|
+
url: 'about:blank',
|
|
61
|
+
capabilities,
|
|
62
|
+
dir
|
|
63
|
+
})
|
|
64
|
+
const code = await childProcess.closed
|
|
65
|
+
if (code !== 0) {
|
|
66
|
+
throw UTRError.BROWSER_PROBE_FAILED(code.toString())
|
|
67
|
+
}
|
|
68
|
+
let browserCapabilities
|
|
69
|
+
try {
|
|
70
|
+
browserCapabilities = Object.assign({
|
|
71
|
+
modules: [],
|
|
72
|
+
screenshot: null,
|
|
73
|
+
scripts: false,
|
|
74
|
+
parallel: true,
|
|
75
|
+
traces: []
|
|
76
|
+
}, JSON.parse((await readFile(capabilities)).toString()))
|
|
77
|
+
} catch (e) {
|
|
78
|
+
throw UTRError.MISSING_OR_INVALID_BROWSER_CAPABILITIES(e.message)
|
|
79
|
+
}
|
|
80
|
+
return browserCapabilities
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const browserCapabilities = await execute('probe')
|
|
84
|
+
job.browserCapabilities = browserCapabilities
|
|
85
|
+
|
|
86
|
+
const { modules } = browserCapabilities
|
|
87
|
+
const resolvedModules = {}
|
|
88
|
+
if (modules.length) {
|
|
89
|
+
for await (const name of browserCapabilities.modules) {
|
|
90
|
+
resolvedModules[name] = await resolvePackage(job, name)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
job.browserModules = resolvedModules
|
|
94
|
+
if (browserCapabilities['probe-with-modules']) {
|
|
95
|
+
job.browserCapabilities = await execute('probe/with-modules')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
output.browserCapabilities(job.browserCapabilities)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function start (job, url, scripts = []) {
|
|
102
|
+
const output = getOutput(job)
|
|
103
|
+
if (!job[$browsers]) {
|
|
104
|
+
job[$browsers] = {}
|
|
105
|
+
}
|
|
106
|
+
output.browserStart(url)
|
|
107
|
+
const reportDir = join(job.reportDir, filename(url))
|
|
108
|
+
const resolvedScripts = []
|
|
109
|
+
for await (const script of scripts) {
|
|
110
|
+
if (script.endsWith('.js')) {
|
|
111
|
+
const scriptFilename = join(__dirname, 'inject', script)
|
|
112
|
+
const scriptContent = (await readFile(scriptFilename)).toString()
|
|
113
|
+
resolvedScripts.push(scriptContent)
|
|
114
|
+
} else {
|
|
115
|
+
resolvedScripts.push(script)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (resolvedScripts.length) {
|
|
119
|
+
resolvedScripts.unshift(`window['ui5-test-runner/base-host'] = 'http://localhost:${job.port}'
|
|
120
|
+
`)
|
|
121
|
+
}
|
|
23
122
|
const pageBrowser = {
|
|
24
|
-
|
|
123
|
+
url,
|
|
25
124
|
reportDir,
|
|
26
|
-
|
|
125
|
+
scripts: resolvedScripts,
|
|
27
126
|
retry: 0
|
|
28
127
|
}
|
|
29
|
-
const promise
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
128
|
+
const { promise, resolve, reject } = allocPromise()
|
|
129
|
+
pageBrowser.done = value => {
|
|
130
|
+
delete job[$browsers][url]
|
|
131
|
+
resolve(value)
|
|
132
|
+
}
|
|
133
|
+
pageBrowser.failed = reason => {
|
|
134
|
+
delete job[$browsers][url]
|
|
135
|
+
reject(reason)
|
|
136
|
+
}
|
|
137
|
+
job[$browsers][url] = pageBrowser
|
|
138
|
+
await run(job, pageBrowser)
|
|
139
|
+
await promise
|
|
140
|
+
output.browserStopped(url)
|
|
37
141
|
}
|
|
38
142
|
|
|
39
143
|
async function run (job, pageBrowser) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
144
|
+
const output = getOutput(job)
|
|
145
|
+
const { url, retry, reportDir, scripts } = pageBrowser
|
|
146
|
+
let dir = reportDir
|
|
147
|
+
if (retry) {
|
|
148
|
+
output.browserRetry(url, retry)
|
|
149
|
+
dir = join(dir, retry.toString())
|
|
150
|
+
if (pageBrowser.console.count) {
|
|
151
|
+
try {
|
|
152
|
+
await pageBrowser.console.flush
|
|
153
|
+
.then(() => unlink(join(reportDir, 'console.jsonl')))
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
157
|
+
}
|
|
43
158
|
}
|
|
44
|
-
|
|
159
|
+
pageBrowser.console = {
|
|
160
|
+
count: 0,
|
|
161
|
+
byApi: {},
|
|
162
|
+
flush: Promise.resolve()
|
|
163
|
+
}
|
|
164
|
+
await recreateDir(dir)
|
|
45
165
|
delete pageBrowser.stopped
|
|
46
|
-
const childProcess =
|
|
47
|
-
|
|
166
|
+
const childProcess = await instantiate(job, {
|
|
167
|
+
url,
|
|
168
|
+
retry,
|
|
169
|
+
scripts,
|
|
170
|
+
dir
|
|
171
|
+
})
|
|
48
172
|
pageBrowser.childProcess = childProcess
|
|
49
173
|
const timeout = getPageTimeout(job)
|
|
50
174
|
if (timeout) {
|
|
51
175
|
pageBrowser.timeoutId = setTimeout(() => {
|
|
52
|
-
output.browserTimeout(
|
|
53
|
-
stop(job,
|
|
176
|
+
output.browserTimeout(url, dir)
|
|
177
|
+
stop(job, url)
|
|
54
178
|
}, timeout)
|
|
55
179
|
}
|
|
56
180
|
childProcess.on('message', message => {
|
|
@@ -58,63 +182,100 @@ async function run (job, pageBrowser) {
|
|
|
58
182
|
const { id } = message
|
|
59
183
|
screenshots[id]()
|
|
60
184
|
delete screenshots[id]
|
|
61
|
-
} else
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
185
|
+
} else if (message.command === 'console') {
|
|
186
|
+
++pageBrowser.console.count
|
|
187
|
+
if (!pageBrowser.console.byApi[message.api]) {
|
|
188
|
+
pageBrowser.console.byApi[message.api] = 1
|
|
189
|
+
} else {
|
|
190
|
+
++pageBrowser.console.byApi[message.api]
|
|
191
|
+
}
|
|
192
|
+
pageBrowser.console.flush = pageBrowser.console.flush
|
|
193
|
+
.then(() => writeFile(join(reportDir, 'console.jsonl'), JSON.stringify({
|
|
194
|
+
t: message.t,
|
|
195
|
+
api: message.api,
|
|
196
|
+
args: message.args
|
|
197
|
+
}) + '\n', {
|
|
198
|
+
flag: 'a+'
|
|
199
|
+
}))
|
|
65
200
|
}
|
|
66
201
|
})
|
|
67
|
-
childProcess.on('close',
|
|
202
|
+
childProcess.on('close', async code => {
|
|
68
203
|
if (!pageBrowser.stopped) {
|
|
69
|
-
|
|
70
|
-
|
|
204
|
+
if (code === 0) {
|
|
205
|
+
output.browserClosed(url, code, dir)
|
|
206
|
+
}
|
|
207
|
+
childProcess.closed.then(() => stop(job, url, true))
|
|
71
208
|
}
|
|
72
209
|
})
|
|
73
|
-
if (!job.browserCapabilities) {
|
|
74
|
-
childProcess.send({ command: 'capabilities' })
|
|
75
|
-
}
|
|
76
210
|
}
|
|
77
211
|
|
|
78
|
-
async function screenshot (job,
|
|
79
|
-
if (
|
|
80
|
-
|
|
212
|
+
async function screenshot (job, url, filename) {
|
|
213
|
+
if (!job.browserCapabilities.screenshot) {
|
|
214
|
+
throw UTRError.BROWSER_SCREENSHOT_NOT_SUPPORTED()
|
|
81
215
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
216
|
+
try {
|
|
217
|
+
const { childProcess, reportDir } = job[$browsers][url]
|
|
218
|
+
const absoluteFilename = join(reportDir, filename + job.browserCapabilities.screenshot)
|
|
85
219
|
if (childProcess.connected) {
|
|
86
220
|
const id = ++lastScreenshotId
|
|
87
|
-
const promise
|
|
88
|
-
|
|
89
|
-
})
|
|
221
|
+
const { promise, resolve, reject } = allocPromise()
|
|
222
|
+
screenshots[id] = resolve
|
|
90
223
|
childProcess.send({
|
|
91
224
|
id,
|
|
92
225
|
command: 'screenshot',
|
|
93
|
-
filename
|
|
226
|
+
filename: absoluteFilename
|
|
94
227
|
})
|
|
228
|
+
const timeoutId = setTimeout(() => {
|
|
229
|
+
reject(UTRError.BROWSER_SCREENSHOT_TIMEOUT())
|
|
230
|
+
}, job.screenshotTimeout)
|
|
95
231
|
await promise
|
|
232
|
+
clearTimeout(timeoutId)
|
|
233
|
+
const result = await stat(absoluteFilename)
|
|
234
|
+
if (!result.isFile() || result.size === 0) {
|
|
235
|
+
throw new Error('File expected')
|
|
236
|
+
}
|
|
237
|
+
return absoluteFilename
|
|
96
238
|
}
|
|
239
|
+
} catch (e) {
|
|
240
|
+
if (e.code === UTRError.BROWSER_SCREENSHOT_TIMEOUT_CODE) {
|
|
241
|
+
throw e
|
|
242
|
+
}
|
|
243
|
+
throw UTRError.BROWSER_SCREENSHOT_FAILED(e.toString())
|
|
97
244
|
}
|
|
98
245
|
}
|
|
99
246
|
|
|
100
|
-
async function stop (job,
|
|
101
|
-
const pageBrowser = job
|
|
247
|
+
async function stop (job, url, retry = false) {
|
|
248
|
+
const pageBrowser = job[$browsers][url]
|
|
102
249
|
if (pageBrowser) {
|
|
103
250
|
pageBrowser.stopped = true
|
|
104
|
-
const { childProcess, done, timeoutId } = pageBrowser
|
|
251
|
+
const { childProcess, done, failed, timeoutId } = pageBrowser
|
|
105
252
|
if (timeoutId) {
|
|
106
253
|
clearTimeout(timeoutId)
|
|
107
254
|
}
|
|
108
255
|
if (childProcess.connected) {
|
|
109
|
-
|
|
256
|
+
/* istanbul ignore else */
|
|
257
|
+
if (!job.debugKeepBrowserOpen) {
|
|
258
|
+
childProcess.send({ command: 'stop' })
|
|
259
|
+
}
|
|
260
|
+
const { promise: closeTimeout, resolve } = allocPromise()
|
|
261
|
+
const timeoutId = setTimeout(resolve, job.browserCloseTimeout)
|
|
262
|
+
await Promise.race([
|
|
263
|
+
childProcess.closed,
|
|
264
|
+
closeTimeout
|
|
265
|
+
])
|
|
266
|
+
clearTimeout(timeoutId)
|
|
110
267
|
}
|
|
111
|
-
|
|
112
|
-
|
|
268
|
+
await pageBrowser.console.flush
|
|
269
|
+
if (retry) {
|
|
270
|
+
if (++pageBrowser.retry <= job.browserRetry) {
|
|
271
|
+
run(job, pageBrowser)
|
|
272
|
+
} else {
|
|
273
|
+
failed(UTRError.BROWSER_FAILED())
|
|
274
|
+
}
|
|
113
275
|
} else {
|
|
114
|
-
delete job.browsers[relativeUrl]
|
|
115
276
|
done()
|
|
116
277
|
}
|
|
117
278
|
}
|
|
118
279
|
}
|
|
119
280
|
|
|
120
|
-
module.exports = { start, screenshot, stop }
|
|
281
|
+
module.exports = { probe, start, screenshot, stop }
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { check, serve, body } = require('reserve')
|
|
4
|
+
const { probe, start, stop } = require('../browsers')
|
|
5
|
+
const { join } = require('path')
|
|
6
|
+
const { getOutput } = require('../output')
|
|
7
|
+
const EventEmitter = require('events')
|
|
8
|
+
const { performance } = require('perf_hooks')
|
|
9
|
+
const { cleanDir, allocPromise, filename } = require('../tools')
|
|
10
|
+
const { $browsers } = require('../symbols')
|
|
11
|
+
const tests = require('./tests')
|
|
12
|
+
|
|
13
|
+
async function capabilities (job) {
|
|
14
|
+
const output = getOutput(job)
|
|
15
|
+
|
|
16
|
+
async function exit (code) {
|
|
17
|
+
if (job.keepAlive) {
|
|
18
|
+
job.status = 'Serving'
|
|
19
|
+
} else {
|
|
20
|
+
if (job.debugKeepReport) {
|
|
21
|
+
output.wrap(() => console.log('Report folder', job.reportDir, 'not cleaned because of --debug-keep-report.'))
|
|
22
|
+
} else if (code !== 0) {
|
|
23
|
+
output.wrap(() => console.error('Report folder', job.reportDir, 'not cleaned because of errors.'))
|
|
24
|
+
} else {
|
|
25
|
+
await cleanDir(job.reportDir)
|
|
26
|
+
}
|
|
27
|
+
output.stop()
|
|
28
|
+
process.exit(code)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
output.reportOnJobProgress()
|
|
34
|
+
try {
|
|
35
|
+
await probe(job)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
output.wrap(() => console.error('Unable to probe'))
|
|
38
|
+
exit(-1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const listeners = []
|
|
42
|
+
|
|
43
|
+
const configuration = await check({
|
|
44
|
+
port: job.port,
|
|
45
|
+
mappings: [
|
|
46
|
+
require('../cors'), {
|
|
47
|
+
custom: async (request, response) => {
|
|
48
|
+
response.setHeader('x-ui5-test-runner', 'capabilities')
|
|
49
|
+
}
|
|
50
|
+
}, {
|
|
51
|
+
match: /x-wait-time=(\d+)/,
|
|
52
|
+
custom: async (request, response, waitTime) => {
|
|
53
|
+
response.setHeader('x-wait-time', waitTime)
|
|
54
|
+
await new Promise(resolve => setTimeout(resolve, parseInt(waitTime)))
|
|
55
|
+
}
|
|
56
|
+
}, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
match: '^/_/(.*)',
|
|
59
|
+
custom: async (request, response, endpoint) => {
|
|
60
|
+
const { referer, 'x-page-url': xPageUrl } = request.headers
|
|
61
|
+
const listenerIndex = (xPageUrl || referer).match(/\blistener=(\d+)/)[1]
|
|
62
|
+
const listener = listeners[listenerIndex]
|
|
63
|
+
listener.emit('endpoint', {
|
|
64
|
+
endpoint,
|
|
65
|
+
body: JSON.parse(await body(request))
|
|
66
|
+
})
|
|
67
|
+
response.writeHead(200)
|
|
68
|
+
response.end()
|
|
69
|
+
}
|
|
70
|
+
}, {
|
|
71
|
+
match: '^/(.*)',
|
|
72
|
+
file: join(__dirname, '$1')
|
|
73
|
+
}]
|
|
74
|
+
})
|
|
75
|
+
const server = serve(configuration)
|
|
76
|
+
if (job.logServer) {
|
|
77
|
+
server.on('redirected', output.redirected)
|
|
78
|
+
}
|
|
79
|
+
await new Promise(resolve => server
|
|
80
|
+
.on('ready', ({ port }) => {
|
|
81
|
+
job.port = port
|
|
82
|
+
resolve()
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
job.status = 'Running tests'
|
|
87
|
+
|
|
88
|
+
const filteredTests = tests
|
|
89
|
+
.filter((test) => !test.for || test.for(job.browserCapabilities))
|
|
90
|
+
.filter(({ name }) => !job.debugCapabilitiesTest || name.startsWith(job.debugCapabilitiesTest))
|
|
91
|
+
output.wrap(() => console.log('Number of tests :', filteredTests.length))
|
|
92
|
+
|
|
93
|
+
let errors = 0
|
|
94
|
+
|
|
95
|
+
const { promise: forEver } = allocPromise()
|
|
96
|
+
|
|
97
|
+
const next = async () => {
|
|
98
|
+
if (filteredTests.length === 0) {
|
|
99
|
+
if (!job[$browsers] || Object.keys(job[$browsers]).length === 0) {
|
|
100
|
+
output.wrap(() => console.log('Done.'))
|
|
101
|
+
exit(errors)
|
|
102
|
+
}
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
const { label, url, scripts, endpoint = () => { } } = filteredTests.shift()
|
|
106
|
+
|
|
107
|
+
const listenerIndex = listeners.length
|
|
108
|
+
const listener = new EventEmitter()
|
|
109
|
+
listeners.push(listener)
|
|
110
|
+
let pageUrl
|
|
111
|
+
if (url.startsWith('http')) {
|
|
112
|
+
pageUrl = url
|
|
113
|
+
} else {
|
|
114
|
+
pageUrl = `http://localhost:${job.port}/tests/${url}`
|
|
115
|
+
}
|
|
116
|
+
if (url.includes('?')) {
|
|
117
|
+
pageUrl += `&listener=${listenerIndex}`
|
|
118
|
+
} else {
|
|
119
|
+
pageUrl += `?listener=${listenerIndex}`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const now = performance.now()
|
|
123
|
+
let timeoutId
|
|
124
|
+
if (!job.debugCapabilitiesNoTimeout) {
|
|
125
|
+
const timeout = job.pageTimeout || 120000
|
|
126
|
+
timeoutId = setTimeout(() => done(`Timeout (${timeout})`), timeout)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function done (error) {
|
|
130
|
+
if (done.called) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
done.called = true
|
|
134
|
+
if (timeoutId) {
|
|
135
|
+
clearTimeout(timeoutId)
|
|
136
|
+
}
|
|
137
|
+
await stop(job, pageUrl)
|
|
138
|
+
const timeSpent = Math.floor(performance.now() - now)
|
|
139
|
+
if (error) {
|
|
140
|
+
output.wrap(() => console.log('❌', label, `[${filename(pageUrl)}]`, error))
|
|
141
|
+
if (job.failFast) {
|
|
142
|
+
exit(1)
|
|
143
|
+
}
|
|
144
|
+
++errors
|
|
145
|
+
} else {
|
|
146
|
+
output.wrap(() => console.log('✔️', label, timeSpent, 'ms'))
|
|
147
|
+
}
|
|
148
|
+
await next()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const context = {
|
|
152
|
+
job
|
|
153
|
+
}
|
|
154
|
+
listener.on('endpoint', async data => {
|
|
155
|
+
try {
|
|
156
|
+
if (await endpoint.call(context, data, pageUrl) !== false) {
|
|
157
|
+
done()
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {
|
|
160
|
+
done(e)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
start(job, pageUrl, scripts)
|
|
165
|
+
.catch(reason => done(reason))
|
|
166
|
+
.then(() => {
|
|
167
|
+
if (!context.stopExpected) {
|
|
168
|
+
done('Failed')
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let parallel
|
|
174
|
+
if (!job.browserCapabilities.parallel) {
|
|
175
|
+
parallel = 1
|
|
176
|
+
} else {
|
|
177
|
+
parallel = job.parallel
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const testsCount = filteredTests.length
|
|
181
|
+
for (let i = 0; i < Math.max(Math.min(parallel, testsCount), 1); ++i) {
|
|
182
|
+
next()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await forEver
|
|
186
|
+
} catch (error) {
|
|
187
|
+
output.wrap(() => console.error(error))
|
|
188
|
+
exit(-1)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
capabilities
|
|
194
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const assert = require('assert')
|
|
4
|
+
|
|
5
|
+
module.exports = [{
|
|
6
|
+
label: 'Loads a page',
|
|
7
|
+
url: 'basic/index.html'
|
|
8
|
+
}, {
|
|
9
|
+
label: 'Loads a UI5 example',
|
|
10
|
+
url: 'basic/ui5.html',
|
|
11
|
+
endpoint: ({ body }) => {
|
|
12
|
+
assert.strictEqual(body['sap.m.Button'], true)
|
|
13
|
+
}
|
|
14
|
+
}, {
|
|
15
|
+
label: 'Loads a UI5 inside an iframe',
|
|
16
|
+
url: 'basic/iframe.html',
|
|
17
|
+
endpoint: ({ body }) => {
|
|
18
|
+
assert.strictEqual(body['sap.m.Button'], true)
|
|
19
|
+
}
|
|
20
|
+
}]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<body>
|
|
4
|
+
<H1>UI5 example</H1>
|
|
5
|
+
<p>Checks if the browser can load a page</p>
|
|
6
|
+
<script
|
|
7
|
+
src="https://ui5.sap.com/resources/sap-ui-core.js"
|
|
8
|
+
id="sap-ui-bootstrap"
|
|
9
|
+
data-sap-ui-libs="sap.m"
|
|
10
|
+
data-sap-ui-theme="sap_fiori_3"
|
|
11
|
+
data-sap-ui-compatVersion="edge">
|
|
12
|
+
</script>
|
|
13
|
+
<script>
|
|
14
|
+
sap.ui.getCore().attachInit(function() {
|
|
15
|
+
const xhr = new XMLHttpRequest()
|
|
16
|
+
xhr.open('POST', '/_/log')
|
|
17
|
+
if (parent !== window) {
|
|
18
|
+
xhr.setRequestHeader('x-page-url', parent.document.location.toString())
|
|
19
|
+
}
|
|
20
|
+
xhr.send('{"sap.m.Button":' + !!sap.m.Button + '}')
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const assert = require('assert')
|
|
4
|
+
|
|
5
|
+
const endpoint = ({ body }) => {
|
|
6
|
+
assert.strictEqual(body.test, true)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = [{
|
|
10
|
+
label: 'Dynamic include',
|
|
11
|
+
url: 'dynamic-include/one.html',
|
|
12
|
+
endpoint
|
|
13
|
+
}, {
|
|
14
|
+
label: 'Dynamic includes',
|
|
15
|
+
url: 'dynamic-include/two.html',
|
|
16
|
+
endpoint
|
|
17
|
+
}, {
|
|
18
|
+
label: 'Dynamic include mixed with a static one',
|
|
19
|
+
url: 'dynamic-include/mix.html',
|
|
20
|
+
endpoint
|
|
21
|
+
}]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<body>
|
|
4
|
+
<H1>basic</H1>
|
|
5
|
+
<p>Checks if the browser can mix static script with a dynamic script inserted with document.write</p>
|
|
6
|
+
<script>
|
|
7
|
+
document.write('<script src="test.js?x-wait-time=1000"><' + '/script>'); // wait 1s before delivering
|
|
8
|
+
</script>
|
|
9
|
+
<script src="post.js"></script>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.test = true
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<body>
|
|
4
|
+
<H1>basic</H1>
|
|
5
|
+
<p>Checks if the browser can load sequentially scripts inserted with document.write</p>
|
|
6
|
+
<script>
|
|
7
|
+
document.write('<script src="test.js?x-wait-time=1000"><' + '/script>'); // wait 1s before delivering
|
|
8
|
+
document.write('<script src="post.js"><' + '/script>');
|
|
9
|
+
</script>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { readdirSync } = require('fs')
|
|
2
|
+
|
|
3
|
+
module.exports = readdirSync(__dirname)
|
|
4
|
+
.filter(name => !name.endsWith('.js'))
|
|
5
|
+
.map(name => {
|
|
6
|
+
const tests = require(`./${name}`)
|
|
7
|
+
if (Array.isArray(tests)) {
|
|
8
|
+
tests.forEach((test, index) => {
|
|
9
|
+
test.name = `${name}:${index}`
|
|
10
|
+
})
|
|
11
|
+
} else {
|
|
12
|
+
tests.name = name
|
|
13
|
+
}
|
|
14
|
+
return tests
|
|
15
|
+
})
|
|
16
|
+
.flat()
|