ui5-test-runner 6.0.0-beta.5 → 6.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 (130) hide show
  1. package/README.md +20 -29
  2. package/dist/Command.js +52 -0
  3. package/dist/Npm.js +154 -74
  4. package/dist/browsers/RecoverableBrowserError.js +6 -0
  5. package/dist/browsers/chromeArguments.js +5 -0
  6. package/dist/browsers/chromeOptions.js +29 -0
  7. package/dist/browsers/consoleMessage.js +23 -0
  8. package/dist/browsers/factory.js +168 -3
  9. package/dist/browsers/networkResponse.js +15 -0
  10. package/dist/browsers/playwright.js +104 -0
  11. package/dist/browsers/puppeteer.js +54 -88
  12. package/dist/browsers/seleniumWebdriver.js +124 -0
  13. package/dist/browsers/webdriverio.js +85 -0
  14. package/dist/cli.js +18 -12
  15. package/dist/configuration/CommandLine.js +46 -26
  16. package/dist/configuration/ConfigurationValidator.js +132 -18
  17. package/dist/configuration/OptionValidationError.js +50 -2
  18. package/dist/configuration/derivations.js +8 -0
  19. package/dist/configuration/options.js +380 -23
  20. package/dist/configuration/validations.js +135 -0
  21. package/dist/configuration/validators/boolean.js +1 -1
  22. package/dist/configuration/validators/enumeration.js +7 -0
  23. package/dist/configuration/validators/fsEntry.js +12 -12
  24. package/dist/configuration/validators/index.js +8 -2
  25. package/dist/configuration/validators/integer.js +7 -1
  26. package/dist/configuration/validators/json.js +23 -0
  27. package/dist/configuration/validators/libraryMapping.js +28 -0
  28. package/dist/configuration/validators/percent.js +2 -2
  29. package/dist/configuration/validators/regexp.js +2 -2
  30. package/dist/configuration/validators/secret.js +8 -0
  31. package/dist/configuration/validators/string.js +1 -1
  32. package/dist/configuration/validators/timeout.js +2 -2
  33. package/dist/configuration/validators/url.js +3 -3
  34. package/dist/end.js +58 -0
  35. package/dist/if.js +22 -0
  36. package/dist/modes/Modes.js +2 -1
  37. package/dist/modes/batch/BatchItem.js +1 -0
  38. package/dist/modes/batch/batchTask.js +159 -0
  39. package/dist/modes/batch/index.js +98 -0
  40. package/dist/modes/batch/output.js +22 -0
  41. package/dist/modes/batch/report.js +45 -0
  42. package/dist/modes/batch/resolve.js +105 -0
  43. package/dist/modes/dumpConfig.js +6 -0
  44. package/dist/modes/execute.js +6 -5
  45. package/dist/modes/help.js +86 -2
  46. package/dist/modes/log/LogReader.js +3 -2
  47. package/dist/modes/log/LogStorage.js +31 -27
  48. package/dist/modes/log/index.js +38 -6
  49. package/dist/modes/log/{REserve.js → reserve.js} +25 -2
  50. package/dist/modes/mcp/IDocument.js +2 -0
  51. package/dist/modes/mcp/frontmatter.js +142 -0
  52. package/dist/modes/mcp/index.js +49 -0
  53. package/dist/modes/mcp/knowledgeBase.js +104 -0
  54. package/dist/modes/mcp/remoteKnowledgeBase.js +79 -0
  55. package/dist/modes/mcp/reserve.js +175 -0
  56. package/dist/modes/mcp/tools/getTopic.js +46 -0
  57. package/dist/modes/mcp/tools/index.js +6 -0
  58. package/dist/modes/mcp/tools/listTopics.js +14 -0
  59. package/dist/modes/test/PageContext.js +1 -0
  60. package/dist/modes/test/Server.js +99 -0
  61. package/dist/modes/test/browser.js +31 -17
  62. package/dist/modes/test/browserConfig.js +17 -0
  63. package/dist/modes/test/coverage/collect.js +38 -0
  64. package/dist/modes/test/coverage/index.js +3 -0
  65. package/dist/modes/test/coverage/instrument.js +70 -0
  66. package/dist/modes/test/coverage/nyc.js +15 -0
  67. package/dist/modes/test/coverage/report.js +71 -0
  68. package/dist/modes/test/coverage/settings.js +53 -0
  69. package/dist/modes/test/index.js +94 -34
  70. package/dist/modes/test/pageTask.js +293 -98
  71. package/dist/modes/test/report.js +20 -3
  72. package/dist/modes/test/reserve.js +76 -0
  73. package/dist/modes/test/screenshot.js +49 -0
  74. package/dist/platform/Crypto.js +9 -0
  75. package/dist/platform/Exit.js +33 -24
  76. package/dist/platform/FileSystem.js +6 -2
  77. package/dist/platform/Host.js +9 -1
  78. package/dist/platform/Http.js +54 -29
  79. package/dist/platform/Module.js +2 -1
  80. package/dist/platform/Path.js +5 -1
  81. package/dist/platform/Process.js +67 -13
  82. package/dist/platform/Terminal.js +12 -8
  83. package/dist/platform/Thread.js +1 -1
  84. package/dist/platform/assert.js +13 -12
  85. package/dist/platform/environment.js +9 -18
  86. package/dist/platform/index.js +1 -0
  87. package/dist/platform/logger/CompressionContext.js +117 -0
  88. package/dist/platform/logger/allCompressed.js +8 -7
  89. package/dist/platform/logger/compress.js +29 -129
  90. package/dist/platform/logger/output/BaseLoggerOutput.js +49 -31
  91. package/dist/platform/logger/output/InteractiveLoggerOutput.js +1 -1
  92. package/dist/platform/logger/output/LlmLoggerOutput.js +29 -0
  93. package/dist/platform/logger/output/factory.js +5 -1
  94. package/dist/platform/logger/output.js +24 -6
  95. package/dist/platform/logger/toInternalLogAttributes.js +10 -11
  96. package/dist/platform/logger/traceFileName.js +1 -0
  97. package/dist/platform/logger.js +52 -21
  98. package/dist/platform/mock.js +110 -19
  99. package/dist/platform/version.js +1 -2
  100. package/dist/platform/workerBootstrap.js +1 -1
  101. package/dist/reports/initReportBuilder.js +18 -0
  102. package/dist/reports/junit.js +64 -0
  103. package/dist/reports/saveReport.js +10 -0
  104. package/dist/reports/textReport.js +92 -0
  105. package/dist/reserveLogger.js +22 -0
  106. package/dist/sendToParentProcess.js +6 -0
  107. package/dist/start.js +79 -0
  108. package/dist/types/ISecretValue.js +1 -0
  109. package/dist/types/SecretValue.js +15 -0
  110. package/dist/ui/agent.js +4 -2
  111. package/dist/ui/html-report.js +2 -2
  112. package/dist/ui/lib.js +1 -1
  113. package/dist/ui/log-viewer.js +1 -1
  114. package/dist/utils/node/Folder.js +2 -4
  115. package/dist/utils/node/FramedStreamReader.js +4 -5
  116. package/dist/utils/node/FramedStreamWriter.js +6 -5
  117. package/dist/utils/node/anonymize.js +11 -0
  118. package/dist/utils/node/timeout.js +11 -0
  119. package/dist/utils/shared/ProgressBar.js +1 -2
  120. package/dist/utils/shared/TestReportBuilder.js +11 -7
  121. package/dist/utils/shared/host.js +11 -0
  122. package/dist/utils/shared/object.js +12 -3
  123. package/dist/utils/shared/schema.js +20 -0
  124. package/dist/utils/shared/string.js +1 -0
  125. package/dist/utils/shared/toIError.js +3 -7
  126. package/dist/utils/shared/version.js +52 -0
  127. package/package.json +2 -78
  128. package/dist/configuration/validators/browser.js +0 -11
  129. package/dist/modes/test/REserve.js +0 -31
  130. package/dist/modes/test/server.js +0 -109
