ui5-test-runner 5.0.0 → 5.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.
@@ -0,0 +1,27 @@
1
+ {
2
+ "testTimeout": 15000,
3
+ "setupFilesAfterEnv": [
4
+ "./test/setup.js"
5
+ ],
6
+ "testPathIgnorePatterns": [
7
+ "/node_modules/",
8
+ "/capabilities/"
9
+ ],
10
+ "collectCoverage": true,
11
+ "collectCoverageFrom": [
12
+ "src/*.js"
13
+ ],
14
+ "coveragePathIgnorePatterns": [
15
+ "\\.spec\\.js",
16
+ "output\\.js",
17
+ "b\\capabilities\\b"
18
+ ],
19
+ "coverageThreshold": {
20
+ "global": {
21
+ "branches": 80,
22
+ "functions": 80,
23
+ "lines": 80,
24
+ "statements": 80
25
+ }
26
+ }
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui5-test-runner",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Standalone test runner for UI5",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "test:report": "node ./src/defaults/report.js ./test/report && reserve --config ./test/report/reserve.json",
22
22
  "test:text-report": "node ./src/defaults/text-report.js ./test/report",
23
23
  "build:doc": "node build/doc",
24
- "clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio"
24
+ "clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio jsdom"
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
@@ -43,25 +43,24 @@
43
43
  },
44
44
  "homepage": "https://github.com/ArnaudBuchholz/ui5-test-runner#readme",
45
45
  "dependencies": {
46
- "commander": "^12.0.0",
47
- "mime": "^3.0.0",
46
+ "commander": "^12.1.0",
48
47
  "punybind": "^1.2.1",
49
48
  "punyexpr": "^1.0.4",
50
- "reserve": "^1.15.9"
49
+ "reserve": "2.0.1"
51
50
  },
52
51
  "devDependencies": {
53
- "@openui5/types": "^1.123.0",
54
- "@ui5/cli": "^3.10.0",
52
+ "@openui5/types": "^1.124.0",
53
+ "@ui5/cli": "^3.10.3",
55
54
  "@ui5/middleware-code-coverage": "^1.1.1",
56
55
  "dotenv": "^16.4.5",
57
56
  "jest": "^29.7.0",
58
57
  "nock": "^13.5.4",
59
58
  "nyc": "^15.1.0",
60
- "rimraf": "^5.0.5",
59
+ "rimraf": "^5.0.7",
61
60
  "standard": "^17.1.0",
62
61
  "start-server-and-test": "^2.0.3",
63
62
  "typescript": "^5.4.5",
64
- "ui5-tooling-transpile": "^3.4.0"
63
+ "ui5-tooling-transpile": "^3.4.2"
65
64
  },
66
65
  "optionalDependencies": {
67
66
  "fsevents": "^2.3.3"
@@ -77,32 +76,5 @@
77
76
  "sap",
78
77
  "opaTest"
79
78
  ]
80
- },
81
- "jest": {
82
- "testTimeout": 15000,
83
- "setupFilesAfterEnv": [
84
- "./test/setup.js"
85
- ],
86
- "testPathIgnorePatterns": [
87
- "/node_modules/",
88
- "/capabilities/"
89
- ],
90
- "collectCoverage": true,
91
- "collectCoverageFrom": [
92
- "src/*.js"
93
- ],
94
- "coveragePathIgnorePatterns": [
95
- "\\.spec\\.js",
96
- "output\\.js",
97
- "b\\capabilities\\b"
98
- ],
99
- "coverageThreshold": {
100
- "global": {
101
- "branches": 80,
102
- "functions": 80,
103
- "lines": 80,
104
- "statements": 80
105
- }
106
- }
107
79
  }
108
80
  }
@@ -62,17 +62,23 @@ async function capabilities (job) {
62
62
  const listener = listeners[listenerIndex]
63
63
  await listener({
64
64
  endpoint,
65
- body: JSON.parse(await body(request))
65
+ body: await body(request).json()
66
66
  })
67
67
  response.writeHead(200)
68
68
  response.end()
69
69
  }
