ui5-test-runner 5.13.1 → 6.0.0-beta.2

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 (186) hide show
  1. package/README.md +3 -2
  2. package/dist/Npm.js +80 -0
  3. package/dist/browsers/IBrowser.js +1 -0
  4. package/dist/browsers/factory.js +9 -0
  5. package/dist/browsers/puppeteer.js +158 -0
  6. package/dist/cli.js +20 -0
  7. package/dist/configuration/CommandLine.js +112 -0
  8. package/dist/configuration/Configuration.js +1 -0
  9. package/dist/configuration/ConfigurationValidator.js +79 -0
  10. package/dist/configuration/Option.js +1 -0
  11. package/dist/configuration/OptionValidationError.js +15 -0
  12. package/dist/configuration/indexedOptions.js +13 -0
  13. package/dist/configuration/options.js +191 -0
  14. package/dist/configuration/validators/OptionValidator.js +1 -0
  15. package/dist/configuration/validators/boolean.js +15 -0
  16. package/dist/configuration/validators/browser.js +11 -0
  17. package/dist/configuration/validators/fsEntry.js +70 -0
  18. package/dist/configuration/validators/index.js +20 -0
  19. package/dist/configuration/validators/integer.js +10 -0
  20. package/dist/configuration/validators/percent.js +17 -0
  21. package/dist/configuration/validators/regexp.js +20 -0
  22. package/dist/configuration/validators/string.js +7 -0
  23. package/dist/configuration/validators/timeout.js +24 -0
  24. package/dist/configuration/validators/url.js +8 -0
  25. package/dist/modes/ModeFunction.js +1 -0
  26. package/dist/modes/Modes.js +9 -0
  27. package/dist/modes/execute.js +27 -0
  28. package/dist/modes/help.js +3 -0
  29. package/dist/modes/log/ILogStorage.js +1 -0
  30. package/dist/modes/log/LogMetrics.js +9 -0
  31. package/dist/modes/log/LogReader.js +37 -0
  32. package/dist/modes/log/LogStorage.js +68 -0
  33. package/dist/modes/log/REserve.js +101 -0
  34. package/dist/modes/log/index.js +58 -0
  35. package/dist/modes/test/REserve.js +31 -0
  36. package/dist/modes/test/agent.js +8 -0
  37. package/dist/modes/test/browser.js +37 -0
  38. package/dist/modes/test/index.js +66 -0
  39. package/dist/modes/test/pageTask.js +145 -0
  40. package/dist/modes/test/report.js +3 -0
  41. package/dist/modes/test/server.js +109 -0
  42. package/dist/modes/version.js +11 -0
  43. package/dist/platform/Exit.js +139 -0
  44. package/dist/platform/FileSystem.js +13 -0
  45. package/dist/platform/Host.js +10 -0
  46. package/dist/platform/Http.js +38 -0
  47. package/dist/platform/Path.js +5 -0
  48. package/dist/platform/Process.js +133 -0
  49. package/dist/platform/Terminal.js +47 -0
  50. package/dist/platform/Thread.js +43 -0
  51. package/dist/platform/ZLib.js +7 -0
  52. package/dist/platform/assert.js +17 -0
  53. package/dist/platform/constants.js +5 -0
  54. package/dist/platform/environment.js +28 -0
  55. package/dist/platform/index.js +13 -0
  56. package/dist/platform/logger/ILogger.js +1 -0
  57. package/dist/platform/logger/allCompressed.js +54 -0
  58. package/dist/platform/logger/compress.js +277 -0
  59. package/dist/platform/logger/output/BaseLoggerOutput.js +158 -0
  60. package/dist/platform/logger/output/InteractiveLoggerOutput.js +102 -0
  61. package/dist/platform/logger/output/StaticLoggerOutput.js +32 -0
  62. package/dist/platform/logger/output/factory.js +10 -0
  63. package/dist/platform/logger/output.js +58 -0
  64. package/dist/platform/logger/proxy.js +6 -0
  65. package/dist/platform/logger/toInternalLogAttributes.js +22 -0
  66. package/dist/platform/logger/types.js +7 -0
  67. package/dist/platform/logger.js +138 -0
  68. package/dist/platform/mock.js +104 -0
  69. package/dist/platform/version.js +8 -0
  70. package/dist/platform/workerBootstrap.js +21 -0
  71. package/dist/reports/html.js +46 -0
  72. package/dist/types/AgentState.js +1 -0
  73. package/dist/types/CommonTestReportFormat.js +50 -0
  74. package/dist/types/IError.js +1 -0
  75. package/dist/types/IUserInterfaceController.js +1 -0
  76. package/dist/types/typeUtilities.js +1 -0
  77. package/dist/ui/agent.js +3 -0
  78. package/dist/ui/html-report.js +2 -0
  79. package/dist/ui/lib.js +1 -0
  80. package/dist/ui/log-viewer.js +2 -0
  81. package/dist/utils/node/Folder.js +28 -0
  82. package/dist/utils/node/FramedStreamReader.js +86 -0
  83. package/dist/utils/node/FramedStreamWriter.js +27 -0
  84. package/dist/utils/shared/ProgressBar.js +43 -0
  85. package/dist/utils/shared/TestReportBuilder.js +48 -0
  86. package/dist/utils/shared/memoize.js +19 -0
  87. package/dist/utils/shared/object.js +8 -0
  88. package/dist/utils/shared/parallelize.js +59 -0
  89. package/dist/utils/shared/string.js +23 -0
  90. package/dist/utils/shared/toIError.js +17 -0
  91. package/package.json +73 -50
  92. package/.releaserc +0 -5
  93. package/index.js +0 -175
  94. package/jest.config.json +0 -31
  95. package/src/add-test-pages.js +0 -67
  96. package/src/batch.js +0 -214
  97. package/src/browsers.js +0 -319
  98. package/src/capabilities/index.js +0 -204
  99. package/src/capabilities/tests/basic/iframe.html +0 -8
  100. package/src/capabilities/tests/basic/index.html +0 -12
  101. package/src/capabilities/tests/basic/index.js +0 -20
  102. package/src/capabilities/tests/basic/ui5.html +0 -24
  103. package/src/capabilities/tests/dynamic-include/index.js +0 -21
  104. package/src/capabilities/tests/dynamic-include/mix.html +0 -11
  105. package/src/capabilities/tests/dynamic-include/one.html +0 -11
  106. package/src/capabilities/tests/dynamic-include/post.js +0 -3
  107. package/src/capabilities/tests/dynamic-include/test.js +0 -1
  108. package/src/capabilities/tests/dynamic-include/two.html +0 -11
  109. package/src/capabilities/tests/index.js +0 -16
  110. package/src/capabilities/tests/local-storage/index.html +0 -16
  111. package/src/capabilities/tests/local-storage/index.js +0 -21
  112. package/src/capabilities/tests/screenshot/index.html +0 -23
  113. package/src/capabilities/tests/screenshot/index.js +0 -24
  114. package/src/capabilities/tests/scripts/coverage.html +0 -32
  115. package/src/capabilities/tests/scripts/iframe.html +0 -18
  116. package/src/capabilities/tests/scripts/index.js +0 -59
  117. package/src/capabilities/tests/scripts/qunit.html +0 -22
  118. package/src/capabilities/tests/scripts/testsuite.html +0 -10
  119. package/src/capabilities/tests/scripts/testsuite.js +0 -8
  120. package/src/capabilities/tests/timeout/index.html +0 -21
  121. package/src/capabilities/tests/timeout/index.js +0 -19
  122. package/src/capabilities/tests/traces/index.html +0 -18
  123. package/src/capabilities/tests/traces/index.js +0 -81
  124. package/src/capabilities/tests/ui5/focus.html +0 -89
  125. package/src/capabilities/tests/ui5/index.js +0 -39
  126. package/src/capabilities/tests/ui5/language.html +0 -50
  127. package/src/capabilities/tests/ui5/timezone.html +0 -27
  128. package/src/clean.js +0 -22
  129. package/src/cors.js +0 -21
  130. package/src/coverage.js +0 -384
  131. package/src/csv-reader.js +0 -36
  132. package/src/csv-writer.js +0 -55
  133. package/src/defaults/.nycrc.json +0 -4
  134. package/src/defaults/browser.js +0 -217
  135. package/src/defaults/happy-dom.js +0 -123
  136. package/src/defaults/jsdom/compatibility.js +0 -163
  137. package/src/defaults/jsdom/debug.js +0 -23
  138. package/src/defaults/jsdom/resource-loader.js +0 -44
  139. package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +0 -39
  140. package/src/defaults/jsdom.js +0 -95
  141. package/src/defaults/json-report.js +0 -36
  142. package/src/defaults/junit-xml-report.js +0 -90
  143. package/src/defaults/playwright.js +0 -142
  144. package/src/defaults/puppeteer.js +0 -124
  145. package/src/defaults/report/common.js +0 -38
  146. package/src/defaults/report/decompress.js +0 -19
  147. package/src/defaults/report/default.html +0 -99
  148. package/src/defaults/report/main.js +0 -69
  149. package/src/defaults/report/progress.js +0 -60
  150. package/src/defaults/report/styles.css +0 -66
  151. package/src/defaults/report.js +0 -91
  152. package/src/defaults/scan-ui5.js +0 -26
  153. package/src/defaults/selenium-webdriver/chrome.js +0 -39
  154. package/src/defaults/selenium-webdriver/edge.js +0 -24
  155. package/src/defaults/selenium-webdriver/firefox.js +0 -30
  156. package/src/defaults/selenium-webdriver.js +0 -129
  157. package/src/defaults/text-report.js +0 -108
  158. package/src/defaults/webdriverio.js +0 -80
  159. package/src/end.js +0 -62
  160. package/src/endpoints.js +0 -219
  161. package/src/error.js +0 -54
  162. package/src/get-job-progress.js +0 -78
  163. package/src/handle.js +0 -43
  164. package/src/if.js +0 -10
  165. package/src/inject/jest2qunit.js +0 -289
  166. package/src/inject/opa-iframe-coverage.js +0 -22
  167. package/src/inject/post.js +0 -141
  168. package/src/inject/qunit-hooks.js +0 -107
  169. package/src/inject/qunit-redirect.js +0 -65
  170. package/src/inject/ui5-coverage.js +0 -33
  171. package/src/job-mode.js +0 -65
  172. package/src/job.js +0 -493
  173. package/src/npm.js +0 -136
  174. package/src/options.js +0 -95
  175. package/src/output.js +0 -739
  176. package/src/parallelize.js +0 -63
  177. package/src/qunit-hooks.js +0 -219
  178. package/src/report.js +0 -89
  179. package/src/reserve.js +0 -25
  180. package/src/start.js +0 -133
  181. package/src/symbols.js +0 -8
  182. package/src/tests.js +0 -183
  183. package/src/timeout.js +0 -53
  184. package/src/tools.js +0 -179
  185. package/src/ui5.js +0 -199
  186. package/src/unhandled.js +0 -32
