ui5-test-runner 3.3.5 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +9 -16
  2. package/package.json +17 -18
  3. package/src/browsers.js +9 -1
  4. package/src/capabilities/index.js +6 -6
  5. package/src/capabilities/tests/screenshot/index.html +13 -3
  6. package/src/capabilities/tests/screenshot/index.js +11 -5
  7. package/src/capabilities/tests/traces/index.js +1 -1
  8. package/src/coverage.js +51 -42
  9. package/src/defaults/puppeteer.js +10 -20
  10. package/src/defaults/report/progress.js +11 -0
  11. package/src/endpoints.js +8 -0
  12. package/src/get-job-progress.js +9 -0
  13. package/src/inject/opa-iframe-coverage.js +0 -1
  14. package/src/inject/post.js +4 -4
  15. package/src/inject/qunit-hooks.js +18 -2
  16. package/src/inject/qunit-intercept.js +6 -0
  17. package/src/inject/ui5-coverage.js +6 -0
  18. package/src/job-mode.js +0 -1
  19. package/src/job.js +10 -10
  20. package/src/options.js +4 -0
  21. package/src/output.js +56 -24
  22. package/src/qunit-hooks.js +43 -29
  23. package/src/symbols.js +2 -1
  24. package/src/tests.js +6 -1
  25. package/src/add-test-pages.spec.js +0 -95
  26. package/src/browser.spec.js +0 -724
  27. package/src/cors.spec.js +0 -41
  28. package/src/coverage.spec.js +0 -120
  29. package/src/csv-reader.spec.js +0 -42
  30. package/src/csv-writer.spec.js +0 -77
  31. package/src/error.spec.js +0 -17
  32. package/src/get-job-progress.spec.js +0 -175
  33. package/src/inject/post.spec.js +0 -147
  34. package/src/job.spec.js +0 -418
  35. package/src/npm.spec.js +0 -98
  36. package/src/options.spec.js +0 -141
  37. package/src/qunit-hooks.spec.js +0 -747
  38. package/src/simulate.spec.js +0 -547
  39. package/src/timeout.spec.js +0 -39
  40. package/src/tools.spec.js +0 -90
  41. package/src/unhandled.spec.js +0 -63