70
70
  }, {
71
71
  match: '^/inject/(.*)',
72
- file: join(__dirname, '../inject/$1')
72
+ cwd: join(__dirname, '../inject'),
73
+ file: '$1',
74
+ static: !job.debugDevMode
73
75
  }, {
74
76
  match: '^/(.*)',
75
- file: join(__dirname, '$1')
77
+ cwd: __dirname,
78
+ file: '$1',
79
+ static: !job.debugDevMode
80
+ }, {
81
+ status: 404
76
82
  }]
77
83
  })
78
84
  const server = serve(configuration)
package/src/coverage.js CHANGED
@@ -202,7 +202,7 @@ async function checkAllSourcesAreAvailable (job, coverageFilename) {
202
202
  output.debug('coverage', 'Checking remote source files...')
203
203
  const coverageData = require(coverageFilename)
204
204
  const filenames = Object.keys(coverageData)
205
- let changes = 0
205
+ let changes = false
206
206
  let basePath
207
207
  for (const filename of filenames) {
208
208
  const fileData = coverageData[filename]
@@ -213,7 +213,12 @@ async function checkAllSourcesAreAvailable (job, coverageFilename) {
213
213
  }
214
214
  if (filePath && filePath !== fileData.path) {
215
215
  fileData.path = filePath
216
- ++changes
216
+ changes = true
217
+ }
218
+ if (filename !== filePath) {
219
+ delete coverageData[filename]
220
+ coverageData[filePath] = fileData
221
+ changes = true
217
222
  }
218
223
  const fileFolder = dirname(filePath)
219
224
  if (basePath === undefined) {
@@ -229,7 +234,7 @@ async function checkAllSourcesAreAvailable (job, coverageFilename) {
229
234
  job.nycSettings.cwd = basePath
230
235
  await writeFile(job[$nycSettingsPath], JSON.stringify(job.nycSettings))
231
236
  }
232
- if (changes > 0) {
237
+ if (changes) {
233
238
  await writeFile(coverageFilename, JSON.stringify(coverageData))
234
239
  }
235
240
  }
@@ -292,8 +297,8 @@ module.exports = {
292
297
  const instrumentedBasePath = join(job.coverageTempDir, 'instrumented')
293
298
  const instrumentedMapping = {
294
299
  match: /(.*\.js)(\?.*)?$/,
295
- file: join(instrumentedBasePath, '$1'),
296
- 'ignore-if-not-found': true
300
+ cwd: instrumentedBasePath,
301
+ file: '$1'
297
302
  }
298
303
  if (job.mode === 'legacy' || job[$remoteOnLegacy]) {
299
304
  return [{
@@ -8,32 +8,64 @@ function fakeMatchMedia () {
8
8
  }
9
9
  }
10
10
 
11
- function wrapXHR (window, networkWriter) {
12
- const { XMLHttpRequest } = window
13
- const { open } = XMLHttpRequest.prototype
11
+ function wrapXHR ({ XMLHttpRequest }) {
12
+ const { open, send } = XMLHttpRequest.prototype
13
+ const $async = Symbol('async')
14
14
  XMLHttpRequest.prototype.open = function (...args) {
15
- const [method, url] = args
15
+ const [method, url, async] = args
16
16
  const log = () => {
17
17
  const { status } = this
18
- networkWriter.append({
18
+ console.log(JSON.stringify({
19
+ timestamp: new Date().toISOString(),
20
+ channel: 'network',
21
+ initiator: 'xhr',
19
22
  method,
20
23
  url,
24
+ async,
21
25
  status
22
- })
26
+ }))
23
27
  }
24
28
  this.addEventListener('load', log)
25
29
  this.addEventListener('error', log)
30
+ if (async === false) {
31
+ this[$async] = { method, url }
32
+ }
26
33
  return open.call(this, ...args)
27
34
  }
35
+ XMLHttpRequest.prototype.send = function (...args) {
36
+ if (this[$async]) {
37
+ const { method, url } = this[$async]
38
+ console.log(JSON.stringify({
39
+ timestamp: new Date().toISOString(),
40
+ channel: 'debug',
41
+ message: '>> XMLHttpRequest.prototype.send',
42
+ method,
43
+ url,
44
+ async: false
45
+ }))
46
+ }
47
+ const result = send.call(this, ...args)
48
+ if (this[$async]) {
49
+ const { method, url } = this[$async]
50
+ console.log(JSON.stringify({
51
+ timestamp: new Date().toISOString(),
52
+ channel: 'debug',
53
+ message: '<< XMLHttpRequest.prototype.send',
54
+ method,
55
+ url,
56
+ async: false
57
+ }))
58
+ }
59
+ return result
60
+ }
28
61
  }
29
62
 
30
- function adjustXPathResult (window) {
63
+ function adjustXPathResult ({ Document }) {
31
64
  /* https://ui5.sap.com/resources/sap/ui/model/odata/AnnotationParser-dbg.js
32
65
  getXPath: function() {
33
66
  xmlNodes.length = xmlNodes.snapshotLength;
34
67
  */
35
- const { Document } = window
36
- const evaluate = Document.prototype.evaluate
68
+ const { evaluate } = Document.prototype
37
69
  Document.prototype.evaluate = function () {
38
70
  const result = evaluate.apply(this, arguments)
39
71
  let length = result.length
@@ -48,10 +80,10 @@ function adjustXPathResult (window) {
48
80
  }
49
81
  }
50
82
 
51
- function fixMatchesDontThrow (window) {
52
- // https://github.com/jsdom/jsdom/issues/3057
53
- // Fix _nwsapiDontThrow which throws :-(
54
- const { document } = window
83
+ function fixMatchesDontThrow ({ document }) {
84
+ /* https://github.com/jsdom/jsdom/issues/3057
85
+ Fix _nwsapiDontThrow which throws :-(
86
+ */
55
87
  const [impl] = Object.getOwnPropertySymbols(document)
56
88
  const documentImpl = document[impl]
57
89
  let _nwsapiDontThrow
@@ -74,10 +106,45 @@ function fixMatchesDontThrow (window) {
74
106
  })
75
107
  }
76
108
 
77
- module.exports = ({
78
- window,
79
- networkWriter
80
- }) => {
109
+ function fixCaseSensitiveSelectors ({ Document }) {
110
+ /* https://github.com/SAP/openui5/blob/f41ed5504db1dc576dae7e7d403aaa02b918fef5/src/sap.ui.core/src/ui5loader-autoconfig.js#L75
111
+ oResult = check(globalThis.document.querySelector('SCRIPT[src][id=sap-ui-bootstrap]'), rResources);
112
+ jsdom uses case sensitive implementation of querySelector
113
+ */
114
+ const uppercaseTag = /\bSCRIPT\b/g
115
+ const { querySelector, querySelectorAll } = Document.prototype
116
+ Object.assign(Document.prototype, {
117
+ querySelector (selectors) {
118
+ const result = querySelector.call(this, selectors) || { length: 0 }
119
+ if (result.length === 0 && selectors.match(uppercaseTag)) {
120
+ console.log(JSON.stringify({
121
+ timestamp: new Date().toISOString(),
122
+ channel: 'debug',
123
+ message: 'overriding selectors upon empty result of document.querySelector',
124
+ selectors
125
+ }))
126
+ return querySelector.call(this, selectors.replace(uppercaseTag, tag => tag.toLowerCase()))
127
+ }
128
+ return result
129
+ },
130
+
131
+ querySelectorAll (selectors) {
132
+ const result = querySelectorAll.call(this, selectors) || { length: 0 }
133
+ if (result.length === 0 && selectors.match(uppercaseTag)) {
134
+ console.log(JSON.stringify({
135
+ timestamp: new Date().toISOString(),
136
+ channel: 'debug',
137
+ message: 'overriding selectors upon empty result of document.querySelectorAll',
138
+ selectors
139
+ }))
140
+ return querySelectorAll.call(this, selectors.replace(uppercaseTag, tag => tag.toLowerCase()))
141
+ }
142
+ return result
143
+ }
144
+ })
145
+ }
146
+
147
+ module.exports = window => {
81
148
  window.addEventListener('error', event => {
82
149
  const { message, filename, lineno, colno } = event
83
150
  window.console.error(`${filename}@${lineno}:${colno}: ${message}`)
@@ -89,7 +156,8 @@ module.exports = ({
89
156
  }
90
157
  window.matchMedia = window.matchMedia || fakeMatchMedia
91
158
 
92
- wrapXHR(window, networkWriter)
159
+ wrapXHR(window)
93
160
  adjustXPathResult(window)
94
161
  fixMatchesDontThrow(window)
162
+ fixCaseSensitiveSelectors(window)
95
163
  }
@@ -1,8 +1,4 @@
1
- module.exports = ({
2
- jsdom,
3
- networkWriter,
4
- consoleWriter
5
- }) => {
1
+ module.exports = jsdom => {
6
2
  const { ResourceLoader: JSDOMResourceLoader } = jsdom
7
3
 
8
4
  const { readFile } = require('fs/promises')
@@ -15,19 +11,24 @@ module.exports = ({
15
11
  const { response } = request
16
12
  let status
17
13
  if (response === undefined) {
18
- consoleWriter.append({
14
+ console.log(JSON.stringify({
15
+ timestamp: new Date().toISOString(),
16
+ channel: 'console',
19
17
  type: 'error',
20
18
  message: 'NETWORK ERROR : ' + (reason ? reason.toString() : 'unknown reason')
21
- })
19
+ }))
22
20
  status = 599
23
21
  } else {
24
22
  status = response.statusCode
25
23
  }
26
- networkWriter.append({
24
+ console.log(JSON.stringify({
25
+ timestamp: new Date().toISOString(),
26
+ channel: 'network',
27
+ initiator: 'resource-loader',
27
28
  method: 'GET',
28
29
  url,
29
30
  status
30
- })
31
+ }))
31
32
  }
32
33
  request.then(log, log)
33
34
  if (url.match(/sap\/ui\/test\/matchers\/Visible(-dbg)?.js/)) {
@@ -11,24 +11,42 @@ require('./browser')({
11
11
  capabilities: {
12
12
  modules: ['jsdom'],
13
13
  scripts: true,
14
- traces: ['console', 'network']
14
+ traces: ['multiplex']
15
15
  }
16
16
  },
17
17
 
18
18
  async run ({
19
19
  settings: { url, scripts, modules },
20
- options,
21
- consoleWriter,
22
- networkWriter
20
+ options
23
21
  }) {
24
22
  const jsdom = require(modules.jsdom)
25
23
  const { JSDOM, VirtualConsole } = jsdom
26
24
 
27
25
  const virtualConsole = new VirtualConsole()
28
- virtualConsole.on('error', (...args) => consoleWriter.append({ type: 'error', text: args.join(' ') }))
29
- virtualConsole.on('warn', (...args) => consoleWriter.append({ type: 'warning', text: args.join(' ') }))
30
- virtualConsole.on('info', (...args) => consoleWriter.append({ type: 'info', text: args.join(' ') }))
31
- virtualConsole.on('log', (...args) => consoleWriter.append({ type: 'log', text: args.join(' ') }))
26
+ virtualConsole.on('error', (...args) => console.log(JSON.stringify({
27
+ timestamp: new Date().toISOString(),
28
+ channel: 'console',
29
+ type: 'error',
30
+ message: args.join(' ')
31
+ })))
32
+ virtualConsole.on('warn', (...args) => console.log(JSON.stringify({
33
+ timestamp: new Date().toISOString(),
34
+ channel: 'console',
35
+ type: 'warning',
36
+ message: args.join(' ')
37
+ })))
38
+ virtualConsole.on('info', (...args) => console.log(JSON.stringify({
39
+ timestamp: new Date().toISOString(),
40
+ channel: 'console',
41
+ type: 'info',
42
+ message: args.join(' ')
43
+ })))
44
+ virtualConsole.on('log', (...args) => console.log(JSON.stringify({
45
+ timestamp: new Date().toISOString(),
46
+ channel: 'console',
47
+ type: 'log',
48
+ message: args.join(' ')
49
+ })))
32
50
 
33
51
  let mainWindow
34
52
 
@@ -41,7 +59,7 @@ require('./browser')({
41
59
  writable: false
42
60
  })
43
61
  }
44
- require('./jsdom/compatibility')({ window, networkWriter })
62
+ require('./jsdom/compatibility')(window)
45
63
  if (options.debug) {
46
64
  require('./jsdom/debug')(window)
47
65
  }
@@ -64,11 +82,7 @@ require('./browser')({
64
82
  runScripts: 'dangerously',
65
83
  pretendToBeVisual: true,
66
84
  virtualConsole,
67
- resources: require('./jsdom/resource-loader')({
68
- jsdom,
69
- networkWriter,
70
- consoleWriter
71
- }),
85
+ resources: require('./jsdom/resource-loader')(jsdom),
72
86
  beforeParse
73
87
  })
74
88
  }
package/src/endpoints.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { join } = require('path')
3
+ const { join, dirname, basename } = require('path')
4
4
  const { body } = require('reserve')
5
5
  const { extractPageUrl } = require('./tools')
6
6
  const { Request, Response } = require('reserve')
@@ -18,7 +18,7 @@ const punybindBinPath = join(resolveDependencyPath('punybind'), 'dist/punybind.j
18
18
  module.exports = job => {
19
19
  async function endpointImpl (api, implementation, request) {
20
20
  const url = extractPageUrl(request.headers)
21
- const data = JSON.parse(await body(request))
21
+ const data = await body(request)
22
22
  try {
23
23
  await implementation.call(this, url, data)
24
24
  } catch (error) {
@@ -75,7 +75,9 @@ module.exports = job => {
75
75
  }, {
76
76
  // QUnit hooks
77
77
  match: '^/_/qunit-hooks.js',
78
- file: join(__dirname, './inject/qunit-hooks.js')
78
+ cwd: __dirname,
79
+ file: 'inject/qunit-hooks.js',
80
+ static: !job.debugDevMode
79
81
  }, {
80
82
  // Concatenate qunit.js source with hooks
81
83
  match: /\/thirdparty\/(qunit(?:-2)?(?:-dbg)?\.js)/,
@@ -123,23 +125,33 @@ module.exports = job => {
123
125
  }, {
124
126
  // UI to follow progress
125
127
  match: '^/_/progress.html',
126
- file: job.progressPage
128
+ cwd: dirname(job.progressPage),
129
+ file: basename(job.progressPage),
130
+ static: !job.debugDevMode
127
131
  }, {
128
132
  // Report 'main' substituted for progress
129
133
  match: '^/_/report/main.js',
130
- file: join(__dirname, 'defaults/report/progress.js')
134
+ cwd: __dirname,
135
+ file: 'defaults/report/progress.js',
136
+ static: !job.debugDevMode
131
137
  }, {
132
138
  // Other report resources
133
139
  match: '^/_/report/(.*)',
134
- file: join(__dirname, 'defaults/report/$1')
140
+ cwd: __dirname,
141
+ file: 'defaults/report/$1',
142
+ static: !job.debugDevMode
135
143
  }, {
136
144
  // punybind
137
145
  match: '^/_/punybind.js',
138
- file: punybindBinPath
146
+ cwd: dirname(punybindBinPath),
147
+ file: basename(punybindBinPath),
148
+ static: !job.debugDevMode
139
149
  }, {
140
150
  // punyexpr
141
151
  match: '^/_/punyexpr.js',
142
- file: punyexprBinPath
152
+ cwd: dirname(punyexprBinPath),
153
+ file: basename(punyexprBinPath),
154
+ static: !job.debugDevMode
143
155
  }, {
144
156
  // Endpoint to retry on progress
145
157
  method: 'INFO',
@@ -155,15 +167,21 @@ module.exports = job => {
155
167
  }, {
156
168
  // Endpoint to coverage files
157
169
  match: '^/_/coverage/(.*)',
158
- file: join(job.coverageReportDir, '$1')
170
+ cwd: job.coverageReportDir,
171
+ file: '$1',
172
+ static: false
159
173
  }, {
160
174
  // Endpoint to report
161
175
  match: '^/_/report.html',
162
- file: join(__dirname, 'report.html')
176
+ cwd: __dirname,
177
+ file: 'report.html',
178
+ static: !job.debugDevMode
163
179
  }, {
164
180
  // Endpoint to report files
165
181
  match: '^/_/(.*)',
166
- file: join(job.reportDir, '$1')
182
+ cwd: job.reportDir,
183
+ file: '$1',
184
+ static: false
167
185
  }]
168
186
  : []
169
187
  }
@@ -83,7 +83,10 @@
83
83
  })
84
84
  xhr.open('POST', base + '/_/' + url)
85
85
  xhr.setRequestHeader('x-page-url', top.location)
86
- xhr.send(stringify(data))
86
+ xhr.setRequestHeader('content-type', 'application/json')
87
+ const json = stringify(data)
88
+ xhr.setRequestHeader('content-length', json.length)
89
+ xhr.send(json)
87
90
  })
88
91
  }
89
92
  lastPost = lastPost
package/src/job-mode.js CHANGED
@@ -28,6 +28,7 @@ function buildAndCheckMode (job) {
28
28
  'reportDir',
29
29
  'pageTimeout',
30
30
  'browserCloseTimeout',
31
+ 'browserRetry',
31
32
  'failFast',
32
33
  'keepAlive',
33
34
  'alternateNpmPath',
package/src/job.js CHANGED
@@ -150,6 +150,7 @@ function getCommand (cwd) {
150
150
  .option('-cpi, --coverage-proxy-include <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to instrument for coverage`, regex, '.*')
151
151
  .option('-cpe, --coverage-proxy-exclude <regexp>', `[🔗] ${EXPERIMENTAL_OPTION} urls to ignore for coverage`, regex, '/((test-)?resources|tests?)/')
152
152
 
153
+ .addOption(new Option('--debug-dev-mode', DEBUG_OPTION, boolean).hideHelp())
153
154
  .addOption(new Option('--debug-probe-only', DEBUG_OPTION, boolean).hideHelp())
154
155
  .addOption(new Option('--debug-keep-browser-open', DEBUG_OPTION, boolean).hideHelp())
155
156
  .addOption(new Option('--debug-memory', DEBUG_OPTION, boolean).hideHelp())
package/src/output.js CHANGED
@@ -260,6 +260,9 @@ function build (job) {
260
260
  version: () => {
261
261
  const { name, version } = require(join(__dirname, '../package.json'))
262
262
  log(job, p80()`${name}@${version}`)
263
+ if (job.debugDevMode) {
264
+ log(job, p80()`⚠️ Development mode ⚠️`)
265
+ }
263
266
  },
264
267
 
265
268
  serving: url => {
package/src/reserve.js CHANGED
@@ -1,4 +1,3 @@
1
- const { join } = require('path')
2
1
  const cors = require('./cors')
3
2
  const endpoints = require('./endpoints')
4
3
  const { mappings: coverage } = require('./coverage')
@@ -17,9 +16,9 @@ module.exports = async job => check({
17
16
  {
18
17
  // Project mapping
19
18
  match: /^\/(.*)/,
20
- file: join(job.webapp, '$1'),
21
- strict: true,
22
- 'ignore-if-not-found': true
19
+ cwd: job.webapp,
20
+ file: '$1',
21
+ static: !job.watch && !job.debugDevMode
23
22
  },
24
23
  ...job.serveOnly ? [{ status: 404 }] : unhandled(job)
25
24
  ]
package/src/ui5.js CHANGED
@@ -63,7 +63,7 @@ module.exports = {
63
63
 
64
64
  const cacheBase = buildCacheBase(job)
65
65
  const match = /\/((?:test-)?resources\/.*)/
66
- const ifCacheEnabled = (request, url, match) => job.cache ? match : false
66
+ const ifCacheEnabled = (request, url, match) => job.cache
67
67
  const uncachable = {}
68
68
  const cachingInProgress = {}
69
69
 
@@ -90,8 +90,9 @@ module.exports = {
90
90
  // UI5 from cache
91
91
  match,
92
92
  'if-match': ifCacheEnabled,
93
- file: join(cacheBase, '$1'),
94
- 'ignore-if-not-found': true
93
+ cwd: cacheBase,
94
+ file: '$1',
95
+ static: !job.debugDevMode
95
96
  }, {
96
97
  // UI5 caching
97
98
  method: 'GET',
@@ -130,8 +131,12 @@ module.exports = {
130
131
  job.libs.forEach(({ relative, source }) => {
131
132
  mappings.unshift({
132
133
  match: new RegExp(`\\/resources\\/${relative.replace(/\//g, '\\/')}(.*)`),
133
- file: join(source, '$1'),
134
- 'ignore-if-not-found': true
134
+ cwd: source,
135
+ file: '$1',
136
+ static: !job.watch && !job.debugDevMode
137
+ }, {
138
+ match: new RegExp(`\\/resources\\/${relative.replace(/\//g, '\\/')}(.*)`),
139
+ status: 404
135
140
  })
136
141
  })
137
142