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.
- package/README.md +3 -2
- package/dist/Npm.js +80 -0
- package/dist/browsers/IBrowser.js +1 -0
- package/dist/browsers/factory.js +9 -0
- package/dist/browsers/puppeteer.js +158 -0
- package/dist/cli.js +20 -0
- package/dist/configuration/CommandLine.js +112 -0
- package/dist/configuration/Configuration.js +1 -0
- package/dist/configuration/ConfigurationValidator.js +79 -0
- package/dist/configuration/Option.js +1 -0
- package/dist/configuration/OptionValidationError.js +15 -0
- package/dist/configuration/indexedOptions.js +13 -0
- package/dist/configuration/options.js +191 -0
- package/dist/configuration/validators/OptionValidator.js +1 -0
- package/dist/configuration/validators/boolean.js +15 -0
- package/dist/configuration/validators/browser.js +11 -0
- package/dist/configuration/validators/fsEntry.js +70 -0
- package/dist/configuration/validators/index.js +20 -0
- package/dist/configuration/validators/integer.js +10 -0
- package/dist/configuration/validators/percent.js +17 -0
- package/dist/configuration/validators/regexp.js +20 -0
- package/dist/configuration/validators/string.js +7 -0
- package/dist/configuration/validators/timeout.js +24 -0
- package/dist/configuration/validators/url.js +8 -0
- package/dist/modes/ModeFunction.js +1 -0
- package/dist/modes/Modes.js +9 -0
- package/dist/modes/execute.js +27 -0
- package/dist/modes/help.js +3 -0
- package/dist/modes/log/ILogStorage.js +1 -0
- package/dist/modes/log/LogMetrics.js +9 -0
- package/dist/modes/log/LogReader.js +37 -0
- package/dist/modes/log/LogStorage.js +68 -0
- package/dist/modes/log/REserve.js +101 -0
- package/dist/modes/log/index.js +58 -0
- package/dist/modes/test/REserve.js +31 -0
- package/dist/modes/test/agent.js +8 -0
- package/dist/modes/test/browser.js +37 -0
- package/dist/modes/test/index.js +66 -0
- package/dist/modes/test/pageTask.js +145 -0
- package/dist/modes/test/report.js +3 -0
- package/dist/modes/test/server.js +109 -0
- package/dist/modes/version.js +11 -0
- package/dist/platform/Exit.js +139 -0
- package/dist/platform/FileSystem.js +13 -0
- package/dist/platform/Host.js +10 -0
- package/dist/platform/Http.js +38 -0
- package/dist/platform/Path.js +5 -0
- package/dist/platform/Process.js +133 -0
- package/dist/platform/Terminal.js +47 -0
- package/dist/platform/Thread.js +43 -0
- package/dist/platform/ZLib.js +7 -0
- package/dist/platform/assert.js +17 -0
- package/dist/platform/constants.js +5 -0
- package/dist/platform/environment.js +28 -0
- package/dist/platform/index.js +13 -0
- package/dist/platform/logger/ILogger.js +1 -0
- package/dist/platform/logger/allCompressed.js +54 -0
- package/dist/platform/logger/compress.js +277 -0
- package/dist/platform/logger/output/BaseLoggerOutput.js +158 -0
- package/dist/platform/logger/output/InteractiveLoggerOutput.js +102 -0
- package/dist/platform/logger/output/StaticLoggerOutput.js +32 -0
- package/dist/platform/logger/output/factory.js +10 -0
- package/dist/platform/logger/output.js +58 -0
- package/dist/platform/logger/proxy.js +6 -0
- package/dist/platform/logger/toInternalLogAttributes.js +22 -0
- package/dist/platform/logger/types.js +7 -0
- package/dist/platform/logger.js +138 -0
- package/dist/platform/mock.js +104 -0
- package/dist/platform/version.js +8 -0
- package/dist/platform/workerBootstrap.js +21 -0
- package/dist/reports/html.js +46 -0
- package/dist/types/AgentState.js +1 -0
- package/dist/types/CommonTestReportFormat.js +50 -0
- package/dist/types/IError.js +1 -0
- package/dist/types/IUserInterfaceController.js +1 -0
- package/dist/types/typeUtilities.js +1 -0
- package/dist/ui/agent.js +3 -0
- package/dist/ui/html-report.js +2 -0
- package/dist/ui/lib.js +1 -0
- package/dist/ui/log-viewer.js +2 -0
- package/dist/utils/node/Folder.js +28 -0
- package/dist/utils/node/FramedStreamReader.js +86 -0
- package/dist/utils/node/FramedStreamWriter.js +27 -0
- package/dist/utils/shared/ProgressBar.js +43 -0
- package/dist/utils/shared/TestReportBuilder.js +48 -0
- package/dist/utils/shared/memoize.js +19 -0
- package/dist/utils/shared/object.js +8 -0
- package/dist/utils/shared/parallelize.js +59 -0
- package/dist/utils/shared/string.js +23 -0
- package/dist/utils/shared/toIError.js +17 -0
- package/package.json +73 -50
- package/.releaserc +0 -5
- package/index.js +0 -175
- package/jest.config.json +0 -31
- package/src/add-test-pages.js +0 -67
- package/src/batch.js +0 -214
- package/src/browsers.js +0 -319
- package/src/capabilities/index.js +0 -204
- package/src/capabilities/tests/basic/iframe.html +0 -8
- package/src/capabilities/tests/basic/index.html +0 -12
- package/src/capabilities/tests/basic/index.js +0 -20
- package/src/capabilities/tests/basic/ui5.html +0 -24
- package/src/capabilities/tests/dynamic-include/index.js +0 -21
- package/src/capabilities/tests/dynamic-include/mix.html +0 -11
- package/src/capabilities/tests/dynamic-include/one.html +0 -11
- package/src/capabilities/tests/dynamic-include/post.js +0 -3
- package/src/capabilities/tests/dynamic-include/test.js +0 -1
- package/src/capabilities/tests/dynamic-include/two.html +0 -11
- package/src/capabilities/tests/index.js +0 -16
- package/src/capabilities/tests/local-storage/index.html +0 -16
- package/src/capabilities/tests/local-storage/index.js +0 -21
- package/src/capabilities/tests/screenshot/index.html +0 -23
- package/src/capabilities/tests/screenshot/index.js +0 -24
- package/src/capabilities/tests/scripts/coverage.html +0 -32
- package/src/capabilities/tests/scripts/iframe.html +0 -18
- package/src/capabilities/tests/scripts/index.js +0 -59
- package/src/capabilities/tests/scripts/qunit.html +0 -22
- package/src/capabilities/tests/scripts/testsuite.html +0 -10
- package/src/capabilities/tests/scripts/testsuite.js +0 -8
- package/src/capabilities/tests/timeout/index.html +0 -21
- package/src/capabilities/tests/timeout/index.js +0 -19
- package/src/capabilities/tests/traces/index.html +0 -18
- package/src/capabilities/tests/traces/index.js +0 -81
- package/src/capabilities/tests/ui5/focus.html +0 -89
- package/src/capabilities/tests/ui5/index.js +0 -39
- package/src/capabilities/tests/ui5/language.html +0 -50
- package/src/capabilities/tests/ui5/timezone.html +0 -27
- package/src/clean.js +0 -22
- package/src/cors.js +0 -21
- package/src/coverage.js +0 -384
- package/src/csv-reader.js +0 -36
- package/src/csv-writer.js +0 -55
- package/src/defaults/.nycrc.json +0 -4
- package/src/defaults/browser.js +0 -217
- package/src/defaults/happy-dom.js +0 -123
- package/src/defaults/jsdom/compatibility.js +0 -163
- package/src/defaults/jsdom/debug.js +0 -23
- package/src/defaults/jsdom/resource-loader.js +0 -44
- package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +0 -39
- package/src/defaults/jsdom.js +0 -95
- package/src/defaults/json-report.js +0 -36
- package/src/defaults/junit-xml-report.js +0 -90
- package/src/defaults/playwright.js +0 -142
- package/src/defaults/puppeteer.js +0 -124
- package/src/defaults/report/common.js +0 -38
- package/src/defaults/report/decompress.js +0 -19
- package/src/defaults/report/default.html +0 -99
- package/src/defaults/report/main.js +0 -69
- package/src/defaults/report/progress.js +0 -60
- package/src/defaults/report/styles.css +0 -66
- package/src/defaults/report.js +0 -91
- package/src/defaults/scan-ui5.js +0 -26
- package/src/defaults/selenium-webdriver/chrome.js +0 -39
- package/src/defaults/selenium-webdriver/edge.js +0 -24
- package/src/defaults/selenium-webdriver/firefox.js +0 -30
- package/src/defaults/selenium-webdriver.js +0 -129
- package/src/defaults/text-report.js +0 -108
- package/src/defaults/webdriverio.js +0 -80
- package/src/end.js +0 -62
- package/src/endpoints.js +0 -219
- package/src/error.js +0 -54
- package/src/get-job-progress.js +0 -78
- package/src/handle.js +0 -43
- package/src/if.js +0 -10
- package/src/inject/jest2qunit.js +0 -289
- package/src/inject/opa-iframe-coverage.js +0 -22
- package/src/inject/post.js +0 -141
- package/src/inject/qunit-hooks.js +0 -107
- package/src/inject/qunit-redirect.js +0 -65
- package/src/inject/ui5-coverage.js +0 -33
- package/src/job-mode.js +0 -65
- package/src/job.js +0 -493
- package/src/npm.js +0 -136
- package/src/options.js +0 -95
- package/src/output.js +0 -739
- package/src/parallelize.js +0 -63
- package/src/qunit-hooks.js +0 -219
- package/src/report.js +0 -89
- package/src/reserve.js +0 -25
- package/src/start.js +0 -133
- package/src/symbols.js +0 -8
- package/src/tests.js +0 -183
- package/src/timeout.js +0 -53
- package/src/tools.js +0 -179
- package/src/ui5.js +0 -199
- 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,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,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();
|