@@ -4,6 +4,7 @@ export const options = [
4
4
  short: 'c',
5
5
  type: 'fs-entry',
6
6
  description: 'set working directory',
7
+ defaultLabel: 'current working directory',
7
8
  default: process.cwd()
8
9
  },
9
10
  {
@@ -19,26 +20,131 @@ export const options = [
19
20
  description: 'path of the testsuite file',
20
21
  default: 'test/testsuite.qunit.html'
21
22
  },
23
+ {
24
+ name: 'agentDetectionInterval',
25
+ type: 'timeout',
26
+ browserExposed: true,
27
+ description: 'initial polling interval when detecting a test framework after page load',
28
+ default: 100
29
+ },
30
+ {
31
+ name: 'agentDetectionMaxInterval',
32
+ type: 'timeout',
33
+ browserExposed: true,
34
+ description: 'maximum polling interval when detecting a test framework after page load',
35
+ default: 1000
36
+ },
37
+ {
38
+ name: 'agentDetectionTimeout',
39
+ type: 'timeout',
40
+ browserExposed: true,
41
+ description: 'maximum time to wait for a test framework to be detected after page load',
42
+ default: 5000
43
+ },
44
+ {
45
+ name: 'agentNoTestsTimeout',
46
+ type: 'timeout',
47
+ browserExposed: true,
48
+ description: 'time to wait after QUnit.done fires with no tests before declaring the page done',
49
+ default: 5000
50
+ },
51
+ {
52
+ name: 'agentScreenshotTimeout',
53
+ type: 'timeout',
54
+ browserExposed: true,
55
+ description: 'maximum time OPA waits for a pending screenshot before continuing',
56
+ default: 10_000
57
+ },
22
58
  {
23
59
  name: 'alternateNpmPath',
24
60
  type: 'fs-entry',
25
- description: 'alternate NPM package path'
61
+ batchForwarded: true,
62
+ description: 'additional path the runner searches for npm packages after the local and global locations'
63
+ },
64
+ {
65
+ name: 'batch',
66
+ type: 'string',
67
+ multiple: true,
68
+ description: 'batch item specification (folder, config file, or regex pattern)'
69
+ },
70
+ {
71
+ name: 'batchId',
72
+ type: 'string',
73
+ description: 'identifier for the batch item'
74
+ },
75
+ {
76
+ name: 'batchLabel',
77
+ type: 'string',
78
+ description: 'display label for the batch item'
79
+ },
80
+ {
81
+ name: 'batchOutput',
82
+ type: 'boolean',
83
+ description: 'print output.txt for every batch item after execution, in alphabetical batch-id order'
84
+ },
85
+ {
86
+ name: 'batchTimeout',
87
+ short: 'bt',
88
+ type: 'timeout',
89
+ description: 'fails a batch item if it takes longer than this timeout'
26
90
  },
27
91
  {
28
92
  name: 'browser',
29
93
  short: 'b',
30
- type: 'browser',
31
- description: 'browser selection',
32
- default: 'puppeteer'
94
+ type: 'enumeration',
95
+ typeModifiers: new Set(['chrome', 'chromium', 'firefox', 'webkit', 'edge', 'safari']),
96
+ batchForwarded: true,
97
+ description: 'browser selection (per driver)'
98
+ },
99
+ {
100
+ name: 'browserBasicAuthPassword',
101
+ type: 'secret',
102
+ description: 'password for HTTP basic authentication'
103
+ },
104
+ {
105
+ name: 'browserBasicAuthUsername',
106
+ type: 'string',
107
+ description: 'username for HTTP basic authentication'
108
+ },
109
+ {
110
+ name: 'browserDevice',
111
+ type: 'string',
112
+ batchForwarded: true,
113
+ description: 'emulate a Puppeteer-known device — sets the viewport, user agent, and touch support, overriding browser viewport width and height'
114
+ },
115
+ {
116
+ name: 'browserOptions',
117
+ type: 'json',
118
+ batchForwarded: true,
119
+ description: 'browser-specific options (JSON)',
120
+ default: {}
121
+ },
122
+ {
123
+ name: 'browserViewportHeight',
124
+ short: 'H',
125
+ type: 'integer',
126
+ typeModifiers: new Set(['positive', 'non-zero']),
127
+ description: 'height of the browser viewport in pixels',
128
+ default: 1080
33
129
  },
34
130
  {
35
- name: 'capabilities',
131
+ name: 'browserViewportWidth',
132
+ short: 'W',
133
+ type: 'integer',
134
+ typeModifiers: new Set(['positive', 'non-zero']),
135
+ description: 'width of the browser viewport in pixels',
136
+ default: 1920
137
+ },
138
+ {
139
+ name: 'browserVisible',
140
+ short: 'V',
36
141
  type: 'boolean',
37
- description: 'run browser tests'
142
+ description: 'control if the browser should be visible during the tests'
38
143
  },
39
144
  {
40
145
  name: 'ci',
41
146
  type: 'boolean',
147
+ batchForwarded: true,
42
148
  description: 'forces CI mode (no interactive output)',
43
149
  default: !process.stdout.isTTY
44
150
  },
@@ -49,21 +155,137 @@ export const options = [
49
155
  description: 'read options from a configuration file',
50
156
  default: 'ui5-test-runner.json'
51
157
  },
158
+ {
159
+ name: 'coverage',
160
+ type: 'boolean',
161
+ description: 'enable code coverage'
162
+ },
163
+ {
164
+ name: 'coverageCheckBranches',
165
+ short: 'ccb',
166
+ type: 'percent',
167
+ description: 'minimum branch coverage threshold (0 = no check)',
168
+ default: 0
169
+ },
170
+ {
171
+ name: 'coverageCheckFunctions',
172
+ short: 'ccf',
173
+ type: 'percent',
174
+ description: 'minimum function coverage threshold (0 = no check)',
175
+ default: 0
176
+ },
177
+ {
178
+ name: 'coverageCheckLines',
179
+ short: 'ccl',
180
+ type: 'percent',
181
+ description: 'minimum line coverage threshold (0 = no check)',
182
+ default: 0
183
+ },
184
+ {
185
+ name: 'coverageCheckStatements',
186
+ short: 'ccs',
187
+ type: 'percent',
188
+ description: 'minimum statement coverage threshold (0 = no check)',
189
+ default: 0
190
+ },
191
+ {
192
+ name: 'coverageRemoteInstrument',
193
+ type: 'regexp',
194
+ multiple: true,
195
+ browserExposed: true,
196
+ description: 'resources sub-paths to instrument for coverage in remote mode'
197
+ },
198
+ {
199
+ name: 'coverageReportDir',
200
+ short: 'crd',
201
+ type: 'fs-entry',
202
+ typeModifiers: new Set(['overwrite']),
203
+ description: 'directory for the final coverage report',
204
+ default: 'coverage'
205
+ },
206
+ {
207
+ name: 'coverageReporters',
208
+ short: 'cr',
209
+ type: 'string',
210
+ multiple: true,
211
+ description: 'istanbul-lib-report reporters to use',
212
+ default: ['lcov', 'cobertura']
213
+ },
214
+ {
215
+ name: 'coverageSettings',
216
+ short: 'cs',
217
+ type: 'fs-entry',
218
+ typeModifiers: new Set(['file', 'safe-default']),
219
+ description: 'path to the Istanbul configuration file (.nycrc.json)',
220
+ default: '.nycrc.json'
221
+ },
222
+ {
223
+ name: 'coverageSourceDir',
224
+ type: 'fs-entry',
225
+ typeModifiers: new Set(['safe-default']),
226
+ description: 'directory containing the source files used for coverage reporting'
227
+ },
228
+ {
229
+ name: 'coverageTempDir',
230
+ short: 'ctd',
231
+ type: 'fs-entry',
232
+ typeModifiers: new Set(['overwrite']),
233
+ description: 'temporary directory for coverage data',
234
+ default: '.nyc_output'
235
+ },
52
236
  {
53
237
  name: 'debugKeepBrowserOpen',
54
238
  type: 'boolean',
55
239
  description: 'keeps the browser open after the tests completed'
56
240
  },
241
+ {
242
+ name: 'debugLog',
243
+ type: 'string',
244
+ multiple: true,
245
+ description: 'output debug logs for given sources'
246
+ },
247
+ {
248
+ name: 'debugMcpLocalDocs',
249
+ type: 'boolean',
250
+ description: 'use local docs/ directory instead of fetching from GitHub (development only)'
251
+ },
252
+ {
253
+ name: 'driver',
254
+ short: 'd',
255
+ type: 'enumeration',
256
+ typeModifiers: new Set(['puppeteer', 'playwright', 'webdriverio', 'selenium-webdriver']),
257
+ browserExposed: true,
258
+ batchForwarded: true,
259
+ description: 'driver selection',
260
+ default: 'puppeteer'
261
+ },
262
+ {
263
+ name: 'dumpConfig',
264
+ type: 'boolean',
265
+ description: 'dump the resolved configuration as JSON and exit'
266
+ },
267
+ {
268
+ name: 'end',
269
+ type: 'string',
270
+ description: 'command to be executed after the tests'
271
+ },
272
+ {
273
+ name: 'endTimeout',
274
+ type: 'timeout',
275
+ description: 'maximum waiting time for the end command to execute'
276
+ },
57
277
  {
58
278
  name: 'failFast',
59
279
  short: 'f',
60
280
  type: 'boolean',
281
+ batchForwarded: true,
61
282
  description: 'stop the whole execution after the first failing page'
62
283
  },
63
284
  {
64
285
  name: 'failOpaFast',
65
286
  short: 'fo',
66
287
  type: 'boolean',
288
+ batchForwarded: true,
67
289
  description: 'stop the OPA page execution after the first failing test'
68
290
  },
69
291
  {
@@ -75,43 +297,84 @@ export const options = [
75
297
  {
76
298
  name: 'help',
77
299
  type: 'boolean',
78
- description: 'display help'
300
+ description: 'display the list of available options and exit'
79
301
  },
80
302
  {
81
- name: 'keepAlive',
82
- short: 'k',
83
- type: 'boolean',
84
- description: 'keep the server alive'
303
+ name: 'if',
304
+ type: 'string',
305
+ description: 'skip execution if the expression evaluates to falsy'
85
306
  },
86
307
  {
87
- name: 'localhost',
308
+ name: 'junitXmlFilename',
88
309
  type: 'string',
89
- description: 'hostname for legacy URL',
90
- default: 'localhost'
310
+ description: 'filename for the JUnit XML report saved in the report directory',
311
+ default: 'junit.xml'
312
+ },
313
+ {
314
+ name: 'lib',
315
+ type: 'library-mapping',
316
+ multiple: true,
317
+ description: 'map a UI5 resources sub-folder to a local source folder, used in legacy mode to serve custom libraries'
318
+ },
319
+ {
320
+ name: 'llm',
321
+ type: 'boolean',
322
+ batchForwarded: true,
323
+ description: 'forces LLM-friendly output (compact, no color, severity-tagged)'
91
324
  },
92
325
  {
93
326
  name: 'log',
94
327
  type: 'fs-entry',
95
328
  typeModifiers: new Set(['file']),
96
- description: 'read and dump log file using jsonl format'
329
+ description: 'path to a .logz trace file produced by a previous run; activates log viewer mode'
330
+ },
331
+ {
332
+ name: 'logDump',
333
+ type: 'boolean',
334
+ description: 'dump all traces to stdout instead of opening a browser (requires --log)'
335
+ },
336
+ {
337
+ name: 'logFilter',
338
+ short: 'lf',
339
+ type: 'string',
340
+ description: 'JavaScript expression (using punyexpr) to filter logs for dumping with --log-dump'
341
+ },
342
+ {
343
+ name: 'mcp',
344
+ type: 'boolean',
345
+ description: 'start a documentation-only MCP server that lets an MCP client answer questions about ui5-test-runner'
97
346
  },
98
347
  {
99
- name: 'logServer',
100
- short: 'l',
348
+ name: 'noBanner',
101
349
  type: 'boolean',
102
- description: 'log inner server traces'
350
+ description: 'do not display the startup banner (logo) and environment information'
103
351
  },
104
352
  {
105
353
  name: 'noNpmInstall',
106
354
  type: 'boolean',
355
+ batchForwarded: true,
107
356
  description: 'prevent any NPM install'
108
357
  },
358
+ {
359
+ name: 'npmAllowInstallScripts',
360
+ type: 'boolean',
361
+ batchForwarded: true,
362
+ description: 'allow postinstall scripts when installing missing packages'
363
+ },
109
364
  {
110
365
  name: 'npmInstall',
111
366
  type: 'string',
112
367
  description: 'npm install strategy for missing packages',
113
368
  default: 'global'
114
369
  },
370
+ {
371
+ name: 'npmInstallMinReleaseAge',
372
+ type: 'integer',
373
+ typeModifiers: new Set(['positive']),
374
+ batchForwarded: true,
375
+ description: 'minimum release age (in days) required before installing a package',
376
+ default: 3
377
+ },
115
378
  {
116
379
  name: 'npmInstallPrefix',
117
380
  type: 'fs-entry',
@@ -121,6 +384,7 @@ export const options = [
121
384
  name: 'outputInterval',
122
385
  short: 'oi',
123
386
  type: 'timeout',
387
+ batchForwarded: true,
124
388
  description: 'interval for reporting progress on non interactive output (CI/CD)',
125
389
  default: 30_000
126
390
  },
@@ -128,31 +392,38 @@ export const options = [
128
392
  name: 'pageFilter',
129
393
  short: 'pf',
130
394
  type: 'regexp',
395
+ batchForwarded: true,
131
396
  description: 'filter pages to execute'
132
397
  },
133
398
  {
134
399
  name: 'pageParams',
135
400
  short: 'pp',
136
401
  type: 'string',
402
+ batchForwarded: true,
137
403
  description: 'add parameters to page URL'
138
404
  },
139
405
  {
140
406
  name: 'pageTimeout',
141
407
  short: 'pt',
142
408
  type: 'timeout',
409
+ batchForwarded: true,
143
410
  description: 'fails a page if it takes longer than this timeout'
144
411
  },
145
412
  {
146
413
  name: 'parallel',
147
414
  short: 'p',
148
415
  type: 'integer',
416
+ typeModifiers: new Set(['positive', 'non-zero']),
417
+ browserExposed: true,
418
+ batchForwarded: true,
149
419
  description: 'number of parallel executions',
150
420
  default: 2
151
421
  },
152
422
  {
153
423
  name: 'port',
154
424
  type: 'integer',
155
- description: 'port to use'
425
+ typeModifiers: new Set(['positive']),
426
+ description: 'Port the local web server listens on; 0 allocates a free port automatically'
156
427
  },
157
428
  {
158
429
  name: 'reportDir',
@@ -162,16 +433,74 @@ export const options = [
162
433
  description: 'directory to output test reports',
163
434
  default: 'report'
164
435
  },
436
+ {
437
+ name: 'screenshot',
438
+ type: 'boolean',
439
+ browserExposed: true,
440
+ batchForwarded: true,
441
+ description: 'take a screenshot after every OPA assertion'
442
+ },
443
+ {
444
+ name: 'screenshotOnFailure',
445
+ type: 'boolean',
446
+ batchForwarded: true,
447
+ description: 'take a screenshot when a test fails',
448
+ default: true
449
+ },
450
+ {
451
+ name: 'screenshotTimeout',
452
+ type: 'timeout',
453
+ batchForwarded: true,
454
+ description: 'maximum time allowed to take a screenshot',
455
+ default: 5000
456
+ },
457
+ {
458
+ name: 'seleniumApiTimeout',
459
+ type: 'timeout',
460
+ description: 'fails a page if a selenium webdriver api call does not respond within this timeout, indicating a dead session',
461
+ default: 10_000
462
+ },
165
463
  {
166
464
  name: 'serveOnly',
167
465
  short: 's',
168
466
  type: 'boolean',
169
- description: 'serve only'
467
+ description: 'start the legacy web server and serve the application without running any tests'
468
+ },
469
+ {
470
+ name: 'splitOpa',
471
+ short: 'so',
472
+ type: 'boolean',
473
+ browserExposed: true,
474
+ batchForwarded: true,
475
+ description: 'split OPA tests by QUnit module, creating one test page per module'
476
+ },
477
+ {
478
+ name: 'start',
479
+ type: 'string',
480
+ description: 'command to be executed before the tests'
481
+ },
482
+ {
483
+ name: 'startTimeout',
484
+ type: 'timeout',
485
+ description: 'maximum waiting time for the start command to become ready',
486
+ default: 30_000
487
+ },
488
+ {
489
+ name: 'startWaitMethod',
490
+ type: 'string',
491
+ description: 'HTTP method used when polling the startWaitUrl',
492
+ default: 'GET'
493
+ },
494
+ {
495
+ name: 'startWaitUrl',
496
+ type: 'url',
497
+ description: 'URL to poll after the start command is executed'
170
498
  },
171
499
  {
172
500
  name: 'ui5',
173
501
  type: 'url',
174
- description: 'UI5 url',
502
+ batchForwarded: true,
503
+ description: 'URL of the UI5 CDN used in legacy mode to serve the UI5 framework resources',
175
504
  default: 'https://ui5.sap.com'
176
505
  },
177
506
  {
@@ -185,19 +514,47 @@ export const options = [
185
514
  name: 'version',
186
515
  type: 'boolean',
187
516
  description: 'display version'
517
+ },
518
+ {
519
+ name: 'watch',
520
+ type: 'boolean',
521
+ description: 'watch for file changes and automatically reload'
188
522
  }
189
523
  ];
190
524
  export const defaults = {
191
- browser: 'puppeteer',
525
+ agentDetectionInterval: 100,
526
+ agentDetectionMaxInterval: 1000,
527
+ agentDetectionTimeout: 5000,
528
+ agentNoTestsTimeout: 5000,
529
+ agentScreenshotTimeout: 10_000,
530
+ browserOptions: {},
531
+ browserViewportHeight: 1080,
532
+ browserViewportWidth: 1920,
192
533
  ci: !process.stdout.isTTY,
193
534
  config: 'ui5-test-runner.json',
535
+ coverageCheckBranches: 0,
536
+ coverageCheckFunctions: 0,
537
+ coverageCheckLines: 0,
538
+ coverageCheckStatements: 0,
539
+ coverageReportDir: 'coverage',
540
+ coverageReporters: ['lcov', 'cobertura'],
541
+ coverageSettings: '.nycrc.json',
542
+ coverageTempDir: '.nyc_output',
194
543
  cwd: process.cwd(),
195
- localhost: 'localhost',
544
+ driver: 'puppeteer',
545
+ junitXmlFilename: 'junit.xml',
196
546
  npmInstall: 'global',
547
+ npmInstallMinReleaseAge: 3,
197
548
  outputInterval: 30_000,
198
549
  parallel: 2,
199
550
  reportDir: 'report',
551
+ screenshotOnFailure: true,
552
+ screenshotTimeout: 5000,
553
+ seleniumApiTimeout: 10_000,
554
+ startTimeout: 30_000,
555
+ startWaitMethod: 'GET',
200
556
  testsuite: 'test/testsuite.qunit.html',
201
557
  ui5: 'https://ui5.sap.com',
202
558
  webapp: 'webapp'
203
559
  };
560
+ export const sensitiveKeys = new Set(['browserBasicAuthPassword']);
@@ -0,0 +1,135 @@
1
+ import { punyexpr } from 'punyexpr';
2
+ import { indexedOptions } from './indexedOptions.js';
3
+ import { OptionValidationError } from './OptionValidationError.js';
4
+ export const validations = [
5
+ (configuration) => {
6
+ if (Object.hasOwn(configuration, 'browser') &&
7
+ !punyexpr("(driver !== 'puppeteer' || browser === 'chrome' || browser === 'firefox')")(configuration)) {
8
+ throw OptionValidationError.createValidationError(indexedOptions.browser, 'browser is not supported by puppeteer (chrome, firefox)');
9
+ }
10
+ },
11
+ (configuration) => {
12
+ if (Object.hasOwn(configuration, 'browser') &&
13
+ !punyexpr("(driver !== 'playwright' || browser === 'chromium' || browser === 'firefox' || browser === 'webkit')")(configuration)) {
14
+ throw OptionValidationError.createValidationError(indexedOptions.browser, 'browser is not supported by playwright (chromium, firefox, webkit)');
15
+ }
16
+ },
17
+ (configuration) => {
18
+ if (Object.hasOwn(configuration, 'browser') &&
19
+ !punyexpr("(driver !== 'webdriverio' || browser === 'chrome' || browser === 'firefox' || browser === 'edge' || browser === 'safari')")(configuration)) {
20
+ throw OptionValidationError.createValidationError(indexedOptions.browser, 'browser is not supported by webdriverio (chrome, firefox, edge, safari)');
21
+ }
22
+ },
23
+ (configuration) => {
24
+ if (Object.hasOwn(configuration, 'browser') &&
25
+ !punyexpr("(driver !== 'selenium-webdriver' || browser === 'chrome' || browser === 'firefox' || browser === 'edge' || browser === 'safari')")(configuration)) {
26
+ throw OptionValidationError.createValidationError(indexedOptions.browser, 'browser is not supported by selenium-webdriver (chrome, firefox, edge, safari)');
27
+ }
28
+ },
29
+ (configuration) => {
30
+ if (Object.hasOwn(configuration, 'browserBasicAuthPassword') &&
31
+ !punyexpr("(driver === 'puppeteer' || driver === 'playwright')")(configuration)) {
32
+ throw OptionValidationError.createValidationError(indexedOptions.browserBasicAuthPassword, 'browserBasicAuthPassword is only supported with puppeteer and playwright drivers');
33
+ }
34
+ },
35
+ (configuration) => {
36
+ if (Object.hasOwn(configuration, 'browserBasicAuthPassword') &&
37
+ !punyexpr('(browserBasicAuthUsername !== undefined)')(configuration)) {
38
+ throw OptionValidationError.createValidationError(indexedOptions.browserBasicAuthPassword, 'browserBasicAuthPassword requires browserBasicAuthUsername');
39
+ }
40
+ },
41
+ (configuration) => {
42
+ if (Object.hasOwn(configuration, 'browserBasicAuthUsername') &&
43
+ !punyexpr("(driver === 'puppeteer' || driver === 'playwright')")(configuration)) {
44
+ throw OptionValidationError.createValidationError(indexedOptions.browserBasicAuthUsername, 'browserBasicAuthUsername is only supported with puppeteer and playwright drivers');
45
+ }
46
+ },
47
+ (configuration) => {
48
+ if (Object.hasOwn(configuration, 'browserBasicAuthUsername') &&
49
+ !punyexpr('(browserBasicAuthPassword !== undefined)')(configuration)) {
50
+ throw OptionValidationError.createValidationError(indexedOptions.browserBasicAuthUsername, 'browserBasicAuthUsername requires browserBasicAuthPassword');
51
+ }
52
+ },
53
+ (configuration) => {
54
+ if (Object.hasOwn(configuration, 'browserDevice') && !punyexpr("(driver === 'puppeteer')")(configuration)) {
55
+ throw OptionValidationError.createValidationError(indexedOptions.browserDevice, 'browserDevice is only supported with the puppeteer driver');
56
+ }
57
+ },
58
+ (configuration) => {
59
+ if (Object.hasOwn(configuration, 'browserDevice') &&
60
+ !punyexpr("(browser === undefined || browser === 'chrome')")(configuration)) {
61
+ throw OptionValidationError.createValidationError(indexedOptions.browserDevice, 'browserDevice requires the chrome browser');
62
+ }
63
+ },
64
+ (configuration) => {
65
+ if (Object.hasOwn(configuration, 'coverage') &&
66
+ !punyexpr("(!coverage || webapp !== '' || coverageSourceDir !== '')")(configuration)) {
67
+ throw OptionValidationError.createValidationError(indexedOptions.coverage, 'at least one of webapp or coverageSourceDir must be set');
68
+ }
69
+ },
70
+ (configuration) => {
71
+ if (Object.hasOwn(configuration, 'debugMcpLocalDocs') &&
72
+ !punyexpr("(!debugMcpLocalDocs || mode === 'mcp')")(configuration)) {
73
+ throw OptionValidationError.createValidationError(indexedOptions.debugMcpLocalDocs, 'this option requires --mcp');
74
+ }
75
+ },
76
+ (configuration) => {
77
+ if (Object.hasOwn(configuration, 'dumpConfig') && !punyexpr("(mode === 'dumpConfig')")(configuration)) {
78
+ throw OptionValidationError.createValidationError(indexedOptions.dumpConfig, 'this option cannot be combined with other mode options');
79
+ }
80
+ },
81
+ (configuration) => {
82
+ if (Object.hasOwn(configuration, 'help') && !punyexpr("(mode === 'help')")(configuration)) {
83
+ throw OptionValidationError.createValidationError(indexedOptions.help, 'this option cannot be combined with other mode options');
84
+ }
85
+ },
86
+ (configuration) => {
87
+ if (Object.hasOwn(configuration, 'log') && !punyexpr("(mode === 'log')")(configuration)) {
88
+ throw OptionValidationError.createValidationError(indexedOptions.log, 'this option cannot be combined with other mode options');
89
+ }
90
+ },
91
+ (configuration) => {
92
+ if (Object.hasOwn(configuration, 'logDump') && !punyexpr('(log !== undefined)')(configuration)) {
93
+ throw OptionValidationError.createValidationError(indexedOptions.logDump, 'requires log');
94
+ }
95
+ },
96
+ (configuration) => {
97
+ if (Object.hasOwn(configuration, 'logFilter') &&
98
+ !punyexpr('(log !== undefined) && (logDump !== undefined)')(configuration)) {
99
+ throw OptionValidationError.createValidationError(indexedOptions.logFilter, 'requires log and logDump');
100
+ }
101
+ },
102
+ (configuration) => {
103
+ if (Object.hasOwn(configuration, 'mcp') && !punyexpr("(mode === 'mcp')")(configuration)) {
104
+ throw OptionValidationError.createValidationError(indexedOptions.mcp, 'this option cannot be combined with other mode options');
105
+ }
106
+ },
107
+ (configuration) => {
108
+ if (Object.hasOwn(configuration, 'startTimeout') &&
109
+ !punyexpr('(start !== undefined) && (startWaitUrl !== undefined)')(configuration)) {
110
+ throw OptionValidationError.createValidationError(indexedOptions.startTimeout, 'requires start and startWaitUrl');
111
+ }
112
+ },
113
+ (configuration) => {
114
+ if (Object.hasOwn(configuration, 'startWaitMethod') &&
115
+ !punyexpr('(start !== undefined) && (startWaitUrl !== undefined)')(configuration)) {
116
+ throw OptionValidationError.createValidationError(indexedOptions.startWaitMethod, 'requires start and startWaitUrl');
117
+ }
118
+ },
119
+ (configuration) => {
120
+ if (Object.hasOwn(configuration, 'startWaitUrl') && !punyexpr('(start !== undefined)')(configuration)) {
121
+ throw OptionValidationError.createValidationError(indexedOptions.startWaitUrl, 'requires start');
122
+ }
123
+ },
124
+ (configuration) => {
125
+ if (Object.hasOwn(configuration, 'version') && !punyexpr("(mode === 'version')")(configuration)) {
126
+ throw OptionValidationError.createValidationError(indexedOptions.version, 'this option cannot be combined with other mode options');
127
+ }
128
+ },
129
+ (configuration) => {
130
+ if (Object.hasOwn(configuration, 'watch') &&
131
+ !punyexpr("(!watch || (mode === 'mcp' && debugMcpLocalDocs))")(configuration)) {
132
+ throw OptionValidationError.createValidationError(indexedOptions.watch, 'this option is valid only for --mcp with --debug-mcp-local-docs');
133
+ }
134
+ }
135
+ ];