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
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
const { join } = require('path')
|
|
2
|
+
const { mock } = require('child_process')
|
|
3
|
+
const jobFactory = require('./job')
|
|
4
|
+
const { probe, start, stop, screenshot } = require('./browsers')
|
|
5
|
+
const { $browsers } = require('./symbols')
|
|
6
|
+
const { readFile, writeFile, stat } = require('fs/promises')
|
|
7
|
+
const { createDir, recreateDir, allocPromise, filename } = require('./tools')
|
|
8
|
+
const { UTRError } = require('./error')
|
|
9
|
+
|
|
10
|
+
const cwd = '/test/project'
|
|
11
|
+
const tmp = join(__dirname, '../tmp')
|
|
12
|
+
|
|
13
|
+
describe('src/browser', () => {
|
|
14
|
+
let job
|
|
15
|
+
let remainingChildProcess
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
const reportDir = join(tmp, 'browser')
|
|
19
|
+
await recreateDir(reportDir)
|
|
20
|
+
job = jobFactory.fromObject(cwd, {
|
|
21
|
+
url: 'http://localhost:8080',
|
|
22
|
+
reportDir,
|
|
23
|
+
browserCloseTimeout: 100,
|
|
24
|
+
'--': ['argument1', 'argument2']
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
job.browserRetry = 0 // avoid retry in case of unexpected close
|
|
30
|
+
if (remainingChildProcess) {
|
|
31
|
+
await stop(job, '/test.html')
|
|
32
|
+
remainingChildProcess.close() // stop command not implemented
|
|
33
|
+
await remainingChildProcess.closed
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('probe', () => {
|
|
38
|
+
it('starts the command with a specific config file', async () => {
|
|
39
|
+
let config
|
|
40
|
+
mock({
|
|
41
|
+
api: 'fork',
|
|
42
|
+
scriptPath: job.browser,
|
|
43
|
+
exec: async childProcess => {
|
|
44
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
45
|
+
await writeFile(config.capabilities, '{}')
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
await probe(job)
|
|
49
|
+
expect(config.url).toStrictEqual('about:blank')
|
|
50
|
+
expect(job.browserCapabilities.parallel).toStrictEqual(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('fails if the browser does not generate capabilities', async () => {
|
|
54
|
+
mock({
|
|
55
|
+
api: 'fork',
|
|
56
|
+
scriptPath: job.browser,
|
|
57
|
+
exec: () => {}
|
|
58
|
+
})
|
|
59
|
+
await expect(probe(job)).rejects.toMatchObject({
|
|
60
|
+
name: 'UTRError:MISSING_OR_INVALID_BROWSER_CAPABILITIES'
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('reads and merge browser capabilities', async () => {
|
|
65
|
+
mock({
|
|
66
|
+
api: 'fork',
|
|
67
|
+
scriptPath: job.browser,
|
|
68
|
+
exec: async childProcess => {
|
|
69
|
+
const config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
70
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
71
|
+
screenshot: false,
|
|
72
|
+
traces: ['console']
|
|
73
|
+
}))
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
await probe(job)
|
|
77
|
+
expect(job.browserCapabilities.traces).toStrictEqual(['console'])
|
|
78
|
+
expect(job.browserCapabilities.parallel).toStrictEqual(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('probes only once', async () => {
|
|
82
|
+
let count = 0
|
|
83
|
+
mock({
|
|
84
|
+
api: 'fork',
|
|
85
|
+
scriptPath: job.browser,
|
|
86
|
+
exec: async childProcess => {
|
|
87
|
+
++count
|
|
88
|
+
const config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
89
|
+
await writeFile(config.capabilities, '{}')
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
await probe(job)
|
|
93
|
+
expect(count).toStrictEqual(1)
|
|
94
|
+
await probe(job)
|
|
95
|
+
expect(count).toStrictEqual(1)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('handles failure during probe', async () => {
|
|
99
|
+
mock({
|
|
100
|
+
api: 'fork',
|
|
101
|
+
scriptPath: job.browser,
|
|
102
|
+
exec: async childProcess => {
|
|
103
|
+
childProcess.close(-1)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
await expect(probe(job)).rejects.toThrowError(UTRError.BROWSER_PROBE_FAILED('-1'))
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('dependent modules', () => {
|
|
110
|
+
const npmGlobal = join(tmp, 'npm/global')
|
|
111
|
+
|
|
112
|
+
it('handles dependent modules', async () => {
|
|
113
|
+
mock({
|
|
114
|
+
api: 'exec',
|
|
115
|
+
scriptPath: 'npm',
|
|
116
|
+
args: ['install', 'dependentModule', '-g'],
|
|
117
|
+
exec: async childProcess => {
|
|
118
|
+
await createDir(join(npmGlobal, 'dependentModule'))
|
|
119
|
+
await writeFile(join(npmGlobal, 'dependentModule', 'package.json'), `{
|
|
120
|
+
"version": "1.0.0"
|
|
121
|
+
}`)
|
|
122
|
+
childProcess.stdout.write('OK')
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
mock({
|
|
126
|
+
api: 'exec',
|
|
127
|
+
scriptPath: 'npm',
|
|
128
|
+
args: ['view', 'dependentModule', 'version'],
|
|
129
|
+
exec: async childProcess => {
|
|
130
|
+
childProcess.stdout.write('1.0.0\n')
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
mock({
|
|
134
|
+
api: 'fork',
|
|
135
|
+
scriptPath: job.browser,
|
|
136
|
+
exec: async childProcess => {
|
|
137
|
+
const config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
138
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
139
|
+
modules: ['reserve', 'dependentModule']
|
|
140
|
+
}))
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
await probe(job)
|
|
144
|
+
expect(job.browserCapabilities.modules).toStrictEqual(['reserve', 'dependentModule'])
|
|
145
|
+
expect(job.browserModules.reserve).toStrictEqual(join(__dirname, '../node_modules/reserve'))
|
|
146
|
+
expect(job.browserModules.dependentModule).toStrictEqual(join(npmGlobal, 'dependentModule'))
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('fails if a dependent module cannot be installed', async () => {
|
|
150
|
+
mock({
|
|
151
|
+
api: 'exec',
|
|
152
|
+
scriptPath: 'npm',
|
|
153
|
+
args: ['install', 'dependentModuleNotInstallable', '-g'],
|
|
154
|
+
exec: childProcess => {
|
|
155
|
+
childProcess.stdout.write('KO')
|
|
156
|
+
childProcess.close(-1)
|
|
157
|
+
},
|
|
158
|
+
close: false
|
|
159
|
+
})
|
|
160
|
+
mock({
|
|
161
|
+
api: 'fork',
|
|
162
|
+
scriptPath: job.browser,
|
|
163
|
+
exec: async childProcess => {
|
|
164
|
+
const config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
165
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
166
|
+
modules: ['reserve', 'dependentModuleNotInstallable']
|
|
167
|
+
}))
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
await expect(probe(job)).rejects.toMatchObject({
|
|
171
|
+
name: 'UTRError:NPM_FAILED'
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('probe with modules', () => {
|
|
177
|
+
it('enables a second call if dependent modules were expected', async () => {
|
|
178
|
+
let config
|
|
179
|
+
let count = 0
|
|
180
|
+
mock({
|
|
181
|
+
api: 'fork',
|
|
182
|
+
scriptPath: job.browser,
|
|
183
|
+
exec: async childProcess => {
|
|
184
|
+
++count
|
|
185
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
186
|
+
if (!config.modules) {
|
|
187
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
188
|
+
modules: ['reserve'],
|
|
189
|
+
'probe-with-modules': true,
|
|
190
|
+
screenshot: true
|
|
191
|
+
}))
|
|
192
|
+
} else {
|
|
193
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
194
|
+
modules: ['reserve'],
|
|
195
|
+
screenshot: false
|
|
196
|
+
}))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
await probe(job)
|
|
201
|
+
expect(count).toStrictEqual(2)
|
|
202
|
+
expect(config.modules.reserve).not.toBeUndefined()
|
|
203
|
+
expect(job.browserCapabilities.screenshot).toStrictEqual(false)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('handles failure on second probe', async () => {
|
|
207
|
+
mock({
|
|
208
|
+
api: 'fork',
|
|
209
|
+
scriptPath: job.browser,
|
|
210
|
+
exec: async childProcess => {
|
|
211
|
+
const config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
212
|
+
if (!config.modules) {
|
|
213
|
+
await writeFile(config.capabilities, JSON.stringify({
|
|
214
|
+
modules: ['reserve'],
|
|
215
|
+
'probe-with-modules': true,
|
|
216
|
+
screenshot: true
|
|
217
|
+
}))
|
|
218
|
+
} else {
|
|
219
|
+
childProcess.close(-1)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
await expect(probe(job)).rejects.toThrowError(UTRError.BROWSER_PROBE_FAILED('-1'))
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('start and stop', () => {
|
|
229
|
+
const capabilities = {
|
|
230
|
+
whatever: 'is passed'
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
beforeEach(() => {
|
|
234
|
+
job.browserCapabilities = capabilities
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('returns a promise resolved on stop (even if the child process remains)', async () => {
|
|
238
|
+
mock({
|
|
239
|
+
api: 'fork',
|
|
240
|
+
scriptPath: job.browser,
|
|
241
|
+
exec: async childProcess => {
|
|
242
|
+
remainingChildProcess = childProcess
|
|
243
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
244
|
+
},
|
|
245
|
+
close: false
|
|
246
|
+
})
|
|
247
|
+
await start(job, '/test.html')
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('passes the provided capabilities', async () => {
|
|
251
|
+
let config
|
|
252
|
+
mock({
|
|
253
|
+
api: 'fork',
|
|
254
|
+
scriptPath: job.browser,
|
|
255
|
+
exec: async childProcess => {
|
|
256
|
+
remainingChildProcess = childProcess
|
|
257
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
258
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
259
|
+
},
|
|
260
|
+
close: false
|
|
261
|
+
})
|
|
262
|
+
await start(job, '/test.html')
|
|
263
|
+
expect(config.capabilities).toStrictEqual(capabilities)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('passes URL to open', async () => {
|
|
267
|
+
let config
|
|
268
|
+
mock({
|
|
269
|
+
api: 'fork',
|
|
270
|
+
scriptPath: job.browser,
|
|
271
|
+
exec: async childProcess => {
|
|
272
|
+
remainingChildProcess = childProcess
|
|
273
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
274
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
275
|
+
},
|
|
276
|
+
close: false
|
|
277
|
+
})
|
|
278
|
+
await start(job, '/test.html')
|
|
279
|
+
expect(config.url).toStrictEqual('/test.html')
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('passes browser arguments', async () => {
|
|
283
|
+
let config
|
|
284
|
+
mock({
|
|
285
|
+
api: 'fork',
|
|
286
|
+
scriptPath: job.browser,
|
|
287
|
+
exec: async childProcess => {
|
|
288
|
+
remainingChildProcess = childProcess
|
|
289
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
290
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
291
|
+
},
|
|
292
|
+
close: false
|
|
293
|
+
})
|
|
294
|
+
await start(job, '/test.html')
|
|
295
|
+
expect(config.args).toEqual(['argument1', 'argument2'])
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('captures outputs', async () => {
|
|
299
|
+
mock({
|
|
300
|
+
api: 'fork',
|
|
301
|
+
scriptPath: job.browser,
|
|
302
|
+
exec: async childProcess => {
|
|
303
|
+
remainingChildProcess = childProcess
|
|
304
|
+
await childProcess.stdout.write('stdout')
|
|
305
|
+
await childProcess.stderr.write('stderr')
|
|
306
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
307
|
+
childProcess.on('message.received', message => {
|
|
308
|
+
if (message.command === 'stop') {
|
|
309
|
+
childProcess.close()
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
},
|
|
313
|
+
close: false
|
|
314
|
+
})
|
|
315
|
+
await start(job, '/test.html')
|
|
316
|
+
const stdout = (await readFile(remainingChildProcess.stdoutFilename)).toString()
|
|
317
|
+
expect(stdout).toStrictEqual('stdout')
|
|
318
|
+
const stderr = (await readFile(remainingChildProcess.stderrFilename)).toString()
|
|
319
|
+
expect(stderr).toStrictEqual('stderr')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('stops automatically after a timeout', async () => {
|
|
323
|
+
const { promise: waitingForStop, resolve: stopReceived } = allocPromise()
|
|
324
|
+
mock({
|
|
325
|
+
api: 'fork',
|
|
326
|
+
scriptPath: job.browser,
|
|
327
|
+
exec: async childProcess => {
|
|
328
|
+
childProcess.on('message.received', message => {
|
|
329
|
+
if (message.command === 'stop') {
|
|
330
|
+
childProcess.close()
|
|
331
|
+
stopReceived()
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
},
|
|
335
|
+
close: false
|
|
336
|
+
})
|
|
337
|
+
job.pageTimeout = 100
|
|
338
|
+
await Promise.all([
|
|
339
|
+
start(job, '/test.html'),
|
|
340
|
+
waitingForStop
|
|
341
|
+
])
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('retries on abnormal termination', async () => {
|
|
345
|
+
let config
|
|
346
|
+
mock({
|
|
347
|
+
api: 'fork',
|
|
348
|
+
scriptPath: job.browser,
|
|
349
|
+
exec: async childProcess => {
|
|
350
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
351
|
+
if (config.retry === 0) {
|
|
352
|
+
childProcess.close(-1)
|
|
353
|
+
} else {
|
|
354
|
+
remainingChildProcess = childProcess
|
|
355
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
close: false
|
|
359
|
+
})
|
|
360
|
+
await start(job, '/test.html')
|
|
361
|
+
expect(config.retry).toStrictEqual(1)
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('fails after all retries', async () => {
|
|
365
|
+
let config
|
|
366
|
+
mock({
|
|
367
|
+
api: 'fork',
|
|
368
|
+
scriptPath: job.browser,
|
|
369
|
+
exec: async childProcess => {
|
|
370
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
371
|
+
childProcess.close(-1)
|
|
372
|
+
},
|
|
373
|
+
close: false
|
|
374
|
+
})
|
|
375
|
+
await expect(start(job, '/test.html')).rejects.toMatchObject({
|
|
376
|
+
name: 'UTRError:BROWSER_FAILED'
|
|
377
|
+
})
|
|
378
|
+
expect(config.retry).toStrictEqual(1)
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
const parallels = [2, 3, 4, 6]
|
|
382
|
+
parallels.forEach(parallel =>
|
|
383
|
+
it(`can run more than one page simultaneously (${parallel})`, async () => {
|
|
384
|
+
let ready
|
|
385
|
+
let setReady
|
|
386
|
+
mock({
|
|
387
|
+
api: 'fork',
|
|
388
|
+
scriptPath: job.browser,
|
|
389
|
+
exec: async childProcess => {
|
|
390
|
+
childProcess.on('message.received', async message => {
|
|
391
|
+
if (message.command === 'stop') {
|
|
392
|
+
childProcess.close()
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
setReady()
|
|
396
|
+
},
|
|
397
|
+
close: false
|
|
398
|
+
})
|
|
399
|
+
const startPromises = []
|
|
400
|
+
for (let step = 0; step < parallel; ++step) {
|
|
401
|
+
const allocatedPromise = allocPromise()
|
|
402
|
+
ready = allocatedPromise.promise
|
|
403
|
+
setReady = allocatedPromise.resolve
|
|
404
|
+
const promise = start(job, `/test${step}.html`)
|
|
405
|
+
await ready
|
|
406
|
+
startPromises.push(promise)
|
|
407
|
+
}
|
|
408
|
+
for (let step = 0; step < parallel; ++step) {
|
|
409
|
+
const promise = startPromises[step]
|
|
410
|
+
await stop(job, `/test${step}.html`)
|
|
411
|
+
await promise
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
it('ignores unknown pages', async () => {
|
|
417
|
+
job[$browsers] = {}
|
|
418
|
+
await stop(job, '/unknown.html')
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it('ignores unknown messages', async () => {
|
|
422
|
+
const { promise: waitingForReady, resolve: ready } = allocPromise()
|
|
423
|
+
mock({
|
|
424
|
+
api: 'fork',
|
|
425
|
+
scriptPath: job.browser,
|
|
426
|
+
exec: async childProcess => {
|
|
427
|
+
childProcess.emit('message', {
|
|
428
|
+
command: 'unknown'
|
|
429
|
+
})
|
|
430
|
+
childProcess.on('message.received', async message => {
|
|
431
|
+
if (message.command === 'stop') {
|
|
432
|
+
childProcess.close()
|
|
433
|
+
}
|
|
434
|
+
})
|
|
435
|
+
ready()
|
|
436
|
+
},
|
|
437
|
+
close: false
|
|
438
|
+
})
|
|
439
|
+
const started = start(job, '/test.html')
|
|
440
|
+
await waitingForReady
|
|
441
|
+
await stop(job, '/test.html')
|
|
442
|
+
await started
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
describe('script injection', () => {
|
|
447
|
+
beforeEach(() => {
|
|
448
|
+
job.browserCapabilities = {
|
|
449
|
+
scripts: true
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it('does not use any script by default', async () => {
|
|
454
|
+
let config
|
|
455
|
+
mock({
|
|
456
|
+
api: 'fork',
|
|
457
|
+
scriptPath: job.browser,
|
|
458
|
+
exec: async childProcess => {
|
|
459
|
+
remainingChildProcess = childProcess
|
|
460
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
461
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
462
|
+
},
|
|
463
|
+
close: false
|
|
464
|
+
})
|
|
465
|
+
await start(job, '/test.html')
|
|
466
|
+
expect(config.scripts).toEqual([])
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
it('injects window[\'ui5-test-runner/base-host\'] before any script', async () => {
|
|
470
|
+
let config
|
|
471
|
+
mock({
|
|
472
|
+
api: 'fork',
|
|
473
|
+
scriptPath: job.browser,
|
|
474
|
+
exec: async childProcess => {
|
|
475
|
+
remainingChildProcess = childProcess
|
|
476
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
477
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
478
|
+
},
|
|
479
|
+
close: false
|
|
480
|
+
})
|
|
481
|
+
await start(job, '/test.html', ['whatever'])
|
|
482
|
+
expect(config.scripts.length).toEqual(2)
|
|
483
|
+
expect(config.scripts[0]).toEqual('window[\'ui5-test-runner/base-host\'] = \'http://localhost:0\'\n')
|
|
484
|
+
expect(config.scripts[1]).toEqual('whatever')
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
it('translates pre-defined scripts', async () => {
|
|
488
|
+
let config
|
|
489
|
+
mock({
|
|
490
|
+
api: 'fork',
|
|
491
|
+
scriptPath: job.browser,
|
|
492
|
+
exec: async childProcess => {
|
|
493
|
+
remainingChildProcess = childProcess
|
|
494
|
+
config = JSON.parse((await readFile(childProcess.args[0])).toString())
|
|
495
|
+
setTimeout(() => stop(job, '/test.html'), 0)
|
|
496
|
+
},
|
|
497
|
+
close: false
|
|
498
|
+
})
|
|
499
|
+
await start(job, '/test.html', ['post.js'])
|
|
500
|
+
expect(config.scripts.length).toEqual(2)
|
|
501
|
+
expect(config.scripts[1]).toMatch(/ui5-test-runner\/base-host/)
|
|
502
|
+
expect(config.scripts[1]).toMatch(/ui5-test-runner\/post/)
|
|
503
|
+
expect(config.scripts[1]).toMatch(/function post \(url, data\)/)
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
describe('screenshot', () => {
|
|
508
|
+
describe('supporting', () => {
|
|
509
|
+
beforeEach(() => {
|
|
510
|
+
job.browserCapabilities = {
|
|
511
|
+
screenshot: '.png'
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
it('should generate a file', async () => {
|
|
516
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
517
|
+
let fileName
|
|
518
|
+
mock({
|
|
519
|
+
api: 'fork',
|
|
520
|
+
scriptPath: job.browser,
|
|
521
|
+
exec: async childProcess => {
|
|
522
|
+
remainingChildProcess = childProcess
|
|
523
|
+
childProcess.on('message.received', async message => {
|
|
524
|
+
if (message.command === 'screenshot') {
|
|
525
|
+
fileName = message.filename
|
|
526
|
+
await writeFile(fileName, 'some random content to avoid empty file')
|
|
527
|
+
childProcess.emit('message', message)
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
setReady()
|
|
531
|
+
},
|
|
532
|
+
close: false
|
|
533
|
+
})
|
|
534
|
+
const started = start(job, '/test.html')
|
|
535
|
+
await ready
|
|
536
|
+
const result = await screenshot(job, '/test.html', 'screenshot')
|
|
537
|
+
expect(fileName).toMatch(/\bscreenshot\.png$/)
|
|
538
|
+
expect(result).toStrictEqual(fileName)
|
|
539
|
+
stop(job, '/test.html')
|
|
540
|
+
await started
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
it('fails if the file is missing', async () => {
|
|
544
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
545
|
+
mock({
|
|
546
|
+
api: 'fork',
|
|
547
|
+
scriptPath: job.browser,
|
|
548
|
+
exec: async childProcess => {
|
|
549
|
+
remainingChildProcess = childProcess
|
|
550
|
+
childProcess.on('message.received', async message => {
|
|
551
|
+
if (message.command === 'screenshot') {
|
|
552
|
+
childProcess.emit('message', message)
|
|
553
|
+
}
|
|
554
|
+
})
|
|
555
|
+
setReady()
|
|
556
|
+
},
|
|
557
|
+
close: false
|
|
558
|
+
})
|
|
559
|
+
start(job, '/test.html')
|
|
560
|
+
await ready
|
|
561
|
+
await expect(screenshot(job, '/test.html', 'screenshot')).rejects.toMatchObject({
|
|
562
|
+
name: 'UTRError:BROWSER_SCREENSHOT_FAILED'
|
|
563
|
+
})
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
it('fails if the file is empty', async () => {
|
|
567
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
568
|
+
mock({
|
|
569
|
+
api: 'fork',
|
|
570
|
+
scriptPath: job.browser,
|
|
571
|
+
exec: async childProcess => {
|
|
572
|
+
remainingChildProcess = childProcess
|
|
573
|
+
childProcess.on('message.received', async message => {
|
|
574
|
+
if (message.command === 'screenshot') {
|
|
575
|
+
await writeFile(message.filename, '')
|
|
576
|
+
childProcess.emit('message', message)
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
setReady()
|
|
580
|
+
},
|
|
581
|
+
close: false
|
|
582
|
+
})
|
|
583
|
+
start(job, '/test.html')
|
|
584
|
+
await ready
|
|
585
|
+
await expect(screenshot(job, '/test.html', 'screenshot')).rejects.toMatchObject({
|
|
586
|
+
name: 'UTRError:BROWSER_SCREENSHOT_FAILED'
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
it('fails after a timeout', async () => {
|
|
591
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
592
|
+
job.screenshotTimeout = 10
|
|
593
|
+
mock({
|
|
594
|
+
api: 'fork',
|
|
595
|
+
scriptPath: job.browser,
|
|
596
|
+
exec: async childProcess => {
|
|
597
|
+
remainingChildProcess = childProcess
|
|
598
|
+
setReady()
|
|
599
|
+
},
|
|
600
|
+
close: false
|
|
601
|
+
})
|
|
602
|
+
const started = start(job, '/test.html')
|
|
603
|
+
await ready
|
|
604
|
+
await expect(screenshot(job, '/test.html', 'screenshot')).rejects.toMatchObject({
|
|
605
|
+
name: 'UTRError:BROWSER_SCREENSHOT_TIMEOUT'
|
|
606
|
+
})
|
|
607
|
+
stop(job, '/test.html')
|
|
608
|
+
await started
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
describe('edge cases', () => {
|
|
612
|
+
it('ignores disconnected child processes', async () => {
|
|
613
|
+
job[$browsers] = {
|
|
614
|
+
'/disconnected.html': {
|
|
615
|
+
childProcess: {
|
|
616
|
+
connected: false
|
|
617
|
+
},
|
|
618
|
+
reportDir: job.reportDir
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
expect(await screenshot(job, '/disconnected.html')).toBeUndefined()
|
|
622
|
+
})
|
|
623
|
+
})
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
describe('not supporting', () => {
|
|
627
|
+
beforeEach(() => {
|
|
628
|
+
job.browserCapabilities = {}
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
it('fails', async () => {
|
|
632
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
633
|
+
mock({
|
|
634
|
+
api: 'fork',
|
|
635
|
+
scriptPath: job.browser,
|
|
636
|
+
exec: async childProcess => {
|
|
637
|
+
remainingChildProcess = childProcess
|
|
638
|
+
setReady()
|
|
639
|
+
},
|
|
640
|
+
close: false
|
|
641
|
+
})
|
|
642
|
+
const started = start(job, '/test.html')
|
|
643
|
+
await ready
|
|
644
|
+
await expect(screenshot(job, '/test.html', 'screenshot')).rejects.toThrowError(UTRError.BROWSER_SCREENSHOT_NOT_SUPPORTED())
|
|
645
|
+
stop(job, '/test.html')
|
|
646
|
+
await started
|
|
647
|
+
})
|
|
648
|
+
})
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
describe('console', () => {
|
|
652
|
+
it('aggregate console logs in a .jsonl file', async () => {
|
|
653
|
+
const { promise: ready, resolve: setReady } = allocPromise()
|
|
654
|
+
mock({
|
|
655
|
+
api: 'fork',
|
|
656
|
+
scriptPath: job.browser,
|
|
657
|
+
exec: async childProcess => {
|
|
658
|
+
childProcess.on('message.received', async message => {
|
|
659
|
+
if (message.command === 'stop') {
|
|
660
|
+
childProcess.close(0)
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
childProcess.emit('message', {
|
|
664
|
+
command: 'console',
|
|
665
|
+
t: Date.now(),
|
|
666
|
+
api: 'log',
|
|
667
|
+
args: ['Hello']
|
|
668
|
+
})
|
|
669
|
+
childProcess.emit('message', {
|
|
670
|
+
command: 'console',
|
|
671
|
+
t: Date.now(),
|
|
672
|
+
api: 'log',
|
|
673
|
+
args: ['World !']
|
|
674
|
+
})
|
|
675
|
+
setReady()
|
|
676
|
+
},
|
|
677
|
+
close: false
|
|
678
|
+
})
|
|
679
|
+
const started = start(job, '/test.html')
|
|
680
|
+
await ready
|
|
681
|
+
stop(job, '/test.html')
|
|
682
|
+
await started
|
|
683
|
+
const consoleFilename = join(job.reportDir, filename('/test.html'), 'console.jsonl')
|
|
684
|
+
const consoleStat = await stat(consoleFilename)
|
|
685
|
+
expect(consoleStat.isFile()).toStrictEqual(true)
|
|
686
|
+
const consoleContent = (await readFile(consoleFilename)).toString()
|
|
687
|
+
const [firstTrace, secondTrace] = consoleContent.split('\n')
|
|
688
|
+
expect(JSON.parse(firstTrace)).toMatchObject({
|
|
689
|
+
api: 'log',
|
|
690
|
+
args: ['Hello']
|
|
691
|
+
})
|
|
692
|
+
expect(JSON.parse(secondTrace)).toMatchObject({
|
|
693
|
+
api: 'log',
|
|
694
|
+
args: ['World !']
|
|
695
|
+
})
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
it('resets .jsonl file on retry', async () => {
|
|
699
|
+
mock({
|
|
700
|
+
api: 'fork',
|
|
701
|
+
scriptPath: job.browser,
|
|
702
|
+
exec: async childProcess => {
|
|
703
|
+
childProcess.on('message.received', async message => {
|
|
704
|
+
if (message.command === 'stop') {
|
|
705
|
+
childProcess.close(0)
|
|
706
|
+
}
|
|
707
|
+
})
|
|
708
|
+
childProcess.emit('message', {
|
|
709
|
+
command: 'console',
|
|
710
|
+
t: Date.now(),
|
|
711
|
+
api: 'log',
|
|
712
|
+
args: ['Hello World !']
|
|
713
|
+
})
|
|
714
|
+
childProcess.close(-1)
|
|
715
|
+
},
|
|
716
|
+
close: false
|
|
717
|
+
})
|
|
718
|
+
await expect(start(job, '/test.html')).rejects.toThrowError()
|
|
719
|
+
const consoleFilename = join(job.reportDir, filename('/test.html'), 'console.jsonl')
|
|
720
|
+
const consoleContent = (await readFile(consoleFilename)).toString()
|
|
721
|
+
expect(consoleContent.split('\n').length).toStrictEqual(2)
|
|
722
|
+
})
|
|
723
|
+
})
|
|
724
|
+
})
|