@@ -0,0 +1,191 @@
1
+ export const options = [
2
+ {
3
+ name: 'cwd',
4
+ short: 'c',
5
+ type: 'fs-entry',
6
+ description: 'set working directory',
7
+ default: process.cwd()
8
+ },
9
+ {
10
+ name: 'webapp',
11
+ type: 'fs-entry',
12
+ typeModifiers: new Set(['safe-default']),
13
+ description: 'base folder of the UI5 application',
14
+ default: 'webapp'
15
+ },
16
+ {
17
+ name: 'testsuite',
18
+ type: 'string',
19
+ description: 'path of the testsuite file',
20
+ default: 'test/testsuite.qunit.html'
21
+ },
22
+ {
23
+ name: 'alternateNpmPath',
24
+ type: 'fs-entry',
25
+ description: 'alternate NPM package path'
26
+ },
27
+ {
28
+ name: 'browser',
29
+ short: 'b',
30
+ type: 'browser',
31
+ description: 'browser selection',
32
+ default: 'puppeteer'
33
+ },
34
+ {
35
+ name: 'capabilities',
36
+ type: 'boolean',
37
+ description: 'run browser tests'
38
+ },
39
+ {
40
+ name: 'ci',
41
+ type: 'boolean',
42
+ description: 'forces CI mode (no interactive output)',
43
+ default: !process.stdout.isTTY
44
+ },
45
+ {
46
+ name: 'config',
47
+ type: 'fs-entry',
48
+ typeModifiers: new Set(['file', 'safe-default']),
49
+ description: 'read options from a configuration file',
50
+ default: 'ui5-test-runner.json'
51
+ },
52
+ {
53
+ name: 'debugKeepBrowserOpen',
54
+ type: 'boolean',
55
+ description: 'keeps the browser open after the tests completed'
56
+ },
57
+ {
58
+ name: 'failFast',
59
+ short: 'f',
60
+ type: 'boolean',
61
+ description: 'stop the whole execution after the first failing page'
62
+ },
63
+ {
64
+ name: 'failOpaFast',
65
+ short: 'fo',
66
+ type: 'boolean',
67
+ description: 'stop the OPA page execution after the first failing test'
68
+ },
69
+ {
70
+ name: 'globalTimeout',
71
+ short: 't',
72
+ type: 'timeout',
73
+ description: 'limit the tests execution time, fail remaining pages if it takes longer than the timeout'
74
+ },
75
+ {
76
+ name: 'help',
77
+ type: 'boolean',
78
+ description: 'display help'
79
+ },
80
+ {
81
+ name: 'keepAlive',
82
+ short: 'k',
83
+ type: 'boolean',
84
+ description: 'keep the server alive'
85
+ },
86
+ {
87
+ name: 'localhost',
88
+ type: 'string',
89
+ description: 'hostname for legacy URL',
90
+ default: 'localhost'
91
+ },
92
+ {
93
+ name: 'log',
94
+ type: 'fs-entry',
95
+ typeModifiers: new Set(['file']),
96
+ description: 'read and dump log file using jsonl format'
97
+ },
98
+ {
99
+ name: 'logServer',
100
+ short: 'l',
101
+ type: 'boolean',
102
+ description: 'log inner server traces'
103
+ },
104
+ {
105
+ name: 'noNpmInstall',
106
+ type: 'boolean',
107
+ description: 'prevent any NPM install'
108
+ },
109
+ {
110
+ name: 'outputInterval',
111
+ short: 'oi',
112
+ type: 'timeout',
113
+ description: 'interval for reporting progress on non interactive output (CI/CD)',
114
+ default: 30_000
115
+ },
116
+ {
117
+ name: 'pageFilter',
118
+ short: 'pf',
119
+ type: 'regexp',
120
+ description: 'filter pages to execute'
121
+ },
122
+ {
123
+ name: 'pageParams',
124
+ short: 'pp',
125
+ type: 'string',
126
+ description: 'add parameters to page URL'
127
+ },
128
+ {
129
+ name: 'pageTimeout',
130
+ short: 'pt',
131
+ type: 'timeout',
132
+ description: 'fails a page if it takes longer than this timeout'
133
+ },
134
+ {
135
+ name: 'parallel',
136
+ short: 'p',
137
+ type: 'integer',
138
+ description: 'number of parallel executions',
139
+ default: 2
140
+ },
141
+ {
142
+ name: 'port',
143
+ type: 'integer',
144
+ description: 'port to use'
145
+ },
146
+ {
147
+ name: 'reportDir',
148
+ short: 'r',
149
+ type: 'fs-entry',
150
+ typeModifiers: new Set(['overwrite']),
151
+ description: 'directory to output test reports',
152
+ default: 'report'
153
+ },
154
+ {
155
+ name: 'serveOnly',
156
+ short: 's',
157
+ type: 'boolean',
158
+ description: 'serve only'
159
+ },
160
+ {
161
+ name: 'ui5',
162
+ type: 'url',
163
+ description: 'UI5 url',
164
+ default: 'https://ui5.sap.com'
165
+ },
166
+ {
167
+ name: 'url',
168
+ short: 'u',
169
+ type: 'url',
170
+ multiple: true,
171
+ description: 'URL of the page to test'
172
+ },
173
+ {
174
+ name: 'version',
175
+ type: 'boolean',
176
+ description: 'display version'
177
+ }
178
+ ];
179
+ export const defaults = {
180
+ browser: 'puppeteer',
181
+ ci: !process.stdout.isTTY,
182
+ config: 'ui5-test-runner.json',
183
+ cwd: process.cwd(),
184
+ localhost: 'localhost',
185
+ outputInterval: 30_000,
186
+ parallel: 2,
187
+ reportDir: 'report',
188
+ testsuite: 'test/testsuite.qunit.html',
189
+ ui5: 'https://ui5.sap.com',
190
+ webapp: 'webapp'
191
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ const BOOLEAN_TRUE = new Set(['true', 'on', 1]);
3
+ const BOOLEAN_FALSE = new Set(['false', 'off', 0]);
4
+ export const boolean = (option, value) => {
5
+ if (typeof value === 'boolean') {
6
+ return value;
7
+ }
8
+ if (BOOLEAN_TRUE.has(value)) {
9
+ return true;
10
+ }
11
+ if (BOOLEAN_FALSE.has(value)) {
12
+ return false;
13
+ }
14
+ throw new OptionValidationError(option);
15
+ };
@@ -0,0 +1,11 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ const KNOWN_BROWSERS = new Set(['puppeteer']);
3
+ export const browser = (option, value) => {
4
+ if (typeof value === 'string') {
5
+ const name = value.startsWith('$/') && value.endsWith('.js') ? value.slice(2, -3) : value;
6
+ if (KNOWN_BROWSERS.has(name)) {
7
+ return name;
8
+ }
9
+ }
10
+ throw new OptionValidationError(option);
11
+ };
@@ -0,0 +1,70 @@
1
+ import { FileSystem, Host, Path } from '../../platform/index.js';
2
+ import { OptionValidationError } from '../OptionValidationError.js';
3
+ const fsCheckAccess = async (option, path) => {
4
+ const overwrite = option.typeModifiers?.has('overwrite') ?? false;
5
+ try {
6
+ const mode = FileSystem.constants.R_OK | (overwrite ? FileSystem.constants.W_OK : 0);
7
+ await FileSystem.access(path, mode);
8
+ }
9
+ catch (error) {
10
+ if (overwrite) {
11
+ const code = typeof error === 'object' && error && 'code' in error && error.code;
12
+ if (code === 'EACCES') {
13
+ throw new OptionValidationError(option, 'Unable to access file system entry', error);
14
+ }
15
+ if (code === 'ENOENT') {
16
+ return path;
17
+ }
18
+ }
19
+ else {
20
+ throw new OptionValidationError(option, `Unable to access file system entry`, error);
21
+ }
22
+ }
23
+ return undefined;
24
+ };
25
+ const fsCheckStat = async (option, path) => {
26
+ const file = option.typeModifiers?.has('file') ?? false;
27
+ try {
28
+ const stat = await FileSystem.stat(path);
29
+ if (!file && !stat.isDirectory()) {
30
+ throw new OptionValidationError(option, 'Not a folder');
31
+ }
32
+ if (file && !stat.isFile()) {
33
+ throw new OptionValidationError(option, 'Not a file');
34
+ }
35
+ }
36
+ catch (error) {
37
+ throw new OptionValidationError(option, 'Unable to check type', error);
38
+ }
39
+ };
40
+ export const fsOption = async (option, value, configuration) => {
41
+ if (typeof value !== 'string') {
42
+ throw new OptionValidationError(option, 'Expected string');
43
+ }
44
+ let path;
45
+ if (Path.isAbsolute(value)) {
46
+ path = value;
47
+ }
48
+ else if (option.name === 'cwd') {
49
+ path = Path.join(Host.cwd(), value);
50
+ }
51
+ else {
52
+ path = Path.join(configuration.cwd, value);
53
+ }
54
+ try {
55
+ const accessChecked = await fsCheckAccess(option, path);
56
+ if (accessChecked !== undefined) {
57
+ return accessChecked;
58
+ }
59
+ await fsCheckStat(option, path);
60
+ }
61
+ catch (error) {
62
+ if (option.typeModifiers?.has('safe-default') &&
63
+ !Object.prototype.hasOwnProperty.call(configuration, option.name)) {
64
+ return '';
65
+ }
66
+ throw error;
67
+ }
68
+ return path;
69
+ };
70
+ export const fsEntry = fsOption;
@@ -0,0 +1,20 @@
1
+ import { boolean } from './boolean.js';
2
+ import { browser } from './browser.js';
3
+ import { fsEntry } from './fsEntry.js';
4
+ import { integer } from './integer.js';
5
+ import { percent } from './percent.js';
6
+ import { regexp } from './regexp.js';
7
+ import { string } from './string.js';
8
+ import { timeout } from './timeout.js';
9
+ import { url } from './url.js';
10
+ export const validators = {
11
+ boolean,
12
+ browser,
13
+ 'fs-entry': fsEntry,
14
+ integer,
15
+ percent,
16
+ regexp,
17
+ string,
18
+ timeout,
19
+ url
20
+ };
@@ -0,0 +1,10 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const integer = (option, value) => {
3
+ if (typeof value === 'string') {
4
+ value = Number.parseInt(value);
5
+ }
6
+ if (typeof value !== 'number' || value % 1 !== 0) {
7
+ throw new OptionValidationError(option);
8
+ }
9
+ return value;
10
+ };
@@ -0,0 +1,17 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const percent = (option, value) => {
3
+ if (typeof value === 'string') {
4
+ const match = /^(\d{0,3}(:?\.\d{0,3})?)%$/.exec(value);
5
+ if (match) {
6
+ const [, digits = '0'] = match;
7
+ const float = Number.parseFloat(digits);
8
+ if (float >= 0 && float <= 100) {
9
+ value = float;
10
+ }
11
+ }
12
+ }
13
+ if (typeof value === 'number' && !Number.isNaN(value) && value >= 0 && value <= 100) {
14
+ return value;
15
+ }
16
+ throw new OptionValidationError(option);
17
+ };
@@ -0,0 +1,20 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const regexp = (option, value) => {
3
+ if (typeof value === 'object' && value instanceof RegExp) {
4
+ return value;
5
+ }
6
+ if (typeof value !== 'string' || !value) {
7
+ throw new OptionValidationError(option);
8
+ }
9
+ try {
10
+ const match = /^\/(.+)\/(\w*)$/.exec(value);
11
+ if (match) {
12
+ const [, pattern = '', flags = ''] = match;
13
+ return new RegExp(pattern, flags);
14
+ }
15
+ return new RegExp(value);
16
+ }
17
+ catch (error) {
18
+ throw new OptionValidationError(option, 'Invalid regexp', error);
19
+ }
20
+ };
@@ -0,0 +1,7 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const string = (option, value) => {
3
+ if (typeof value !== 'string' || !value) {
4
+ throw new OptionValidationError(option);
5
+ }
6
+ return value;
7
+ };
@@ -0,0 +1,24 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const timeout = (option, value) => {
3
+ if (typeof value === 'string') {
4
+ if (value === 'none') {
5
+ return 0;
6
+ }
7
+ const match = /^(\d+)(ms|s|sec|m|min)?$/.exec(value);
8
+ if (match) {
9
+ const [, digits = '0', specifier = ''] = match;
10
+ let int = Number.parseInt(digits);
11
+ if (['s', 'sec'].includes(specifier)) {
12
+ int = int * 1000;
13
+ }
14
+ if (['m', 'min'].includes(specifier)) {
15
+ int = int * 60 * 1000;
16
+ }
17
+ value = int;
18
+ }
19
+ }
20
+ if (typeof value === 'number' && !Number.isNaN(value) && value >= 0 && value < Number.POSITIVE_INFINITY) {
21
+ return value;
22
+ }
23
+ throw new OptionValidationError(option);
24
+ };
@@ -0,0 +1,8 @@
1
+ import { OptionValidationError } from '../OptionValidationError.js';
2
+ export const looksLikeAnUrl = (value) => !!/^https?:\/\/[^ "]+$/.test(value);
3
+ export const url = (option, value) => {
4
+ if (typeof value === 'string' && looksLikeAnUrl(value)) {
5
+ return value;
6
+ }
7
+ throw new OptionValidationError(option);
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export const Modes = {
2
+ batch: 'batch',
3
+ capabilities: 'capabilities',
4
+ help: 'help',
5
+ legacy: 'legacy',
6
+ log: 'log',
7
+ remote: 'remote',
8
+ version: 'version'
9
+ };
@@ -0,0 +1,27 @@
1
+ import { help } from './help.js';
2
+ import { log } from './log/index.js';
3
+ import { Modes } from './Modes.js';
4
+ import { version } from './version.js';
5
+ import { test } from './test/index.js';
6
+ import { Exit } from '../platform/Exit.js';
7
+ const notImplemented = () => {
8
+ throw new Error('Not implemented');
9
+ };
10
+ const modeFunctions = {
11
+ [Modes.batch]: notImplemented,
12
+ [Modes.capabilities]: notImplemented,
13
+ [Modes.help]: help,
14
+ [Modes.legacy]: test,
15
+ [Modes.log]: log,
16
+ [Modes.remote]: test,
17
+ [Modes.version]: version
18
+ };
19
+ export const execute = async (configuration) => {
20
+ try {
21
+ const modeFunction = modeFunctions[configuration.mode];
22
+ await modeFunction(configuration);
23
+ }
24
+ finally {
25
+ await Exit.shutdown();
26
+ }
27
+ };
@@ -0,0 +1,3 @@
1
+ export const help = () => {
2
+ console.log('Please check https://arnaudbuchholz.github.io/ui5-test-runner/');
3
+ };
@@ -0,0 +1 @@
1
+ export const MAX_LIMIT = 1000;
@@ -0,0 +1,9 @@
1
+ export const getInitialLogMetrics = () => ({
2
+ inputSize: 0,
3
+ chunksCount: 0,
4
+ outputSize: 0,
5
+ minTimestamp: Number.MAX_SAFE_INTEGER,
6
+ maxTimestamp: 0,
7
+ logCount: 0,
8
+ reading: true
9
+ });
@@ -0,0 +1,37 @@
1
+ import { ZLib } from '../../platform/index.js';
2
+ import { createCompressionContext, uncompress } from '../../platform/logger/compress.js';
3
+ import { FramedStreamReader } from '../../utils/node/FramedStreamReader.js';
4
+ import { getInitialLogMetrics } from './LogMetrics.js';
5
+ export const POLL_INTERVAL_MS = 500;
6
+ export const LogReader = {
7
+ async *read(logFileName, signal) {
8
+ const context = createCompressionContext();
9
+ const stream = FramedStreamReader.create(logFileName, POLL_INTERVAL_MS);
10
+ const metrics = getInitialLogMetrics();
11
+ for await (const chunk of stream.read(signal)) {
12
+ metrics.inputSize += 4 + chunk.length;
13
+ ++metrics.chunksCount;
14
+ const lines = ZLib.inflateRawSync(chunk).toString();
15
+ const jsons = uncompress(context, lines);
16
+ for (const json of jsons) {
17
+ ++metrics.logCount;
18
+ metrics.minTimestamp = Math.min(metrics.minTimestamp, json.timestamp);
19
+ metrics.maxTimestamp = Math.max(metrics.maxTimestamp, json.timestamp);
20
+ metrics.outputSize += JSON.stringify(json).length + 1;
21
+ yield {
22
+ type: 'log',
23
+ ...json
24
+ };
25
+ }
26
+ yield {
27
+ type: 'metrics',
28
+ ...metrics
29
+ };
30
+ }
31
+ metrics.reading = false;
32
+ yield {
33
+ type: 'metrics',
34
+ ...metrics
35
+ };
36
+ }
37
+ };
@@ -0,0 +1,68 @@
1
+ import { LogLevel } from '../../platform/logger/types.js';
2
+ import { MAX_LIMIT } from './ILogStorage.js';
3
+ import { punyexpr } from 'punyexpr';
4
+ const LOG_LEVELS = Object.fromEntries(Object.entries(LogLevel).map(([name, value]) => [value, name]));
5
+ export class LogStorage {
6
+ static create() {
7
+ return new this();
8
+ }
9
+ _logs = [];
10
+ _logsAdded = false;
11
+ constructor() { }
12
+ get length() {
13
+ return this._logs.length;
14
+ }
15
+ add(log) {
16
+ this._logs.push(log);
17
+ this._logsAdded = true;
18
+ }
19
+ _sortIfNeeded() {
20
+ if (this._logsAdded) {
21
+ this._logs.sort((a, b) => a.timestamp - b.timestamp);
22
+ this._logsAdded = false;
23
+ }
24
+ }
25
+ static buildFilterExpression(filter) {
26
+ if (!filter) {
27
+ return () => true;
28
+ }
29
+ const expression = punyexpr(filter);
30
+ return (log) => !!expression({
31
+ ...log,
32
+ level: LOG_LEVELS[log.level],
33
+ [punyexpr.propertyOf]: (value, property) => {
34
+ if (value === undefined) {
35
+ return;
36
+ }
37
+ return value[property];
38
+ }
39
+ });
40
+ }
41
+ fetch(query = {}) {
42
+ this._sortIfNeeded();
43
+ const { from = 0, to = Number.MAX_SAFE_INTEGER, filter = '' } = query;
44
+ let { skip = 0, limit = MAX_LIMIT } = query;
45
+ const filterExpression = LogStorage.buildFilterExpression(filter);
46
+ limit = Math.max(0, Math.min(limit, MAX_LIMIT));
47
+ const records = [];
48
+ for (let index = 0; limit > 0 && index < this._logs.length; ++index) {
49
+ const log = this._logs[index];
50
+ if (log.timestamp < from) {
51
+ continue;
52
+ }
53
+ if (log.timestamp > to) {
54
+ break;
55
+ }
56
+ if (skip > 0) {
57
+ --skip;
58
+ continue;
59
+ }
60
+ if (!filterExpression(log)) {
61
+ continue;
62
+ }
63
+ records.push(log);
64
+ --limit;
65
+ }
66
+ return records;
67
+ }
68
+ }
@@ -0,0 +1,101 @@
1
+ import { __developmentMode, __sourcesRoot, Path, FileSystem } from '../../platform/index.js';
2
+ import { memoize } from '../../utils/shared/memoize.js';
3
+ const getLibrarySource = memoize(async () => {
4
+ const path = __developmentMode
5
+ ? Path.join(__sourcesRoot, '../dist/ui', 'lib.js')
6
+ : Path.join(__sourcesRoot, 'ui/lib.js');
7
+ return FileSystem.readFile(path, 'utf8');
8
+ });
9
+ const getLogViewerSource = memoize(async () => {
10
+ const path = __developmentMode
11
+ ? Path.join(__sourcesRoot, '../dist/ui', 'log-viewer.js')
12
+ : Path.join(__sourcesRoot, 'ui/log-viewer.js');
13
+ return FileSystem.readFile(path, 'utf8');
14
+ });
15
+ const getAsInt = (parameters, key) => {
16
+ const value = parameters.get(key);
17
+ return value ? Number.parseInt(value) : undefined;
18
+ };
19
+ export const buildREserveConfiguration = (storage, metrics, abortController) => ({
20
+ port: 0,
21
+ mappings: [
22
+ {
23
+ method: 'GET',
24
+ match: '/query',
25
+ custom: ({ url }) => {
26
+ const searchParameters = new URLSearchParams(new URL(url, 'http://localhost').search);
27
+ const from = getAsInt(searchParameters, 'from');
28
+ const to = getAsInt(searchParameters, 'to');
29
+ const skip = getAsInt(searchParameters, 'skip');
30
+ const limit = getAsInt(searchParameters, 'limit');
31
+ const filter = searchParameters.get('filter') ?? undefined;
32
+ return [
33
+ {
34
+ metrics,
35
+ logs: storage.fetch({ from, to, skip, limit, filter })
36
+ }
37
+ ];
38
+ }
39
+ },
40
+ {
41
+ method: 'GET',
42
+ match: '/lib.js',
43
+ custom: async () => [
44
+ await getLibrarySource(),
45
+ {
46
+ headers: {
47
+ 'Content-Type': 'application/javascript'
48
+ }
49
+ }
50
+ ]
51
+ },
52
+ {
53
+ method: 'GET',
54
+ match: '/log-viewer.js',
55
+ custom: async () => [
56
+ await getLogViewerSource(),
57
+ {
58
+ headers: {
59
+ 'Content-Type': 'application/javascript'
60
+ }
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ match: '/close',
66
+ custom: () => {
67
+ abortController.abort();
68
+ return 200;
69
+ }
70
+ },
71
+ {
72
+ method: 'GET',
73
+ custom: () => [
74
+ `<!DOCTYPE html>
75
+ <html lang="en">
76
+ <head>
77
+ <meta charset="UTF-8">
78
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
79
+ <title>UI5 Test Runner Log Viewer</title>
80
+ </head>
81
+ <body>
82
+ <div id="app"></div>
83
+ <script type="module" src="/lib.js"></script>
84
+ <script type="module" src="/log-viewer.js"></script>
85
+ <script>
86
+ const close = () => navigator.sendBeacon('/close');
87
+ document.addEventListener('visibilitychange', () => (document.visibilityState === 'hidden') && close());
88
+ window.addEventListener('pagehide', (event) => (!event.persisted) && close());
89
+ </script>
90
+ </body>
91
+ </html>
92
+ `,
93
+ {
94
+ headers: {
95
+ 'Content-Type': 'text/html'
96
+ }
97
+ }
98
+ ]
99
+ }
100
+ ]
101
+ });