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.
Files changed (93) hide show
  1. package/README.md +32 -188
  2. package/index.js +47 -16
  3. package/package.json +28 -10
  4. package/src/add-test-pages.js +35 -0
  5. package/src/add-test-pages.spec.js +95 -0
  6. package/src/browser.spec.js +724 -0
  7. package/src/browsers.js +220 -59
  8. package/src/capabilities/index.js +194 -0
  9. package/src/capabilities/tests/basic/iframe.html +8 -0
  10. package/src/capabilities/tests/basic/index.html +12 -0
  11. package/src/capabilities/tests/basic/index.js +20 -0
  12. package/src/capabilities/tests/basic/ui5.html +24 -0
  13. package/src/capabilities/tests/dynamic-include/index.js +21 -0
  14. package/src/capabilities/tests/dynamic-include/mix.html +11 -0
  15. package/src/capabilities/tests/dynamic-include/one.html +11 -0
  16. package/src/capabilities/tests/dynamic-include/post.js +3 -0
  17. package/src/capabilities/tests/dynamic-include/test.js +1 -0
  18. package/src/capabilities/tests/dynamic-include/two.html +11 -0
  19. package/src/capabilities/tests/index.js +16 -0
  20. package/src/capabilities/tests/local-storage/index.html +16 -0
  21. package/src/capabilities/tests/local-storage/index.js +21 -0
  22. package/src/capabilities/tests/screenshot/index.html +13 -0
  23. package/src/capabilities/tests/screenshot/index.js +18 -0
  24. package/src/capabilities/tests/scripts/index.js +50 -0
  25. package/src/capabilities/tests/scripts/qunit.html +22 -0
  26. package/src/capabilities/tests/scripts/testsuite.html +10 -0
  27. package/src/capabilities/tests/scripts/testsuite.js +8 -0
  28. package/src/capabilities/tests/timeout/index.html +21 -0
  29. package/src/capabilities/tests/timeout/index.js +19 -0
  30. package/src/capabilities/tests/traces/index.html +18 -0
  31. package/src/capabilities/tests/traces/index.js +81 -0
  32. package/src/cors.js +1 -1
  33. package/src/cors.spec.js +41 -0
  34. package/src/coverage.js +30 -18
  35. package/src/coverage.spec.js +79 -0
  36. package/src/csv-reader.js +36 -0
  37. package/src/csv-reader.spec.js +42 -0
  38. package/src/csv-writer.js +52 -0
  39. package/src/csv-writer.spec.js +77 -0
  40. package/src/defaults/browser.js +144 -0
  41. package/src/defaults/jsdom/compatibility.js +95 -0
  42. package/src/defaults/jsdom/debug.js +23 -0
  43. package/src/defaults/jsdom/resource-loader.js +43 -0
  44. package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +39 -0
  45. package/src/defaults/jsdom.js +64 -0
  46. package/src/defaults/junit-xml-report.js +64 -0
  47. package/src/defaults/puppeteer.js +111 -0
  48. package/src/defaults/report/common.js +38 -0
  49. package/src/defaults/report/default.html +84 -0
  50. package/src/defaults/report/main.js +44 -0
  51. package/src/defaults/report/progress.js +49 -0
  52. package/src/defaults/report/styles.css +66 -0
  53. package/src/defaults/report.js +69 -0
  54. package/src/defaults/selenium-webdriver/chrome.js +38 -0
  55. package/src/defaults/selenium-webdriver/edge.js +25 -0
  56. package/src/defaults/selenium-webdriver/firefox.js +31 -0
  57. package/src/defaults/selenium-webdriver.js +138 -0
  58. package/src/endpoints.js +70 -124
  59. package/src/error.js +52 -0
  60. package/src/error.spec.js +17 -0
  61. package/src/get-job-progress.js +69 -0
  62. package/src/get-job-progress.spec.js +175 -0
  63. package/src/inject/post.js +96 -0
  64. package/src/inject/post.spec.js +147 -0
  65. package/src/inject/qunit-hooks.js +6 -21
  66. package/src/inject/qunit-intercept.js +30 -0
  67. package/src/inject/qunit-redirect.js +15 -7
  68. package/src/job-mode.js +45 -0
  69. package/src/job.js +254 -108
  70. package/src/job.spec.js +413 -0
  71. package/src/npm.js +73 -0
  72. package/src/npm.spec.js +98 -0
  73. package/src/options.js +73 -0
  74. package/src/options.spec.js +125 -0
  75. package/src/output.js +450 -131
  76. package/src/qunit-hooks.js +116 -0
  77. package/src/qunit-hooks.spec.js +687 -0
  78. package/src/report.js +42 -0
  79. package/src/reserve.js +3 -4
  80. package/src/simulate.spec.js +437 -0
  81. package/src/symbols.js +8 -0
  82. package/src/tests.js +127 -84
  83. package/src/timeout.spec.js +39 -0
  84. package/src/tools.js +111 -4
  85. package/src/tools.spec.js +90 -0
  86. package/src/ui5.js +3 -3
  87. package/src/unhandled.js +6 -6
  88. package/src/unhandled.spec.js +63 -0
  89. package/defaults/chromium.js +0 -62
  90. package/src/progress.html +0 -71
  91. package/src/proxies.js +0 -8
  92. package/src/report.html +0 -202
  93. /package/{defaults → src/defaults}/nyc.json +0 -0
