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

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 +17 -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,158 @@
1
+ import { FileSystem, Path, Terminal } from '../../index.js';
2
+ import { LogLevel } from '../types.js';
3
+ import { ProgressBar } from '../../../utils/shared/ProgressBar.js';
4
+ import { formatDuration } from '../../../utils/shared/string.js';
5
+ import { assert } from '../../assert.js';
6
+ const icons = {
7
+ [LogLevel.debug]: Terminal.BLUE + '<o>',
8
+ [LogLevel.info]: ' ',
9
+ [LogLevel.warn]: Terminal.YELLOW + '/!\\',
10
+ [LogLevel.error]: Terminal.RED + '(X)',
11
+ [LogLevel.fatal]: Terminal.RED + 'o*!'
12
+ };
13
+ const DO_NOT_RENDER_SOURCE = [
14
+ 'browser',
15
+ 'browser/console',
16
+ 'browser/network',
17
+ 'metric',
18
+ 'progress'
19
+ ];
20
+ export class BaseLoggerOutput {
21
+ _configuration;
22
+ _startedAt;
23
+ constructor(configuration, startedAt) {
24
+ this._configuration = configuration;
25
+ this._startedAt = startedAt;
26
+ }
27
+ formatTimestamp(timestamp) {
28
+ return formatDuration(timestamp - this._startedAt);
29
+ }
30
+ render(attributes) {
31
+ const { level, timestamp, source, message, data, error } = attributes;
32
+ if (!DO_NOT_RENDER_SOURCE.includes(source) && level !== LogLevel.debug) {
33
+ return [
34
+ icons[level],
35
+ Terminal.MAGENTA,
36
+ this.formatTimestamp(timestamp),
37
+ Terminal.WHITE,
38
+ ' ',
39
+ message,
40
+ data ? ` ${JSON.stringify(data)}` : '',
41
+ error ? ` ${Terminal.RED}${error.name} ${error.message}` : '',
42
+ '\n'
43
+ ].join('');
44
+ }
45
+ }
46
+ addToReport(rawText) {
47
+ FileSystem.writeFileSync(Path.join(this._configuration.reportDir, 'output.txt'), rawText, {
48
+ encoding: 'utf8',
49
+ flag: 'a'
50
+ });
51
+ }
52
+ _pageProgressMap = {};
53
+ get pageIds() {
54
+ return Object.keys(this._pageProgressMap)
55
+ .map(Number)
56
+ .toSorted((a, b) => a - b);
57
+ }
58
+ get pageProgressMap() {
59
+ return this._pageProgressMap;
60
+ }
61
+ _overallProgressBar = new ProgressBar();
62
+ get overallProgressBar() {
63
+ return this._overallProgressBar;
64
+ }
65
+ _overallprogressSnapshot = {
66
+ totalNumberOfExecutedTests: 0,
67
+ totalNumberOfErrors: 0,
68
+ totalNumberOfTests: 0
69
+ };
70
+ _updateOverallProgressSnapshot(data) {
71
+ if (data.type !== 'unknown') {
72
+ this._overallprogressSnapshot.totalNumberOfExecutedTests += data.value;
73
+ this._overallprogressSnapshot.totalNumberOfErrors += data.errors;
74
+ this._overallprogressSnapshot.totalNumberOfTests += data.max;
75
+ }
76
+ }
77
+ _overallprogress = {
78
+ totalNumberOfExecutedTests: 0,
79
+ totalNumberOfErrors: 0,
80
+ totalNumberOfTests: 0
81
+ };
82
+ _updateOverallProgress() {
83
+ const overallProgress = { ...this._overallprogressSnapshot };
84
+ for (const pageProgress of Object.values(this._pageProgressMap)) {
85
+ if (pageProgress.type !== 'unknown') {
86
+ overallProgress.totalNumberOfExecutedTests += pageProgress.bar.value;
87
+ overallProgress.totalNumberOfErrors += pageProgress.errors;
88
+ overallProgress.totalNumberOfTests += pageProgress.bar.max;
89
+ }
90
+ }
91
+ this._overallprogress = overallProgress;
92
+ }
93
+ get overallProgress() {
94
+ return this._overallprogress;
95
+ }
96
+ _lastOverallStatus = '';
97
+ _startedAtMap = {};
98
+ renderAttributes(attributes) {
99
+ if (attributes.source === 'progress') {
100
+ const { pageId } = attributes;
101
+ let pageProgress;
102
+ let progressBar;
103
+ if (pageId === undefined) {
104
+ progressBar = this._overallProgressBar;
105
+ }
106
+ else {
107
+ pageProgress = this._pageProgressMap[pageId];
108
+ if (!pageProgress) {
109
+ pageProgress = {
110
+ bar: new ProgressBar(),
111
+ type: 'unknown',
112
+ errors: 0
113
+ };
114
+ this._pageProgressMap[pageId] = pageProgress;
115
+ this._startedAtMap[pageId] = Date.now();
116
+ this.addToReport(` ${this.formatTimestamp(attributes.timestamp)} >> ${attributes.message} [${pageId}]
117
+ `);
118
+ }
119
+ const data = attributes.data;
120
+ pageProgress.type = data.type;
121
+ pageProgress.errors = data.errors;
122
+ progressBar = pageProgress.bar;
123
+ }
124
+ progressBar.update(attributes);
125
+ if (pageId && 'remove' in attributes.data) {
126
+ delete this._pageProgressMap[pageId];
127
+ const startedAt = this._startedAtMap[pageId];
128
+ delete this._startedAtMap[pageId];
129
+ assert(startedAt !== undefined);
130
+ const duration = Date.now() - startedAt;
131
+ this
132
+ .addToReport(` ${this.formatTimestamp(attributes.timestamp)} << ${attributes.message} (${formatDuration(duration)}) [${pageId}]
133
+ `);
134
+ this._updateOverallProgressSnapshot(attributes.data);
135
+ }
136
+ this._updateOverallProgress();
137
+ if (pageId === undefined && progressBar.label !== this._lastOverallStatus) {
138
+ this._lastOverallStatus = progressBar.label;
139
+ this.addToReport(`
140
+ ${this.formatTimestamp(attributes.timestamp)}|${this._lastOverallStatus}
141
+ -----+${''.padStart(this._lastOverallStatus.length, '-')}
142
+ `);
143
+ }
144
+ return false;
145
+ }
146
+ return true;
147
+ }
148
+ addAttributesToLoggerOutput(attributes) {
149
+ if (this.renderAttributes(attributes)) {
150
+ const rendered = this.render(attributes);
151
+ if (rendered) {
152
+ const raw = Terminal.stripVTControlCharacters(rendered);
153
+ this.addTextToLoggerOutput(rendered, raw);
154
+ this.addToReport(raw);
155
+ }
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,102 @@
1
+ import { Terminal } from '../../index.js';
2
+ import { BaseLoggerOutput } from './BaseLoggerOutput.js';
3
+ const TICKS_INTERVAL = 250;
4
+ const TICKS_PICTURES = ['[|]', '[/]', '[-]', String.raw `[\]`];
5
+ const TICKS_COLORS = [Terminal.BLUE, Terminal.CYAN, Terminal.MAGENTA, Terminal.CYAN, Terminal.YELLOW, Terminal.CYAN];
6
+ export class InteractiveLoggerOutput extends BaseLoggerOutput {
7
+ _noColor;
8
+ _ticksInterval;
9
+ _tick = 0;
10
+ _terminalWidth = Terminal.width;
11
+ _linesToErase = [];
12
+ _texts = [];
13
+ constructor(configuration, startedAt) {
14
+ super(configuration, startedAt);
15
+ this._noColor = !!process.env['NO_COLOR'];
16
+ Terminal.hideCursor();
17
+ this._ticksInterval = setInterval(this.tick.bind(this), TICKS_INTERVAL);
18
+ }
19
+ _clean() {
20
+ let count = 0;
21
+ for (const width of this._linesToErase) {
22
+ if (width > this._terminalWidth) {
23
+ count += Math.ceil(width / this._terminalWidth);
24
+ }
25
+ else {
26
+ ++count;
27
+ }
28
+ }
29
+ Terminal.eraseToEnd(count);
30
+ this._linesToErase = [];
31
+ }
32
+ terminalResized(width) {
33
+ this._terminalWidth = width;
34
+ this._clean();
35
+ this._progress();
36
+ }
37
+ addTextToLoggerOutput(formatted, raw) {
38
+ this._texts.push(this._noColor ? raw : formatted);
39
+ }
40
+ _progress() {
41
+ const texts = [...this._texts];
42
+ const linesToErase = [];
43
+ this._texts.length = 0;
44
+ for (const pageId of this.pageIds) {
45
+ const pageProgress = this.pageProgressMap[pageId];
46
+ const rendered = pageProgress.bar.render(this._terminalWidth - 4);
47
+ let status = ' ';
48
+ if (pageProgress.errors) {
49
+ status = `${Terminal.RED}${Math.min(pageProgress.errors, 999).toString().padEnd(3, ' ')}${Terminal.WHITE}`;
50
+ }
51
+ else if (pageProgress.type === 'opa') {
52
+ status = `${Terminal.GREEN}OPA${Terminal.WHITE}`;
53
+ }
54
+ else if (pageProgress.type === 'qunit') {
55
+ status = `${Terminal.GREEN}QNT${Terminal.WHITE}`;
56
+ }
57
+ texts.push(status + rendered + '\n');
58
+ linesToErase.push(3 + rendered.length);
59
+ }
60
+ let testStatus = '';
61
+ if (this.overallProgress.totalNumberOfTests !== 0) {
62
+ const { totalNumberOfExecutedTests: executed, totalNumberOfErrors: errors, totalNumberOfTests: total } = this.overallProgress;
63
+ const success = executed - errors;
64
+ const errorStatus = errors > 0 ? `${Terminal.WHITE}+${Terminal.RED}${errors}` : '';
65
+ testStatus = ` (Tests: ${Terminal.GREEN}${success}${errorStatus}${Terminal.WHITE}/${total})`;
66
+ }
67
+ const rendered = this.overallProgressBar.render(this._terminalWidth - 4 - testStatus.length);
68
+ if (this._noColor) {
69
+ texts.push([
70
+ TICKS_PICTURES[this._tick % TICKS_PICTURES.length],
71
+ Terminal.stripVTControlCharacters(rendered + testStatus),
72
+ '\n'
73
+ ].join(''));
74
+ }
75
+ else {
76
+ texts.push([
77
+ TICKS_COLORS[this._tick % TICKS_COLORS.length],
78
+ TICKS_PICTURES[this._tick % TICKS_PICTURES.length],
79
+ Terminal.WHITE,
80
+ rendered,
81
+ testStatus,
82
+ '\n'
83
+ ].join(''));
84
+ }
85
+ linesToErase.push(3 + rendered.length);
86
+ this._clean();
87
+ Terminal.write(texts.join(''));
88
+ for (const line of linesToErase) {
89
+ this._linesToErase.push(line);
90
+ }
91
+ }
92
+ tick() {
93
+ ++this._tick;
94
+ this._progress();
95
+ }
96
+ closeLoggerOutput() {
97
+ clearInterval(this._ticksInterval);
98
+ this._progress();
99
+ this._clean();
100
+ Terminal.showCursor();
101
+ }
102
+ }
@@ -0,0 +1,32 @@
1
+ import { Terminal } from '../../index.js';
2
+ import { BaseLoggerOutput } from './BaseLoggerOutput.js';
3
+ export class StaticLoggerOutput extends BaseLoggerOutput {
4
+ _progressReportInterval;
5
+ constructor(configuration, startedAt) {
6
+ super(configuration, startedAt);
7
+ this._progressReportInterval = setInterval(this.progressReport.bind(this), configuration.outputInterval);
8
+ }
9
+ progressReport() {
10
+ const pageIds = this.pageIds;
11
+ if (pageIds.length > 0) {
12
+ this.addToReport(`
13
+ ${this.formatTimestamp(Date.now())}|Progress
14
+ -----+--------
15
+ `);
16
+ }
17
+ for (const pageId of pageIds) {
18
+ const pageProgress = this.pageProgressMap[pageId];
19
+ const rendered = pageProgress.bar.render(80);
20
+ this.addToReport(rendered + '\n');
21
+ }
22
+ }
23
+ terminalResized() { }
24
+ addTextToLoggerOutput() { }
25
+ addToReport(rawText) {
26
+ super.addToReport(rawText);
27
+ Terminal.write(rawText);
28
+ }
29
+ closeLoggerOutput() {
30
+ clearInterval(this._progressReportInterval);
31
+ }
32
+ }
@@ -0,0 +1,10 @@
1
+ import { InteractiveLoggerOutput } from './InteractiveLoggerOutput.js';
2
+ import { StaticLoggerOutput } from './StaticLoggerOutput.js';
3
+ export const LoggerOutputFactory = {
4
+ build(configuration, startedAt) {
5
+ const { ci } = configuration;
6
+ return ci
7
+ ? new StaticLoggerOutput(configuration, startedAt)
8
+ : new InteractiveLoggerOutput(configuration, startedAt);
9
+ }
10
+ };
@@ -0,0 +1,58 @@
1
+ import { Thread } from '../Thread.js';
2
+ import '../logger.js';
3
+ import { LoggerOutputFactory } from './output/factory.js';
4
+ const fromHexaCode = (value) => ({
5
+ r: Number.parseInt(value.slice(1, 3), 16),
6
+ g: Number.parseInt(value.slice(3, 5), 16),
7
+ b: Number.parseInt(value.slice(5, 7), 16)
8
+ });
9
+ export const workerMain = ({ configuration, startedAt }) => {
10
+ const loggerOutput = LoggerOutputFactory.build(configuration, startedAt);
11
+ const channel = Thread.createBroadcastChannel('logger');
12
+ channel.onmessage = (event) => {
13
+ const { data: message } = event;
14
+ if (message.command === 'terminate') {
15
+ channel.close();
16
+ loggerOutput.closeLoggerOutput();
17
+ }
18
+ else if (message.command === 'log') {
19
+ loggerOutput.addAttributesToLoggerOutput(message);
20
+ }
21
+ else if (message.command === 'terminal-resized') {
22
+ loggerOutput.terminalResized(message.width);
23
+ }
24
+ };
25
+ const from = fromHexaCode('#FF5A37');
26
+ const to = fromHexaCode('#FFA42C');
27
+ const background = '\u001B[40m';
28
+ const diff = {
29
+ r: to.r - from.r,
30
+ g: to.g - from.g,
31
+ b: to.b - from.b
32
+ };
33
+ const rawLogo = String.raw ` _, ,_ _ ____ _ _
34
+ .'/ _, \'. _ _(_) ___| | |_ ___ ___| |_ _ __ _ _ _ __ _ __ ___ _ __
35
+ | \__< )__/ | | | | | |___ \ _____| __/ _ \/ __| __|____| '__| | | | '_ \| '_ \ / _ \ '__|
36
+ \ / | |_| | |___) |_____| || __/\__ \ ||_____| | | |_| | | | | | | | __/ |
37
+ '-..(___)..-' \__,_|_|____/ \__\___||___/\__| |_| \__,_|_| |_|_| |_|\___|_|`;
38
+ const width = Math.max(...rawLogo.split('\n').map((line) => line.length));
39
+ const logo = rawLogo
40
+ .split('\n')
41
+ .map((line) => background +
42
+ [...line.padEnd(width, ' ')]
43
+ .map((c, index) => {
44
+ const r = Math.floor(from.r + (diff.r * index) / width);
45
+ const g = Math.floor(from.g + (diff.g * index) / width);
46
+ const b = Math.floor(from.b + (diff.b * index) / width);
47
+ return `\u001B[38;2;${r};${g};${b}m` + c;
48
+ })
49
+ .join('') +
50
+ '\u001B[0m')
51
+ .join('\n');
52
+ loggerOutput.addToReport(rawLogo + '\n');
53
+ loggerOutput.addTextToLoggerOutput(logo + '\u001B[0m\n', rawLogo + '\n');
54
+ channel.postMessage({
55
+ command: 'ready',
56
+ source: 'output'
57
+ });
58
+ };
@@ -0,0 +1,6 @@
1
+ export let logger;
2
+ const breakDependencyLoopToLogger = async () => {
3
+ const module = await import('../logger.js');
4
+ logger = module.logger;
5
+ };
6
+ void breakDependencyLoopToLogger();
@@ -0,0 +1,22 @@
1
+ import { Host } from '../Host.js';
2
+ import { Thread } from '../Thread.js';
3
+ export const toInternalLogAttributes = (attributes, level) => {
4
+ if (attributes.processId !== undefined) {
5
+ return {
6
+ timestamp: Date.now(),
7
+ level,
8
+ threadId: 0,
9
+ isMainThread: false,
10
+ ...attributes,
11
+ processId: attributes.processId
12
+ };
13
+ }
14
+ return {
15
+ timestamp: Date.now(),
16
+ level,
17
+ processId: Host.pid,
18
+ threadId: Thread.threadId,
19
+ isMainThread: Thread.isMainThread,
20
+ ...attributes
21
+ };
22
+ };
@@ -0,0 +1,7 @@
1
+ export const LogLevel = {
2
+ debug: 0,
3
+ info: 1,
4
+ warn: 2,
5
+ error: 3,
6
+ fatal: 4
7
+ };
@@ -0,0 +1,138 @@
1
+ import { Exit, ExitShutdownError } from './Exit.js';
2
+ import { Host } from './Host.js';
3
+ import { Terminal } from './Terminal.js';
4
+ import { Thread } from './Thread.js';
5
+ import { LogLevel } from './logger/types.js';
6
+ import { toInternalLogAttributes } from './logger/toInternalLogAttributes.js';
7
+ import assert from 'node:assert/strict';
8
+ import { toPlainObject } from '../utils/shared/object.js';
9
+ import { toIError } from '../utils/shared/toIError.js';
10
+ const startedAt = Date.now();
11
+ let channel;
12
+ let loggerWorker;
13
+ let consoleWorker;
14
+ const buffer = [];
15
+ const waitingFor = ['allCompressed', 'output'];
16
+ let ready = !Thread.isMainThread;
17
+ let metricsMonitorInterval;
18
+ const terminalResized = (width) => {
19
+ channel?.postMessage({
20
+ command: 'terminal-resized',
21
+ width
22
+ });
23
+ };
24
+ const start = () => {
25
+ channel = Thread.createBroadcastChannel('logger');
26
+ metricsMonitorInterval = setInterval(() => {
27
+ logger.debug({
28
+ source: 'metric',
29
+ message: '',
30
+ data: {
31
+ cpu: Thread.threadCpuUsage(),
32
+ mem: Host.memoryUsage()
33
+ }
34
+ });
35
+ }, 1000);
36
+ channel.onmessage = (event) => {
37
+ const { data: message } = event;
38
+ if (message.command === 'ready' && Thread.isMainThread) {
39
+ terminalResized(Terminal.width);
40
+ const index = waitingFor.indexOf(message.source);
41
+ waitingFor.splice(index, 1);
42
+ ready = waitingFor.length === 0;
43
+ if (ready && buffer.length > 0) {
44
+ for (const buffered of buffer) {
45
+ channel.postMessage({
46
+ command: 'log',
47
+ ...buffered
48
+ });
49
+ }
50
+ buffer.length = 0;
51
+ }
52
+ }
53
+ else if (message.command === 'terminate') {
54
+ clearInterval(metricsMonitorInterval);
55
+ channel.close();
56
+ }
57
+ };
58
+ };
59
+ const log = (level, attributes) => {
60
+ const allAttributes = toInternalLogAttributes(attributes, level);
61
+ if (attributes.error) {
62
+ allAttributes.error = toIError(attributes.error);
63
+ }
64
+ if (ready && !stopping) {
65
+ channel.postMessage({
66
+ command: 'log',
67
+ ...allAttributes
68
+ });
69
+ }
70
+ else {
71
+ buffer.push(allAttributes);
72
+ }
73
+ };
74
+ if (!Thread.isMainThread) {
75
+ start();
76
+ }
77
+ let stopping;
78
+ export const logger = {
79
+ start(configuration) {
80
+ assert.ok(Thread.isMainThread, 'Call logger.start only in main thread');
81
+ assert.ok(!loggerWorker && !consoleWorker, 'Call logger.start only once');
82
+ start();
83
+ loggerWorker = Thread.createWorker('platform/logger/allCompressed', {
84
+ configuration: toPlainObject(configuration)
85
+ });
86
+ consoleWorker = Thread.createWorker('platform/logger/output', {
87
+ configuration: toPlainObject(configuration),
88
+ startedAt
89
+ });
90
+ if (!configuration.ci) {
91
+ Terminal.setRawMode((data) => {
92
+ if (data.length === 1 && data[0] === 3) {
93
+ logger.warn({ source: 'job', message: 'User requested interruption' });
94
+ Exit.sigInt();
95
+ }
96
+ });
97
+ Terminal.onResize(terminalResized);
98
+ }
99
+ },
100
+ debug(attributes) {
101
+ log(LogLevel.debug, attributes);
102
+ },
103
+ info(attributes) {
104
+ log(LogLevel.info, attributes);
105
+ },
106
+ warn(attributes) {
107
+ log(LogLevel.warn, attributes);
108
+ },
109
+ error(attributes) {
110
+ log(attributes.error && attributes.error instanceof ExitShutdownError ? LogLevel.debug : LogLevel.error, attributes);
111
+ },
112
+ fatal(attributes) {
113
+ log(LogLevel.fatal, attributes);
114
+ },
115
+ async stop() {
116
+ assert.ok(Thread.isMainThread, 'Call logger.stop only in main thread');
117
+ if (stopping) {
118
+ return await stopping;
119
+ }
120
+ const { promise, resolve } = Promise.withResolvers();
121
+ stopping = promise;
122
+ clearInterval(metricsMonitorInterval);
123
+ if (loggerWorker && consoleWorker) {
124
+ while (waitingFor.length > 0) {
125
+ await new Promise((resolve) => setTimeout(resolve, 250));
126
+ }
127
+ const { promise: loggerPromise, resolve: loggerExited } = Promise.withResolvers();
128
+ const { promise: consolePromise, resolve: consoleExited } = Promise.withResolvers();
129
+ loggerWorker.on('exit', loggerExited);
130
+ consoleWorker.on('exit', consoleExited);
131
+ channel.postMessage({ command: 'terminate' });
132
+ await Promise.all([loggerPromise, consolePromise]);
133
+ channel.close();
134
+ }
135
+ Terminal.setRawMode(false);
136
+ resolve();
137
+ }
138
+ };
@@ -0,0 +1,104 @@
1
+ import { vi } from 'vitest';
2
+ import { join } from 'node:path';
3
+ const mockStaticMethodsOfExportedClasses = (actual) => {
4
+ const mocked = { ...actual };
5
+ for (const exportName in mocked) {
6
+ const exportValue = mocked[exportName];
7
+ if (typeof exportValue === 'function') {
8
+ const exportClass = exportValue;
9
+ for (const staticName of Object.getOwnPropertyNames(exportValue)) {
10
+ const staticValue = exportClass[staticName];
11
+ if (typeof staticValue === 'function') {
12
+ exportClass[staticName] = vi.fn();
13
+ }
14
+ }
15
+ }
16
+ }
17
+ return mocked;
18
+ };
19
+ vi.mock(import('./constants.js'), async (importActual) => {
20
+ const mocked = await importActual();
21
+ return {
22
+ ...mocked,
23
+ __developmentMode: false
24
+ };
25
+ });
26
+ export const __unregisterExitAsyncTask = vi.fn();
27
+ export let __lastRegisteredExitAsyncTask;
28
+ vi.mock(import('./Exit.js'), async (importActual) => {
29
+ const mocked = mockStaticMethodsOfExportedClasses(await importActual());
30
+ const { Exit } = mocked;
31
+ vi.mocked(Exit.registerAsyncTask).mockImplementation((task) => {
32
+ __lastRegisteredExitAsyncTask = task;
33
+ return { [Symbol.dispose]: __unregisterExitAsyncTask };
34
+ });
35
+ return mocked;
36
+ });
37
+ vi.mock(import('./FileSystem.js'), async (importActual) => {
38
+ const mocked = mockStaticMethodsOfExportedClasses(await importActual());
39
+ const { FileSystem } = mocked;
40
+ const writeStream = {
41
+ write: vi.fn().mockImplementation((_, callback) => callback()),
42
+ end: vi.fn()
43
+ };
44
+ vi.mocked(FileSystem.createWriteStream).mockReturnValue(writeStream);
45
+ const readStream = {};
46
+ vi.mocked(FileSystem.createReadStream).mockReturnValue(readStream);
47
+ return mocked;
48
+ });
49
+ vi.mock(import('./Host.js'), async (importActual) => mockStaticMethodsOfExportedClasses(await importActual()));
50
+ vi.mock(import('./Http.js'), async (importActual) => mockStaticMethodsOfExportedClasses(await importActual()));
51
+ const logger = {
52
+ start: vi.fn(),
53
+ debug: vi.fn(),
54
+ info: vi.fn(),
55
+ warn: vi.fn(),
56
+ error: vi.fn(),
57
+ fatal: vi.fn(),
58
+ stop: vi.fn()
59
+ };
60
+ vi.mock(import('./logger.js'), () => ({ logger }));
61
+ vi.mock(import('./Path.js'), async (importActual) => {
62
+ const mocked = mockStaticMethodsOfExportedClasses(await importActual());
63
+ const { Path } = mocked;
64
+ vi.mocked(Path.isAbsolute).mockImplementation((path) => path.startsWith('/'));
65
+ vi.mocked(Path.join).mockImplementation((...arguments_) => join(...arguments_).replaceAll('\\', '/'));
66
+ return mocked;
67
+ });
68
+ vi.mock(import('./Process.js'), async (importActual) => mockStaticMethodsOfExportedClasses(await importActual()));
69
+ export let __lastTerminalRawModeCallback = false;
70
+ vi.mock(import('./Terminal.js'), async (importActual) => {
71
+ const actual = await importActual();
72
+ const originalstripVTControlCharacters = actual.Terminal.stripVTControlCharacters;
73
+ const mocked = mockStaticMethodsOfExportedClasses(actual);
74
+ const { Terminal } = mocked;
75
+ vi.mocked(Terminal.setRawMode).mockImplementation((callback) => {
76
+ __lastTerminalRawModeCallback = callback;
77
+ });
78
+ vi.mocked(Terminal.stripVTControlCharacters).mockImplementation(originalstripVTControlCharacters);
79
+ return mocked;
80
+ });
81
+ vi.mock(import('./Thread.js'), async (importActual) => {
82
+ const mocked = mockStaticMethodsOfExportedClasses(await importActual());
83
+ const { Thread } = mocked;
84
+ const channel = {
85
+ postMessage: vi.fn().mockImplementation((data) => {
86
+ channel.onmessage?.({ data });
87
+ }),
88
+ onmessage: undefined,
89
+ close: vi.fn()
90
+ };
91
+ vi.mocked(Thread.createBroadcastChannel).mockReturnValue(channel);
92
+ const eventTarget = new EventTarget();
93
+ const worker = eventTarget;
94
+ Object.assign(worker, {
95
+ on: eventTarget.addEventListener.bind(eventTarget),
96
+ postMessage: vi.fn()
97
+ });
98
+ vi.mocked(Thread.createWorker).mockReturnValue(worker);
99
+ return mocked;
100
+ });
101
+ vi.mock(import('./version.js'), () => ({
102
+ version: vi.fn().mockResolvedValue('ui5-test-runner@1.2.3')
103
+ }));
104
+ vi.mock(import('./ZLib.js'), async (importActual) => mockStaticMethodsOfExportedClasses(await importActual()));
@@ -0,0 +1,8 @@
1
+ import { FileSystem } from './FileSystem.js';
2
+ import { __sourcesRoot } from './constants.js';
3
+ import { Path } from './Path.js';
4
+ import { memoize } from '../utils/shared/memoize.js';
5
+ export const version = memoize(async () => {
6
+ const { name, version } = JSON.parse(await FileSystem.readFile(Path.join(__sourcesRoot, '../package.json'), 'utf8'));
7
+ return `${name}@${version}`;
8
+ });
@@ -0,0 +1,21 @@
1
+ import { workerData } from 'node:worker_threads';
2
+ import { logger } from './logger.js';
3
+ const main = async () => {
4
+ const { path, data } = workerData;
5
+ logger.debug({ source: 'thread', message: `Worker starting...`, data: { path, data } });
6
+ const { workerMain } = (await import(path));
7
+ try {
8
+ await workerMain(data);
9
+ }
10
+ catch (error) {
11
+ logger.fatal({ source: 'thread', message: `An error occurred`, error, data: { path } });
12
+ }
13
+ finally {
14
+ logger.debug({
15
+ source: 'thread',
16
+ message: `Main completed (it does not mean the worker is offline)`,
17
+ data: { path }
18
+ });
19
+ }
20
+ };
21
+ void main();