package/src/job.spec.js DELETED
@@ -1,418 +0,0 @@
1
- const { dirname, join } = require('path')
2
- const { fromObject, fromCmdLine } = require('./job')
3
- const normalizePath = path => path.replace(/\\/g, '/') // win -> unix
4
- const { $valueSources } = require('./symbols')
5
- const { UTRError } = require('./error')
6
-
7
- const cwd = join(__dirname, '../test/project')
8
-
9
- function buildJob (parameters) {
10
- return fromObject(cwd, parameters)
11
- }
12
-
13
- describe('job', () => {
14
- describe('parameter parsing', () => {
15
- it('provides default values', () => {
16
- const job = buildJob({})
17
- expect(job.cwd).toStrictEqual(cwd)
18
- expect(job.port).toStrictEqual(0)
19
- expect(job.ui5).toStrictEqual('https://ui5.sap.com')
20
- expect(job.browser.startsWith(dirname(dirname(__dirname)))).toStrictEqual(true)
21
- expect(normalizePath(job.browser).endsWith('defaults/puppeteer.js')).toStrictEqual(true)
22
- expect(normalizePath(job.webapp).endsWith('/test/project/webapp')).toStrictEqual(true)
23
- expect(job.keepAlive).toStrictEqual(false)
24
- expect(job.screenshot).toStrictEqual(true)
25
- expect(job[$valueSources]).toMatchObject({
26
- cwd: 'default',
27
- port: 'default',
28
- ui5: 'default',
29
- browser: 'default'
30
- })
31
- })
32
-
33
- it('parses parameters', () => {
34
- const job = buildJob({
35
- cwd: '../project2',
36
- port: 8080,
37
- keepAlive: null,
38
- ui5: 'http://localhost:8088/ui5'
39
- })
40
- expect(normalizePath(job.cwd).endsWith('/test/project2')).toStrictEqual(true)
41
- expect(job.port).toStrictEqual(8080)
42
- expect(job.keepAlive).toStrictEqual(true)
43
- expect(job.ui5).toStrictEqual('http://localhost:8088/ui5')
44
- expect(normalizePath(job.webapp).endsWith('/test/project2/webapp')).toStrictEqual(true)
45
- expect(job[$valueSources]).toMatchObject({
46
- cwd: 'cli',
47
- port: 'cli',
48
- keepAlive: 'cli',
49
- ui5: 'cli',
50
- browser: 'default'
51
- })
52
- })
53
-
54
- describe('complex parameter parsing', () => {
55
- it('implements boolean flag', () => {
56
- const job = buildJob({
57
- keepAlive: false,
58
- coverage: null,
59
- logServer: null
60
- })
61
- expect(job.keepAlive).toStrictEqual(false)
62
- expect(job.coverage).toStrictEqual(true)
63
- expect(job.logServer).toStrictEqual(true)
64
- })
65
-
66
- it('implements boolean switch off', () => {
67
- const job = buildJob({
68
- noCoverage: null,
69
- noScreenshot: null
70
- })
71
- expect(job.coverage).toStrictEqual(false)
72
- expect(job.screenshot).toStrictEqual(false)
73
- })
74
-
75
- it('url disables coverage by default', () => {
76
- const job = buildJob({
77
- url: 'http://localhost:8080'
78
- })
79
- expect(job.url).toStrictEqual(['http://localhost:8080'])
80
- expect(job.coverage).toStrictEqual(false)
81
- })
82
-
83
- it('url still allows coverage', () => {
84
- const job = buildJob({
85
- url: 'http://localhost:8080',
86
- coverage: true
87
- })
88
- expect(job.url).toStrictEqual(['http://localhost:8080'])
89
- expect(job.coverage).toStrictEqual(true)
90
- })
91
-
92
- describe('multi values', () => {
93
- const absoluteLibPath = join(__dirname, '../test/project/webapp/lib')
94
-
95
- describe('url', () => {
96
- it('accepts multiple urls', () => {
97
- const job = buildJob({
98
- url: [
99
- 'http://localhost:8080/page1.html',
100
- 'http://localhost:8080/page2.html'
101
- ]
102
- })
103
- expect(job.url).toMatchObject([
104
- 'http://localhost:8080/page1.html',
105
- 'http://localhost:8080/page2.html'
106
- ])
107
- })
108
- })
109
-
110
- describe('libs', () => {
111
- it('accepts one library', () => {
112
- const job = buildJob({
113
- libs: [absoluteLibPath]
114
- })
115
- expect(job.libs).toMatchObject([{
116
- relative: '',
117
- source: absoluteLibPath
118
- }])
119
- })
120
-
121
- it('accepts two libraries', () => {
122
- const project2Path = join(__dirname, '../test/project2')
123
- const job = buildJob({
124
- libs: [absoluteLibPath, 'project2/=../project2']
125
- })
126
- expect(job.libs).toMatchObject([{
127
- relative: '',
128
- source: absoluteLibPath
129
- }, {
130
- relative: 'project2/',
131
- source: project2Path
132
- }])
133
- })
134
- })
135
-
136
- describe('browser parameters', () => {
137
- it('allows passing extra parameter', () => {
138
- const job = buildJob({
139
- '--': ['--visible']
140
- })
141
- expect(job.browserArgs).toEqual(['--visible'])
142
- })
143
-
144
- it('allows passing extra parameters', () => {
145
- const job = buildJob({
146
- '--': ['--visible', '--verbose']
147
- })
148
- expect(job.browserArgs).toEqual(['--visible', '--verbose'])
149
- })
150
- })
151
- })
152
-
153
- describe('parameters using $/', () => {
154
- const job = buildJob({
155
- cwd,
156
- browser: '$/selenium-webdriver.js'
157
- })
158
- expect(job.browser).toStrictEqual(join(__dirname, './defaults/selenium-webdriver.js'))
159
- expect(job.coverageSettings).toStrictEqual(join(__dirname, './defaults/nyc.json'))
160
- expect(job.reportGenerator).toEqual([join(__dirname, './defaults/report.js')])
161
- })
162
-
163
- describe('custom mappings', () => {
164
- it('offers custom mappings', () => {
165
- const job = buildJob({
166
- cwd,
167
- mappings: [
168
- '^/otherlib/(.+)=file(./otherfolder/otherlib/$1)',
169
- '^/ui/oDataService/v1/odata/v4/ServiceName/(.+)=url(http://localhost:18082/odata/v4/ServiceName/$1)'
170
- ]
171
- })
172
- expect(job.mappings).toEqual([
173
- {
174
- match: '^/otherlib/(.+)',
175
- file: './otherfolder/otherlib/$1'
176
- }, {
177
- match: '^/ui/oDataService/v1/odata/v4/ServiceName/(.+)',
178
- url: 'http://localhost:18082/odata/v4/ServiceName/$1'
179
- }
180
- ])
181
- })
182
-
183
- it('rejects invalid mapping', () => {
184
- expect(() => buildJob({
185
- cwd,
186
- mappings: [
187
- '^/otherlib/(.+)=custom(./otherfolder/otherlib/$1)'
188
- ]
189
- })).toThrowError()
190
- })
191
- })
192
- })
193
- })
194
-
195
- describe('validation', () => {
196
- it('fails on negative integers', () => {
197
- expect(() => buildJob({
198
- port: -1
199
- })).toThrow()
200
- })
201
-
202
- it('fails on invalid URL', () => {
203
- expect(() => buildJob({
204
- ui5: 'not_an_url'
205
- })).toThrow()
206
- })
207
-
208
- it('fails on a missing file (does not exist)', () => {
209
- expect(() => buildJob({
210
- testsuite: 'not_a_file'
211
- })).toThrow()
212
- })
213
-
214
- it('fails on a missing file (points to a folder)', () => {
215
- expect(() => buildJob({
216
- testsuite: 'lib'
217
- })).toThrow()
218
- })
219
-
220
- it('fails on a missing folder (does not exist)', () => {
221
- expect(() => buildJob({
222
- webapp: 'not_a_folder'
223
- })).toThrow()
224
- })
225
-
226
- it('fails on a missing folder (points to a file)', () => {
227
- expect(() => buildJob({
228
- webapp: 'webapp/lib/README.md'
229
- })).toThrow()
230
- })
231
-
232
- describe('Path parameters validation', () => {
233
- const parameters = ['webapp', 'browser', 'testsuite']
234
-
235
- parameters.forEach(parameter => {
236
- it(`fails on invalid path for ${parameter}`, () => {
237
- expect(() => buildJob({ [parameter]: 'nope' })).toThrow()
238
- })
239
- })
240
- })
241
-
242
- describe('libs', () => {
243
- it('fails on invalid lib path (absolute)', () => {
244
- const absoluteLibPath = join(__dirname, '../test/project/webapp/lib2')
245
- expect(() => buildJob({
246
- libs: absoluteLibPath
247
- })).toThrow()
248
- })
249
-
250
- it('fails on invalid lib path (relative)', () => {
251
- expect(() => buildJob({
252
- libs: '../project3'
253
- })).toThrow()
254
- })
255
- })
256
-
257
- it('url forbids the use of webapp', () => {
258
- expect(() => buildJob({
259
- webapp: 'not_a_folder',
260
- url: 'http://localhost:8080'
261
- })).toThrow(UTRError.MODE_INCOMPATIBLE_OPTION('webapp'))
262
- })
263
- })
264
-
265
- describe('Using ui5-test-runner.json', () => {
266
- const project2 = join(__dirname, '../test/project2')
267
-
268
- it('enables option overriding at the command level', () => {
269
- const job = fromCmdLine(cwd, [
270
- '--port', '1',
271
- '--port', '2',
272
- '-k', 'true',
273
- '-k', 'false'
274
- ])
275
- expect(job.port).toStrictEqual(2)
276
- expect(job.keepAlive).toStrictEqual(false)
277
- })
278
-
279
- it('preload settings', () => {
280
- const job = buildJob({
281
- cwd: project2
282
- })
283
- expect(job.pageTimeout).toStrictEqual(900000)
284
- expect(job.globalTimeout).toStrictEqual(3600000)
285
- expect(job.failFast).toStrictEqual(true)
286
- expect(job.libs).toEqual([{
287
- relative: 'lib/',
288
- source: join(project2, 'webapp')
289
- }])
290
- expect(job.browserArgs).toEqual(['-1'])
291
- })
292
-
293
- it('allows command line override', () => {
294
- const job = buildJob({
295
- cwd: project2,
296
- globalTimeout: 900000
297
- })
298
- expect(job.pageTimeout).toStrictEqual(900000)
299
- expect(job.globalTimeout).toStrictEqual(900000)
300
- expect(job.failFast).toStrictEqual(true)
301
- expect(job.libs).toEqual([{
302
- relative: 'lib/',
303
- source: join(project2, 'webapp')
304
- }])
305
- expect(job.ui5).toStrictEqual('https://ui5.sap.com')
306
- })
307
-
308
- it('preloads and overrides command line settings', () => {
309
- const job = buildJob({
310
- cwd: project2,
311
- pageTimeout: 60000,
312
- globalTimeout: 900000,
313
- libs: 'project2/=../project2'
314
- })
315
- expect(job.pageTimeout).toStrictEqual(900000)
316
- expect(job.globalTimeout).toStrictEqual(900000)
317
- expect(job.failFast).toStrictEqual(true)
318
- expect(job.libs).toEqual([{
319
- relative: 'lib/',
320
- source: join(project2, 'webapp')
321
- }, {
322
- relative: 'project2/',
323
- source: join(cwd, '../project2')
324
- }])
325
- expect(job.ui5).toStrictEqual('https://ui5.sap.com')
326
- })
327
-
328
- it('preloads and concatenates browser settings', () => {
329
- const job = buildJob({
330
- cwd: project2,
331
- '--': [-2]
332
- })
333
- expect(job.browserArgs).toEqual(['-1', '-2'])
334
- })
335
- })
336
-
337
- describe('mode', () => {
338
- it('returns legacy by default', () => {
339
- expect(fromObject(cwd, {}).mode).toStrictEqual('legacy')
340
- })
341
-
342
- describe('url', () => {
343
- it('enables testing external projects', () => {
344
- expect(fromObject(cwd, {
345
- url: ['http://myserver.remote.url/ui5-app.html']
346
- }).mode).toStrictEqual('url')
347
- })
348
-
349
- // Assuming url could be used to access 'local' server, most options are supported
350
-
351
- describe('incompatible options', () => {
352
- const incompatible = {
353
- testsuite: '../project2'
354
- }
355
-
356
- Object.keys(incompatible).forEach(option => {
357
- it(`is incompatible with ${option}`, () => {
358
- expect(() => fromObject(cwd, {
359
- url: ['http://myserver.remote.url/ui5-app.html'],
360
- [option]: incompatible[option]
361
- })).toThrow(UTRError.MODE_INCOMPATIBLE_OPTION(option))
362
- })
363
- })
364
- })
365
- })
366
-
367
- describe('capabilities', () => {
368
- it('triggers the capabilities tester', () => {
369
- expect(fromObject(cwd, {
370
- capabilities: true
371
- }).mode).toStrictEqual('capabilities')
372
- })
373
-
374
- it('supports cwd, port, logServer, browser, parallel, reportDir, pageTimeout, browserCloseTimeout, failFast and keepAlive', () => {
375
- expect(fromObject('.', {
376
- capabilities: true,
377
- cwd,
378
- port: 8080,
379
- logServer: true,
380
- browser: '$/selenium-webdriver.js',
381
- parallel: 2,
382
- reportDir: join(cwd, '.report'),
383
- pageTimeout: 1000,
384
- browserCloseTimeout: 1000,
385
- failFast: true,
386
- keepAlive: true
387
- }).mode).toStrictEqual('capabilities')
388
- })
389
-
390
- describe('incompatible options', () => {
391
- const incompatible = {
392
- libs: '../project2',
393
- ui5: 'http://localhost:8088/ui5',
394
- cache: join(cwd, '.cache'),
395
- webapp: 'webapp',
396
- testsuite: 'test/testsuite.qunit.html',
397
- pageFilter: '.*',
398
- pageParams: 'sap-ui-debug=true',
399
- coverage: true,
400
- coverageSettings: '$/nyc.json',
401
- coverageTempDir: '.nyc_output',
402
- coverageReportDir: 'coverage',
403
- coverageReporters: 'lcov',
404
- globalTimeout: 1000
405
- }
406
-
407
- Object.keys(incompatible).forEach(option => {
408
- it(`is incompatible with ${option}`, () => {
409
- expect(() => fromObject(cwd, {
410
- capabilities: true,
411
- [option]: incompatible[option]
412
- })).toThrow(UTRError.MODE_INCOMPATIBLE_OPTION(option))
413
- })
414
- })
415
- })
416
- })
417
- })
418
- })
package/src/npm.spec.js DELETED
@@ -1,98 +0,0 @@
1
- const { join } = require('path')
2
- const { resolvePackage } = require('./npm')
3
- const { mock } = require('child_process')
4
- const { cleanDir, createDir, recreateDir } = require('./tools')
5
- const { writeFile } = require('fs/promises')
6
- const { fromObject } = require('./job')
7
- const { getOutput } = require('./output')
8
- const { UTRError } = require('./error')
9
-
10
- const tmp = join(__dirname, '../tmp')
11
- const npmGlobal = join(tmp, 'npm/global')
12
-
13
- describe('src/npm', () => {
14
- const cwd = join(__dirname, '../test/project')
15
- const reportDir = join(__dirname, '../tmp/npm/report')
16
-
17
- let job
18
- let output
19
-
20
- beforeAll(async () => {
21
- await createDir(join(npmGlobal, 'existing_global'))
22
- await writeFile(join(npmGlobal, 'existing_global', 'package.json'), `{
23
- "version": "1.0.0"
24
- }`)
25
- await recreateDir(reportDir)
26
- await cleanDir(join(npmGlobal, 'not_existing'))
27
- })
28
-
29
- beforeEach(() => {
30
- job = fromObject(cwd, {
31
- reportDir,
32
- coverage: false
33
- })
34
- job.status = 'Testing'
35
- output = getOutput(job)
36
- jest.spyOn(output, 'status')
37
- jest.spyOn(output, 'resolvedPackage')
38
- jest.spyOn(output, 'packageNotLatest')
39
- })
40
-
41
- it('detects already installed local package', async () => {
42
- const path = await resolvePackage(job, 'reserve')
43
- expect(path).toStrictEqual(join(__dirname, '../node_modules/reserve'))
44
- expect(output.resolvedPackage).toHaveBeenCalledTimes(1)
45
- expect(output.packageNotLatest).not.toHaveBeenCalled()
46
- })
47
-
48
- it('detects already installed global package (but warn as not the latest)', async () => {
49
- mock({
50
- api: 'exec',
51
- scriptPath: 'npm',
52
- args: ['view', 'existing_global', 'version'],
53
- exec: async childProcess => childProcess.stdout.write('1.0.1\n'),
54
- persist: true
55
- })
56
- const path = await resolvePackage(job, 'existing_global')
57
- expect(path).toStrictEqual(join(npmGlobal, 'existing_global'))
58
- expect(output.resolvedPackage).toHaveBeenCalledTimes(1)
59
- expect(output.packageNotLatest).toHaveBeenCalled()
60
- })
61
-
62
- it('fails if --no-npm-install is set', async () => {
63
- await expect(resolvePackage({
64
- ...job,
65
- npmInstall: false
66
- }, 'not_existing')).rejects.toThrowError(UTRError.NPM_DEPENDENCY_NOT_FOUND('not_existing'))
67
- })
68
-
69
- it('installs missing package globally', async () => {
70
- mock({
71
- api: 'exec',
72
- scriptPath: 'npm',
73
- args: ['install', 'not_existing', '-g'],
74
- exec: async childProcess => {
75
- await createDir(join(npmGlobal, 'not_existing'))
76
- await writeFile(join(npmGlobal, 'not_existing', 'package.json'), `{
77
- "version": "1.0.0"
78
- }`)
79
- childProcess.stdout.write('OK installed')
80
- }
81
- })
82
- const path = await resolvePackage(job, 'not_existing')
83
- expect(path).toStrictEqual(join(npmGlobal, 'not_existing'))
84
- expect(output.resolvedPackage).toHaveBeenCalledTimes(1)
85
- expect(output.packageNotLatest).not.toHaveBeenCalled()
86
- })
87
-
88
- it('fails if the package cannot be installed', async () => {
89
- mock({
90
- api: 'exec',
91
- scriptPath: 'npm',
92
- args: ['install', 'fail_to_install', '-g'],
93
- exec: childProcess => { throw new Error('KO failed') }
94
- })
95
- await expect(resolvePackage(job, 'fail_to_install')).rejects.toThrowError(UTRError.NPM_FAILED('Error: KO failed'))
96
- expect(output.status).toHaveBeenCalledTimes(1) // Won't restore previous status
97
- })
98
- })
@@ -1,141 +0,0 @@
1
- const { any, boolean, integer, timeout, url, arrayOf, percent } = require('./options')
2
- const { InvalidArgumentError } = require('commander')
3
-
4
- function checkType ({ method, validValues, invalidValues }) {
5
- describe(method.name, () => {
6
- if (Array.isArray(validValues)) {
7
- validValues = validValues.reduce((map, validValue) => {
8
- map[validValue] = validValue
9
- return map
10
- }, {})
11
- }
12
- Object.keys(validValues).forEach(validValue => {
13
- const expectedValue = validValues[validValue]
14
- it(`accepts ${JSON.stringify(validValue)}`, () => {
15
- expect(method(validValue)).toBe(expectedValue)
16
- })
17
- })
18
- invalidValues.forEach(invalidValue => {
19
- it(`rejects ${JSON.stringify(invalidValue)}`, () => {
20
- expect(() => method(invalidValue)).toThrowError(InvalidArgumentError)
21
- })
22
- })
23
- })
24
- }
25
-
26
- describe('src/options', () => {
27
- checkType({
28
- method: any,
29
- validValues: {
30
- true: 'true',
31
- 123: '123'
32
- },
33
- invalidValues: [
34
- ]
35
- })
36
-
37
- checkType({
38
- method: boolean,
39
- validValues: {
40
- true: true,
41
- yes: true,
42
- on: true,
43
- false: false,
44
- no: false,
45
- off: false
46
- },
47
- invalidValues: [
48
- '',
49
- 'anything'
50
- ]
51
- })
52
-
53
- describe('boolean (invert)', () => {
54
- it('inverts default value (false)', () => {
55
- expect(boolean(undefined, false)).toBe(true)
56
- })
57
-
58
- it('inverts default value (true)', () => {
59
- expect(boolean(undefined, true)).toBe(false)
60
- })
61
- })
62
-
63
- checkType({
64
- method: integer,
65
- validValues: {
66
- 0: 0,
67
- 1: 1,
68
- 10: 10,
69
- 100: 100,
70
- 1000: 1000,
71
- 10000: 10000
72
- },
73
- invalidValues: [
74
- '',
75
- '-1',
76
- '-10000',
77
- 'abc'
78
- ]
79
- })
80
-
81
- checkType({
82
- method: timeout,
83
- validValues: {
84
- 0: 0,
85
- 1: 1,
86
- 10: 10,
87
- 100: 100,
88
- 1000: 1000,
89
- 10000: 10000,
90
- '10ms': 10,
91
- '10s': 10000,
92
- '10sec': 10000,
93
- '10m': 600000,
94
- '10min': 600000
95
- },
96
- invalidValues: [
97
- '',
98
- '1abc',
99
- '1msec',
100
- '1mins'
101
- ]
102
- })
103
-
104
- checkType({
105
- method: url,
106
- validValues: [
107
- 'https://ui5.sap.com/test-resources/sap/m/demokit/orderbrowser/webapp/test/testsuite.qunit.html',
108
- 'http://localhost:8085/test.html?a=b'
109
- ],
110
- invalidValues: [
111
- '',
112
- '-1',
113
- 'abc',
114
- 'ftp://server.com/path'
115
- ]
116
- })
117
-
118
- checkType({
119
- method: percent,
120
- validValues: {
121
- 0: 0,
122
- 1: 1,
123
- 10: 10,
124
- 100: 100
125
- },
126
- invalidValues: [
127
- '',
128
- '-1',
129
- 'abc',
130
- '101'
131
- ]
132
- })
133
-
134
- describe('arrayOf', () => {
135
- it('builds a type validator that aggregates validated values in an array', () => {
136
- const validator = arrayOf(integer)
137
- const value = validator('2', validator('1'))
138
- expect(value).toStrictEqual([1, 2])
139
- })
140
- })
141
- })