package/src/tests.js CHANGED
@@ -1,106 +1,149 @@
1
1
  'use strict'
2
2
 
3
- const { start } = require('./browsers')
4
- const { instrument, generateCoverageReport } = require('./coverage')
5
- const { filename, recreateDir } = require('./tools')
6
- const { join } = require('path')
7
- const { copyFile, writeFile } = require('fs').promises
3
+ const { probe, start } = require('./browsers')
4
+ const { instrument } = require('./coverage')
5
+ const { recreateDir } = require('./tools')
8
6
  const { globallyTimedOut } = require('./timeout')
9
- const output = require('./output')
7
+ const { save, generate } = require('./report')
8
+ const { getOutput } = require('./output')
9
+ const {
10
+ $probeUrlsStarted,
11
+ $probeUrlsCompleted,
12
+ $testPagesStarted,
13
+ $testPagesCompleted
14
+ } = require('./symbols')
15
+ const { UTRError } = require('./error')
10
16
 
11
- async function saveJob (job) {
12
- await writeFile(join(job.tstReportDir, 'job.json'), JSON.stringify({
13
- ...job,
14
- // Following members are useless because already serialized or not relevant
15
- status: undefined,
16
- testPageUrls: undefined,
17
- browsers: undefined,
18
- testPages: undefined
19
- }))
20
- }
21
-
22
- async function extractTestPages (job) {
23
- job.start = new Date()
24
- await instrument(job)
25
- await recreateDir(job.tstReportDir)
26
- await saveJob(job)
27
- job.status = 'Extracting test pages'
28
- job.testPageUrls = []
29
- await start(job, '/' + job.testsuite)
30
- if (job.testPageUrls.length === 0) {
31
- output.noTestPageFound()
32
- job.failed = true
33
- return Promise.resolve()
34
- }
35
- job.testPagesStarted = 0
36
- job.testPagesCompleted = 0
37
- job.testPages = {}
38
- job.status = 'Executing test pages'
39
- delete job.failed
40
- const promises = []
41
- for (let i = 0; i < Math.min(job.parallel, job.testPageUrls.length); ++i) {
42
- promises.push(runTestPage(job))
43
- }
44
- return Promise.all(promises)
45
- }
46
-
47
- async function runTestPage (job) {
48
- const { length } = job.testPageUrls
49
- if (job.testPagesCompleted === length) {
50
- return generateReport(job)
17
+ async function run (task, job) {
18
+ const {
19
+ urlsMember,
20
+ startedMember,
21
+ completedMember,
22
+ method
23
+ } = task
24
+ const output = getOutput(job)
25
+ const urls = job[urlsMember]
26
+ const { length } = urls
27
+ if (job[completedMember] === length) {
28
+ return
51
29
  }
52
- if (job.testPagesStarted === length) {
30
+ if (job[startedMember] === length) {
53
31
  return
54
32
  }
55
- const index = job.testPagesStarted++
56
- const url = job.testPageUrls[index]
33
+ const index = job[startedMember]++
34
+ const url = urls[index]
57
35
  if (globallyTimedOut(job)) {
58
36
  output.globalTimeout(url)
37
+ job.failed = true
59
38
  } else if (job.failFast && job.failed) {
60
39
  output.failFast(url)
61
40
  } else {
62
- await start(job, url)
63
- const page = job.testPages[url]
64
- if (page) {
65
- const reportFileName = join(job.tstReportDir, `${filename(url)}.json`)
66
- await writeFile(reportFileName, JSON.stringify(page))
41
+ try {
42
+ await method(job, url)
43
+ } catch (error) {
44
+ job.failed = true
45
+ }
46
+ }
47
+ ++job[completedMember]
48
+ return run(task, job)
49
+ }
50
+
51
+ async function probeUrl (job, url) {
52
+ const output = getOutput(job)
53
+ try {
54
+ let scripts
55
+ if (job.mode === 'url' && job.browserCapabilities.scripts) {
56
+ scripts = [
57
+ 'post.js',
58
+ 'qunit-redirect.js'
59
+ ]
60
+ }
61
+ await start(job, url, scripts)
62
+ } catch (error) {
63
+ output.startFailed(url, error)
64
+ throw error
65
+ }
66
+ }
67
+
68
+ async function runTestPage (job, url) {
69
+ const output = getOutput(job)
70
+ try {
71
+ let scripts
72
+ if (job.mode === 'url' && job.browserCapabilities.scripts) {
73
+ scripts = [
74
+ 'post.js',
75
+ 'qunit-intercept.js',
76
+ 'qunit-hooks.js'
77
+ ]
67
78
  }
79
+ await start(job, url, scripts)
80
+ } catch (error) {
81
+ output.startFailed(url, error)
82
+ throw error
83
+ }
84
+ }
85
+
86
+ function parallelize (task, job) {
87
+ const {
88
+ urlsMember,
89
+ completedMember,
90
+ startedMember
91
+ } = task
92
+ job[startedMember] = 0
93
+ job[completedMember] = 0
94
+ const max = Math.min(job.parallel, job[urlsMember].length)
95
+ const promises = []
96
+ for (let i = 0; i < max; ++i) {
97
+ promises.push(run(task, job))
68
98
  }
69
- ++job.testPagesCompleted
70
- return runTestPage(job)
99
+ return Promise.all(promises)
71
100
  }
72
101
 
73
- async function generateReport (job) {
74
- job.status = 'Finalizing'
75
- // Simple report
76
- job.failed = 0
77
- const pages = []
78
- for (const url of job.testPageUrls) {
79
- const page = job.testPages[url]
80
- if (page && page.report) {
81
- pages.push({
82
- url,
83
- failed: page.report.failed
84
- })
85
- job.failed += page.report.failed
86
- } else {
87
- pages.push({
88
- url,
89
- failed: -1
90
- })
91
- job.failed += 1
102
+ async function process (job) {
103
+ const output = getOutput(job)
104
+ job.start = new Date()
105
+ delete job.failed
106
+ await instrument(job)
107
+ await save(job)
108
+ job.testPageUrls = []
109
+
110
+ job.status = 'Probing urls'
111
+ await parallelize({
112
+ urlsMember: 'url',
113
+ startedMember: $probeUrlsStarted,
114
+ completedMember: $probeUrlsCompleted,
115
+ method: probeUrl
116
+ }, job)
117
+
118
+ /* istanbul ignore else */
119
+ if (!job.debugProbeOnly) {
120
+ if (job.testPageUrls.length !== 0) {
121
+ job.status = 'Executing test pages'
122
+ await parallelize({
123
+ urlsMember: 'testPageUrls',
124
+ startedMember: $testPagesStarted,
125
+ completedMember: $testPagesCompleted,
126
+ method: runTestPage
127
+ }, job)
128
+ } else if (Object.keys(job.qunitPages || []).length === 0) {
129
+ output.noTestPageFound()
130
+ job.failed = true
92
131
  }
93
132
  }
94
- output.results(pages)
95
- await saveJob(job)
96
- await copyFile(join(__dirname, 'report.html'), join(job.tstReportDir, 'report.html'))
97
- await generateCoverageReport(job)
98
- output.timeSpent(job.start)
99
- job.status = 'Done'
133
+
134
+ await generate(job)
100
135
  }
101
136
 
102
- module.exports = job => {
103
- if (job.parallel) {
104
- return extractTestPages(job)
137
+ module.exports = {
138
+ async execute (job) {
139
+ await recreateDir(job.reportDir)
140
+ getOutput(job).version()
141
+ await probe(job)
142
+ if (job.mode !== 'url') {
143
+ job.url = [`http://localhost:${job.port}/${job.testsuite}`]
144
+ } else if (!job.browserCapabilities.scripts) {
145
+ throw UTRError.BROWSER_MISS_SCRIPTS_CAPABILITY()
146
+ }
147
+ return process(job)
105
148
  }
106
149
  }
@@ -0,0 +1,39 @@
1
+ const { getPageTimeout, globallyTimedOut } = require('./timeout')
2
+
3
+ describe('src/timeout', () => {
4
+ describe('getPageTimeout', () => {
5
+ it('returns 0 when no timeout is defined', () => {
6
+ expect(getPageTimeout({})).toStrictEqual(0)
7
+ })
8
+
9
+ it('returns pageTimeout when no globalTimeout is defined', () => {
10
+ expect(getPageTimeout({ pageTimeout: 123 })).toStrictEqual(123)
11
+ })
12
+
13
+ it('returns pageTimeout if smaller than globalTimeout', () => {
14
+ expect(getPageTimeout({ pageTimeout: 123, globalTimeout: 1000, start: new Date() })).toStrictEqual(123)
15
+ })
16
+
17
+ it('returns reminder of the globalTimeout', () => {
18
+ expect(getPageTimeout({ globalTimeout: 10, start: new Date() })).not.toStrictEqual(0)
19
+ })
20
+
21
+ it('returns reminder of the globalTimeout if smaller than pageTimeout', () => {
22
+ expect(getPageTimeout({ pageTimeout: 123, globalTimeout: 10, start: new Date() })).not.toStrictEqual(123)
23
+ })
24
+ })
25
+
26
+ describe('globallyTimedOut', () => {
27
+ it('returns false if no globalTimeout', () => {
28
+ expect(globallyTimedOut({})).toStrictEqual(false)
29
+ })
30
+
31
+ it('returns false if not globally timed out', () => {
32
+ expect(globallyTimedOut({ globalTimeout: 1000, start: new Date() })).toStrictEqual(false)
33
+ })
34
+
35
+ it('returns true if globally timed out', () => {
36
+ expect(globallyTimedOut({ globalTimeout: 10, start: new Date(Date.now() - 1000) })).toStrictEqual(true)
37
+ })
38
+ })
39
+ })
package/src/tools.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fsPromises = require('fs').promises
4
4
  const { mkdir, stat } = fsPromises
5
+ const { createHash } = require('crypto')
5
6
 
6
7
  let rm
7
8
  /* istanbul ignore next */ // Hard to test both in the same run
@@ -13,6 +14,19 @@ if (process.version > 'v14.14') {
13
14
 
14
15
  const recursive = { recursive: true }
15
16
 
17
+ const stripUrlHash = url => url.split('#')[0]
18
+
19
+ const filename = url => {
20
+ const hash = createHash('shake256', {
21
+ outputLength: 8
22
+ })
23
+ hash.update(stripUrlHash(url))
24
+ return hash.digest('base64')
25
+ .replace(/=/g, '')
26
+ .replace(/\+/g, '_')
27
+ .replace(/\//g, '$')
28
+ }
29
+
16
30
  const cleanDir = async dir => {
17
31
  try {
18
32
  await stat(dir)
@@ -22,12 +36,105 @@ const cleanDir = async dir => {
22
36
  }
23
37
  }
24
38
 
39
+ const $op = Symbol('pad.op')
40
+ const $x = Symbol('pad.x')
41
+ const $lt = Symbol('pad.lt')
42
+ const $w = Symbol('pad.w')
43
+ function pad (width) {
44
+ if (!width) {
45
+ width = process.stdout.columns || 80
46
+ }
47
+ const ops = {
48
+ [$x] (widthLeft) {
49
+ return ''.padStart(widthLeft, this.text)
50
+ },
51
+ [$lt] (widthLeft) {
52
+ const { text, padding } = this
53
+ if (text.length <= widthLeft) {
54
+ return text.padEnd(widthLeft, padding)
55
+ }
56
+ return '...' + text.substring(text.length - widthLeft + 3)
57
+ },
58
+ [$w] (widthLeft, result, opIndex) {
59
+ const { text } = this
60
+ if (text.length < widthLeft && !text.includes('\n')) {
61
+ return text.padEnd(widthLeft, ' ')
62
+ }
63
+ const lines = []
64
+ text.split(/\r?\n/).forEach(line => {
65
+ if (line.length <= widthLeft) {
66
+ lines.push(line.padEnd(widthLeft, ' '))
67
+ } else {
68
+ for (let offset = 0; offset < line.length; offset += widthLeft - 1) {
69
+ const part = line.slice(offset, offset + widthLeft - 1)
70
+ if (part.length < widthLeft - 1) {
71
+ lines.push(part.padEnd(widthLeft, ' '))
72
+ } else {
73
+ lines.push(`${part}↵`)
74
+ }
75
+ }
76
+ }
77
+ })
78
+ const before = result.slice(0, opIndex).join('')
79
+ const after = result.slice(opIndex + 1).join('')
80
+ return lines.join(after + '\n' + before)
81
+ }
82
+ }
83
+ return (strings, ...values) => {
84
+ const result = []
85
+ let op
86
+ let opIndex
87
+ const length = strings.reduce((total, string, index) => {
88
+ result.push(string)
89
+ total += string.length
90
+ let value = values[index]
91
+ if (value === null || value === undefined) {
92
+ return total
93
+ }
94
+ if (value[$op]) {
95
+ if (opIndex !== undefined) {
96
+ throw new Error('Only one operator is allowed')
97
+ }
98
+ op = value
99
+ opIndex = result.length
100
+ result.push(value)
101
+ } else {
102
+ if (typeof value !== 'string') {
103
+ value = value.toString()
104
+ }
105
+ result.push(value)
106
+ total += value.length
107
+ }
108
+ return total
109
+ }, 0)
110
+ if (op !== undefined) {
111
+ const widthLeft = width - length
112
+ result[opIndex] = ops[op[$op]].call(op, widthLeft, result, opIndex)
113
+ }
114
+ return result.join('')
115
+ }
116
+ }
117
+
118
+ pad.x = (text) => ({ [$op]: $x, text })
119
+ pad.lt = (text, padding = ' ') => ({ [$op]: $lt, text, padding })
120
+ pad.w = (text) => ({ [$op]: $w, text })
121
+
25
122
  module.exports = {
26
- filename: url => escape(url)
27
- .replace(/\//g, '_')
28
- .replace(/%([0-9a-z]{2})/ig, (match, hexa) => `_${hexa}_`),
123
+ stripUrlHash,
124
+ filename,
29
125
  cleanDir,
30
126
  createDir: dir => mkdir(dir, recursive),
31
127
  recreateDir: dir => cleanDir(dir).then(() => mkdir(dir, recursive)),
32
- extractUrl: headers => headers.referer.match(/http:\/\/[^/]+(?::\d+)?(\/.*)/)[1]
128
+ extractPageUrl: headers => headers['x-page-url'],
129
+ allocPromise () {
130
+ let resolve
131
+ let reject
132
+ const promise = new Promise((_resolve, _reject) => {
133
+ resolve = _resolve
134
+ reject = _reject
135
+ })
136
+ return { promise, resolve, reject }
137
+ },
138
+ noop () {},
139
+ pad
33
140
  }
@@ -0,0 +1,90 @@
1
+ const { filename, noop, pad } = require('./tools')
2
+
3
+ describe('src/tools', () => {
4
+ describe('filename', () => {
5
+ const baseUrl = 'http://localhost:8085/test.html'
6
+
7
+ it('generates unique IDs for different search param', () => {
8
+ const id1 = filename(baseUrl + '?param1')
9
+ const id2 = filename(baseUrl + '?param2')
10
+ expect(id1).not.toBe(id2)
11
+ })
12
+
13
+ it('generates similar IDs for different hashes', () => {
14
+ const id1 = filename(baseUrl + '#param1')
15
+ const id2 = filename(baseUrl + '#param2')
16
+ expect(id1).toBe(id2)
17
+ })
18
+ })
19
+
20
+ describe('noop', () => {
21
+ it('is an empty function', () => {
22
+ expect(noop()).toBeUndefined()
23
+ expect(() => noop()).not.toThrow()
24
+ })
25
+ })
26
+
27
+ describe('pad', () => {
28
+ it('works if no operator is given', () => {
29
+ expect(pad(20)`abc`).toStrictEqual('abc')
30
+ })
31
+
32
+ it('fails if several operators are given', () => {
33
+ expect(() => pad()`${pad.x('-')}${pad.x('+')}`).toThrow()
34
+ })
35
+
36
+ it('extends a string (begin)', () => {
37
+ expect(pad(20)`${pad.x('-')}12`).toStrictEqual('------------------12')
38
+ })
39
+
40
+ it('extends a string (middle)', () => {
41
+ expect(pad(20)`1${pad.x('-')}2`).toStrictEqual('1------------------2')
42
+ })
43
+
44
+ it('extends a string (multiple interpolation)', () => {
45
+ expect(pad(20)`1${'3'}${pad.x('-')}${'4'}2`).toStrictEqual('13----------------42')
46
+ })
47
+
48
+ it('extends a string (end)', () => {
49
+ expect(pad(20)`12${pad.x('-')}`).toStrictEqual('12------------------')
50
+ })
51
+
52
+ it('extends a string (multiple chars)', () => {
53
+ expect(pad(20)`1${pad.x('-+')}2`).toStrictEqual('1-+-+-+-+-+-+-+-+-+2')
54
+ })
55
+
56
+ it('left trims a string', () => {
57
+ expect(pad(20)`1${pad.lt('abcdefghijklmnopqrstuvwxyz')}2`).toStrictEqual('1...lmnopqrstuvwxyz2')
58
+ })
59
+
60
+ it('left trims a small string', () => {
61
+ expect(pad(20)`1${pad.lt('abcdef')}2`).toStrictEqual('1abcdef 2')
62
+ })
63
+
64
+ it('wraps text (small enough)', () => {
65
+ expect(pad(20)`1${pad.w('abcdef')}2`).toStrictEqual('1abcdef 2')
66
+ })
67
+
68
+ it('wraps multiline', () => {
69
+ expect(pad(20)`1${pad.w('a\nb')}2`)
70
+ .toStrictEqual(`1a 2
71
+ 1b 2`)
72
+ })
73
+
74
+ it('wraps multiline / long text', () => {
75
+ expect(pad(20)`1${pad.w('first line\nsecond longer line to wrap\nfits exactly width')}2`)
76
+ .toStrictEqual(`1first line 2
77
+ 1second longer lin↵2
78
+ 1e to wrap 2
79
+ 1fits exactly width2`)
80
+ })
81
+
82
+ it('wraps multiline / long text (DOS carriage return)', () => {
83
+ expect(pad(20)`1${pad.w('first line\r\nsecond longer line to wrap\r\nfits exactly width')}2`)
84
+ .toStrictEqual(`1first line 2
85
+ 1second longer lin↵2
86
+ 1e to wrap 2
87
+ 1fits exactly width2`)
88
+ })
89
+ })
90
+ })
package/src/ui5.js CHANGED
@@ -4,12 +4,12 @@ const { dirname, join } = require('path')
4
4
  const { createWriteStream } = require('fs')
5
5
  const { mkdir, unlink } = require('fs').promises
6
6
  const { capture } = require('reserve')
7
- const output = require('./output')
7
+ const { getOutput } = require('./output')
8
8
 
9
9
  module.exports = job => {
10
10
  const [, hostName] = /https?:\/\/([^/]*)/.exec(job.ui5)
11
11
  const [, version] = /(\d+\.\d+\.\d+)?$/.exec(job.ui5)
12
- const cacheBase = join(job.cwd, job.cache, hostName.replace(':', '_'), version || '')
12
+ const cacheBase = join(job.cache || '', hostName.replace(':', '_'), version || '')
13
13
  const match = /\/((?:test-)?resources\/.*)/
14
14
  const ifCacheEnabled = (request, url, match) => job.cache ? match : false
15
15
  const uncachable = {}
@@ -59,7 +59,7 @@ module.exports = job => {
59
59
  file.end()
60
60
  uncachable[path] = true
61
61
  if (response.statusCode !== 404) {
62
- output.failedToCacheUI5resource(path, response.statusCode)
62
+ getOutput(job).failedToCacheUI5resource(path, response.statusCode)
63
63
  }
64
64
  return unlink(cachePath)
65
65
  })
package/src/unhandled.js CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
- const { extractUrl } = require('./tools')
3
+ const { extractPageUrl, noop } = require('./tools')
4
4
  const { join } = require('path')
5
5
  const { writeFile } = require('fs')
6
- const output = require('./output')
6
+ const { getOutput } = require('./output')
7
7
 
8
8
  module.exports = job => {
9
- const unhandled = join(job.tstReportDir, 'unhandled.txt')
9
+ const unhandled = join(job.reportDir, 'unhandled.txt')
10
10
  let outputUnhandled = true
11
11
  return [{
12
12
  custom: ({ headers, method, url }) => {
@@ -20,12 +20,12 @@ module.exports = job => {
20
20
  status = 500
21
21
  }
22
22
  if (outputUnhandled) {
23
- output.unhandled()
23
+ getOutput(job).unhandled()
24
24
  outputUnhandled = false
25
25
  }
26
- writeFile(unhandled, `${extractUrl(headers)} ${status} ${method} ${url}\n`, {
26
+ writeFile(unhandled, `${extractPageUrl(headers)} ${status} ${method} ${url}\n`, {
27
27
  flag: 'a'
28
- }, () => {})
28
+ }, noop)
29
29
  return status
30
30
  }
31
31
  }]
@@ -0,0 +1,63 @@
1
+ const { join } = require('path')
2
+ const { getOutput } = require('./output')
3
+ const jobFactory = require('./job')
4
+ const mappingFactory = require('./unhandled')
5
+
6
+ const cwd = join(__dirname, '../test/project')
7
+
8
+ describe('src/unhandled', () => {
9
+ let unhandled
10
+ let output
11
+ let unhandledCall
12
+
13
+ beforeEach(() => {
14
+ const job = jobFactory.fromCmdLine(cwd, [])
15
+ output = getOutput(job)
16
+ unhandledCall = 0
17
+ output.unhandled = () => {
18
+ ++unhandledCall
19
+ }
20
+ unhandled = mappingFactory(job)[0].custom
21
+ })
22
+
23
+ const expectedIgnores = [
24
+ 'favicon.ico',
25
+ 'component-preload.js',
26
+ 'button-dbg.js',
27
+ 'i18n_en.properties'
28
+ ]
29
+ expectedIgnores.forEach(url => {
30
+ it(`does not log known GET patterns (${url})`, () => {
31
+ expect(unhandled({ method: 'GET', url })).toStrictEqual(404)
32
+ expect(unhandledCall).toStrictEqual(0)
33
+ })
34
+ })
35
+
36
+ const expectedWarnings = [
37
+ 'any-mock-data-file.json',
38
+ 'sourceFile.js'
39
+ ]
40
+ expectedWarnings.forEach(url => {
41
+ it(`warns about 404 GET (${url})`, () => {
42
+ expect(unhandled({ method: 'GET', url, headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(404)
43
+ expect(unhandledCall).toStrictEqual(1)
44
+ })
45
+ })
46
+ it('Warns only once', () => {
47
+ expectedWarnings.forEach(url => {
48
+ expect(unhandled({ method: 'GET', url, headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(404)
49
+ })
50
+ expect(unhandledCall).toStrictEqual(1)
51
+ })
52
+
53
+ it('logs errors for any other verb', () => {
54
+ expect(unhandled({ method: 'POST', url: '/any_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
55
+ expect(unhandledCall).toStrictEqual(1)
56
+ })
57
+
58
+ it('logs errors only once', () => {
59
+ expect(unhandled({ method: 'POST', url: '/any_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
60
+ expect(unhandled({ method: 'POST', url: '/any_other_url', headers: { referer: 'http://localhost:3475/test.html' } })).toStrictEqual(500)
61
+ expect(unhandledCall).toStrictEqual(1)
62
+ })
63
+ })
@@ -1,62 +0,0 @@
1
- 'use strict'
2
-
3
- const puppeteer = require('puppeteer')
4
- const { join } = require('path')
5
- const { createWriteStream } = require('fs')
6
-
7
- const [url, reportDir] = process.argv.slice(2).filter(arg => !arg.startsWith('--'))
8
- const headless = !process.argv.some(arg => arg === '--visible')
9
-
10
- let browser
11
- let page
12
- let consoleStream
13
-
14
- if (reportDir) {
15
- consoleStream = createWriteStream(join(reportDir, 'console.txt'), 'utf-8')
16
- }
17
-
18
- process.on('message', async message => {
19
- try {
20
- if (message.command === 'stop') {
21
- await browser.close()
22
- process.exit(0)
23
- } else if (message.command === 'screenshot') {
24
- if (reportDir && page) {
25
- await page.screenshot({ path: join(reportDir, message.filename) })
26
- process.send(message)
27
- }
28
- } else if (message.command === 'capabilities') {
29
- process.send({
30
- command: 'capabilities',
31
- screenshot: true,
32
- consoleLog: true
33
- })
34
- }
35
- } catch (e) {
36
- console.error(e)
37
- process.exit(-2)
38
- }
39
- })
40
-
41
- async function main () {
42
- browser = await puppeteer.launch({
43
- headless,
44
- defaultViewport: null,
45
- args: [
46
- url,
47
- '--start-maximized',
48
- '--no-sandbox',
49
- '--disable-gpu',
50
- '--disable-extensions'
51
- ]
52
- })
53
- page = (await browser.pages())[0]
54
- if (consoleStream) {
55
- page.on('console', message => consoleStream.write(`${message.type().substr(0, 3).toUpperCase()} ${message.text()}\n`))
56
- }
57
- }
58
-
59
- main().catch(e => {
60
- console.error(e)
61
- process.exit(-1)
62
- })