@brillout/test-e2e 0.5.8 → 0.5.9
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/dist/Logs.d.ts +1 -1
- package/dist/Logs.js +4 -4
- package/dist/TIMEOUTS.d.ts +5 -0
- package/dist/TIMEOUTS.js +9 -0
- package/dist/autoRetry.d.ts +4 -0
- package/dist/autoRetry.js +24 -0
- package/dist/buildTs.js +1 -1
- package/dist/cli.js +1 -0
- package/dist/getCurrentTest.d.ts +28 -7
- package/dist/getCurrentTest.js +28 -1
- package/dist/logTestStatus.js +3 -0
- package/dist/runAll.js +31 -5
- package/dist/runCommandThatTerminates.d.ts +4 -0
- package/dist/runCommandThatTerminates.js +11 -0
- package/dist/setup.d.ts +5 -6
- package/dist/setup.js +97 -332
- package/dist/testScreenshotFixture.js +1 -1
- package/dist/utils/findFiles.js +3 -3
- package/dist/utils/genPromise.d.ts +5 -0
- package/dist/utils/genPromise.js +13 -0
- package/dist/utils/humanizeTime.d.ts +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/runCommand.d.ts +4 -5
- package/dist/utils/runCommand.js +12 -32
- package/dist/utils/runCommandLongRunning.d.ts +20 -0
- package/dist/utils/runCommandLongRunning.js +232 -0
- package/dist/utils/runCommandShortLived.d.ts +6 -0
- package/dist/utils/runCommandShortLived.js +39 -0
- package/package.json +1 -1
package/dist/utils/runCommand.js
CHANGED
|
@@ -2,45 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runCommand = void 0;
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const genPromise_1 = require("./genPromise");
|
|
6
|
+
const humanizeTime_1 = require("./humanizeTime");
|
|
7
|
+
function runCommand(cmd, { timeout, cwd }) {
|
|
8
|
+
const { promise, resolve, reject } = (0, genPromise_1.genPromise)();
|
|
7
9
|
const t = setTimeout(() => {
|
|
8
|
-
|
|
9
|
-
rejectPromise();
|
|
10
|
+
reject(new Error(`Command call \`${cmd}\` (${cwd}) timed out after ${(0, humanizeTime_1.humanizeTime)(timeout)}`));
|
|
10
11
|
}, timeout);
|
|
11
|
-
|
|
12
|
-
(0, child_process_1.exec)(cmd, options, (err, stdout, stderr) => {
|
|
12
|
+
(0, child_process_1.exec)(cmd, { cwd }, (err, _stdout, stderr) => {
|
|
13
13
|
clearTimeout(t);
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (stdout) {
|
|
20
|
-
console.log(stdout);
|
|
21
|
-
}
|
|
22
|
-
if (stderr) {
|
|
23
|
-
console.error(stderr);
|
|
24
|
-
}
|
|
25
|
-
if (err) {
|
|
26
|
-
console.error(err);
|
|
27
|
-
}
|
|
28
|
-
throw new Error(`Command \`${cmd}\` failed [cwd: ${cwd}]`);
|
|
29
|
-
}
|
|
14
|
+
if (stderr) {
|
|
15
|
+
reject(new Error(stderr));
|
|
16
|
+
}
|
|
17
|
+
else if (err) {
|
|
18
|
+
reject(err);
|
|
30
19
|
}
|
|
31
20
|
else {
|
|
32
|
-
|
|
21
|
+
resolve(undefined);
|
|
33
22
|
}
|
|
34
23
|
});
|
|
35
24
|
return promise;
|
|
36
25
|
}
|
|
37
26
|
exports.runCommand = runCommand;
|
|
38
|
-
function genPromise() {
|
|
39
|
-
let resolvePromise;
|
|
40
|
-
let rejectPromise;
|
|
41
|
-
const promise = new Promise((resolve, reject) => {
|
|
42
|
-
resolvePromise = resolve;
|
|
43
|
-
rejectPromise = reject;
|
|
44
|
-
});
|
|
45
|
-
return { promise, resolvePromise, rejectPromise };
|
|
46
|
-
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { runCommandLongRunning };
|
|
2
|
+
declare function runCommandLongRunning({ cmd, cwd, killPort, isReadyLog, isReadyTimeout, onStdout, onStderr, onExit, onTerminationError, terminationTimeout, }: {
|
|
3
|
+
cmd: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
killPort: false | number;
|
|
6
|
+
isReadyLog: string | ((log: string) => boolean);
|
|
7
|
+
isReadyTimeout: number;
|
|
8
|
+
onStdout: (log: string, info: {
|
|
9
|
+
loggedAfterExit: boolean;
|
|
10
|
+
}) => void;
|
|
11
|
+
onStderr: (log: string, info: {
|
|
12
|
+
loggedAfterExit: boolean;
|
|
13
|
+
}) => void;
|
|
14
|
+
onExit: (errMsg?: string) => void;
|
|
15
|
+
onTerminationError: (errMsg: string) => void;
|
|
16
|
+
terminationTimeout: number;
|
|
17
|
+
}): {
|
|
18
|
+
terminate: (force?: true) => Promise<void>;
|
|
19
|
+
isReadyPromise: Promise<void>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runCommandLongRunning = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const strip_ansi_1 = __importDefault(require("strip-ansi"));
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
function runCommandLongRunning({ cmd, cwd, killPort, isReadyLog, isReadyTimeout, onStdout, onStderr, onExit, onTerminationError, terminationTimeout, }) {
|
|
11
|
+
if (killPort)
|
|
12
|
+
killByPort(killPort);
|
|
13
|
+
let onError;
|
|
14
|
+
let onReady;
|
|
15
|
+
let isReady = false;
|
|
16
|
+
const isReadyPromise = new Promise((resolve_, reject_) => {
|
|
17
|
+
onReady = () => {
|
|
18
|
+
clearTimeout(isReadyPromiseTimeout);
|
|
19
|
+
(0, utils_1.assert)(!procExited);
|
|
20
|
+
isReady = true;
|
|
21
|
+
resolve_();
|
|
22
|
+
};
|
|
23
|
+
onError = async (err, alreadyTerminated) => {
|
|
24
|
+
clearTimeout(isReadyPromiseTimeout);
|
|
25
|
+
// We reject before terminating the process in order to preserve log order. (This order is fine because the test runner doesn't exit upon test failure.)
|
|
26
|
+
reject_(err);
|
|
27
|
+
if (!alreadyTerminated) {
|
|
28
|
+
(0, utils_1.assert)(!procExited);
|
|
29
|
+
await terminateProc(true);
|
|
30
|
+
}
|
|
31
|
+
(0, utils_1.assert)(procExited);
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
let isReadyPromiseTimedOut = false;
|
|
35
|
+
const isReadyPromiseTimeout = setTimeout(async () => {
|
|
36
|
+
isReadyPromiseTimedOut = true;
|
|
37
|
+
let errMsg = `Command \`${cmd}\` not ready after ${(0, utils_1.humanizeTime)(isReadyTimeout)}.`;
|
|
38
|
+
if (typeof isReadyLog === 'string') {
|
|
39
|
+
errMsg += ` The stdout of the command did not (yet?) include "${isReadyLog}".`;
|
|
40
|
+
}
|
|
41
|
+
onError(new Error(errMsg));
|
|
42
|
+
}, isReadyTimeout);
|
|
43
|
+
let [command, ...args] = cmd.split(' ');
|
|
44
|
+
let detached = true;
|
|
45
|
+
if ((0, utils_1.isWindows)()) {
|
|
46
|
+
detached = false;
|
|
47
|
+
if (command === 'npm' || command === 'pnpm') {
|
|
48
|
+
command = command + '.cmd';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const proc = (0, child_process_1.spawn)(command, args, { cwd, detached });
|
|
52
|
+
let procExited = false;
|
|
53
|
+
proc.stdin.on('data', async (chunk) => {
|
|
54
|
+
const data = String(chunk);
|
|
55
|
+
onError(new Error(`Command \`${cmd}\` (${cwd}) is invoking stdin which is forbidden. The stdin data: ${data}`));
|
|
56
|
+
});
|
|
57
|
+
proc.stdout.on('data', (chunk) => {
|
|
58
|
+
const log = String(chunk);
|
|
59
|
+
onStdout(log, { loggedAfterExit: procExited });
|
|
60
|
+
{
|
|
61
|
+
const logCleaned = (0, strip_ansi_1.default)(log);
|
|
62
|
+
let isMatch = false;
|
|
63
|
+
if (typeof isReadyLog === 'string') {
|
|
64
|
+
isMatch = logCleaned.includes(isReadyLog);
|
|
65
|
+
}
|
|
66
|
+
else if ((0, utils_1.isCallable)(isReadyLog)) {
|
|
67
|
+
isMatch = isReadyLog(logCleaned);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
(0, utils_1.assert)(false);
|
|
71
|
+
}
|
|
72
|
+
if (isMatch)
|
|
73
|
+
onReady();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
proc.stderr.on('data', async (chunk) => {
|
|
77
|
+
const log = String(chunk);
|
|
78
|
+
onStderr(log, { loggedAfterExit: procExited });
|
|
79
|
+
if (log.includes('EADDRINUSE')) {
|
|
80
|
+
onError(new Error('Port conflict? Port already in use EADDRINUSE.'));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
let exitPromiseResolve;
|
|
84
|
+
const exitPromise = new Promise((r) => (exitPromiseResolve = r));
|
|
85
|
+
proc.on('exit', (code) => {
|
|
86
|
+
procExited = true;
|
|
87
|
+
let errMsg;
|
|
88
|
+
{
|
|
89
|
+
const exitIsExpected = isReady === true || isReadyPromiseTimedOut;
|
|
90
|
+
if (!isSuccessCode(code)) {
|
|
91
|
+
errMsg = `Unexpected termination of command \`${cmd}\` with exit code ${code}`;
|
|
92
|
+
}
|
|
93
|
+
else if (!exitIsExpected) {
|
|
94
|
+
errMsg = `Unexpected premature termination of command \`${cmd}\` (with success exit code ${code})`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// We add errMsg to onExit() because isReadyPromise may have already returned
|
|
98
|
+
onExit(errMsg);
|
|
99
|
+
if (errMsg) {
|
|
100
|
+
onError(new Error(errMsg), true);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
(0, utils_1.assert)(isReady);
|
|
104
|
+
}
|
|
105
|
+
exitPromiseResolve();
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
isReadyPromise,
|
|
109
|
+
async terminate() {
|
|
110
|
+
if (procExited)
|
|
111
|
+
return;
|
|
112
|
+
await terminateProc();
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
async function terminateProc(force) {
|
|
116
|
+
let resolve;
|
|
117
|
+
let reject;
|
|
118
|
+
const promise = new Promise((resolve_, reject_) => {
|
|
119
|
+
resolve = async () => {
|
|
120
|
+
resolve_();
|
|
121
|
+
};
|
|
122
|
+
reject = () => {
|
|
123
|
+
reject_();
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
const terminateTimeout = setTimeout(() => {
|
|
127
|
+
const errMsg = 'Process termination timeout. Cmd: ' + cmd;
|
|
128
|
+
onTerminationError(errMsg);
|
|
129
|
+
/* Don't interrupt the test runner, as the test runner may recover thanks to killByPort() (EDIT: this is actually inaccurate since killByPort() isn't called in CI. Maybe we should reject?)
|
|
130
|
+
reject(new Error(errMsg))
|
|
131
|
+
*/
|
|
132
|
+
resolve();
|
|
133
|
+
}, terminationTimeout);
|
|
134
|
+
(0, utils_1.assert)(proc);
|
|
135
|
+
await stopProcess({
|
|
136
|
+
proc,
|
|
137
|
+
cwd,
|
|
138
|
+
cmd,
|
|
139
|
+
force,
|
|
140
|
+
onTerminationError,
|
|
141
|
+
});
|
|
142
|
+
clearTimeout(terminateTimeout);
|
|
143
|
+
await exitPromise;
|
|
144
|
+
resolve();
|
|
145
|
+
return promise;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.runCommandLongRunning = runCommandLongRunning;
|
|
149
|
+
function stopProcess({ proc, cwd, cmd, force, onTerminationError, }) {
|
|
150
|
+
let resolve;
|
|
151
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
152
|
+
resolve = _resolve;
|
|
153
|
+
});
|
|
154
|
+
const onProcessClose = (code) => {
|
|
155
|
+
if (!isSuccessCode(code)) {
|
|
156
|
+
onTerminationError(`Command \`${cmd}\` (${cwd}) terminated with non-0 error code ${code}`);
|
|
157
|
+
}
|
|
158
|
+
resolve();
|
|
159
|
+
};
|
|
160
|
+
proc.on('close', onProcessClose);
|
|
161
|
+
proc.on('exit', onProcessClose);
|
|
162
|
+
const { pid } = proc;
|
|
163
|
+
(0, utils_1.assert)(pid);
|
|
164
|
+
if ((0, utils_1.isWindows)()) {
|
|
165
|
+
// - https://github.com/nodejs/node/issues/3617#issuecomment-377731194
|
|
166
|
+
// - https://stackoverflow.com/questions/23706055/why-can-i-not-kill-my-child-process-in-nodejs-on-windows/28163919#28163919
|
|
167
|
+
// - taskkill seems to be buggy in many ways:
|
|
168
|
+
// - taskkill seems to make the process exit with code `1`
|
|
169
|
+
// - Because process exits with code `1`, Node.js/npm complains:
|
|
170
|
+
// ```
|
|
171
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr] npm
|
|
172
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr]
|
|
173
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr] ERR!
|
|
174
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr]
|
|
175
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr] code
|
|
176
|
+
// [15:36:10.626][\examples\react][npm run preview][stderr] ELIFECYCLE
|
|
177
|
+
// ```
|
|
178
|
+
// Because stderr isn't empty, runTests() believes that the termination failed.
|
|
179
|
+
// - taskkill sometimes throws:
|
|
180
|
+
// ```
|
|
181
|
+
// ERROR: The process with PID 6052 (child process of PID 3184) could not be terminated.
|
|
182
|
+
// Reason: There is no running instance of the task.
|
|
183
|
+
// ```
|
|
184
|
+
// ```
|
|
185
|
+
// ERROR: The process "6564" not found.
|
|
186
|
+
// ```
|
|
187
|
+
// There doesn't seem to be an option to suppress these errors: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill#parameters
|
|
188
|
+
(0, utils_1.assert)(typeof pid === 'number');
|
|
189
|
+
(0, child_process_1.spawn)('taskkill', ['/pid', String(pid), '/f', '/t'], {
|
|
190
|
+
stdio: [
|
|
191
|
+
'ignore',
|
|
192
|
+
// Ignore following log to avoid polluting non-error logs. (Setting 'inherit' instead of 'ignore' attaches stdout to the root process and not to `proc` which is why the stdout of taskkill isn't intercepted by `proc.stdout.on('data', () => /* ... */)`.)
|
|
193
|
+
// ```
|
|
194
|
+
// SUCCESS: The process with PID 6932 (child process of PID 2744) has been terminated.
|
|
195
|
+
// ```
|
|
196
|
+
'ignore',
|
|
197
|
+
// Ignoring stderr doesn't solve the problem that taskkill makes the process exit with code 1
|
|
198
|
+
'inherit', // stderr
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
(0, utils_1.assert)(typeof pid === 'number');
|
|
204
|
+
const processGroup = -1 * pid;
|
|
205
|
+
const signal = force ? 'SIGKILL' : 'SIGTERM';
|
|
206
|
+
process.kill(processGroup, signal);
|
|
207
|
+
/*
|
|
208
|
+
try {
|
|
209
|
+
process.kill(-proc.pid, signal)
|
|
210
|
+
} catch (err: unknown) {
|
|
211
|
+
// ESRCH: No process or process group can be found corresponding to that specified by pid.
|
|
212
|
+
// => probably means that the process was killed already.
|
|
213
|
+
if (typeof err === 'object' && err !== null && 'code' in err && err['code'] === 'ESRCH') {
|
|
214
|
+
printLog('stdout', '=============== swallowError')
|
|
215
|
+
return
|
|
216
|
+
} else {
|
|
217
|
+
printLog('stdout', '=============== no swallowError')
|
|
218
|
+
throw err
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
*/
|
|
222
|
+
}
|
|
223
|
+
return promise;
|
|
224
|
+
}
|
|
225
|
+
async function killByPort(port) {
|
|
226
|
+
(0, utils_1.assert)((0, utils_1.isLinux)());
|
|
227
|
+
(0, utils_1.assert)(port);
|
|
228
|
+
await (0, utils_1.runCommandShortLived)(`fuser -k ${port}/tcp`, { swallowError: true, timeout: 10 * 1000 });
|
|
229
|
+
}
|
|
230
|
+
function isSuccessCode(code) {
|
|
231
|
+
return code === 0 || code === null || (code === 1 && (0, utils_1.isWindows)());
|
|
232
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runCommandShortLived = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const genPromise_1 = require("./genPromise");
|
|
6
|
+
const humanizeTime_1 = require("./humanizeTime");
|
|
7
|
+
function runCommandShortLived(cmd, { swallowError = false, timeout = 5000, cwd = process.cwd(), } = {}) {
|
|
8
|
+
const { promise, resolve, reject } = (0, genPromise_1.genPromise)();
|
|
9
|
+
const t = setTimeout(() => {
|
|
10
|
+
console.error(`Command call \`${cmd}\` (${cwd}) timed out after ${(0, humanizeTime_1.humanizeTime)(timeout)}`);
|
|
11
|
+
reject();
|
|
12
|
+
}, timeout);
|
|
13
|
+
const options = { cwd };
|
|
14
|
+
(0, child_process_1.exec)(cmd, options, (err, stdout, stderr) => {
|
|
15
|
+
clearTimeout(t);
|
|
16
|
+
if (err || stderr) {
|
|
17
|
+
if (swallowError) {
|
|
18
|
+
resolve('SWALLOWED_ERROR');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
if (stdout) {
|
|
22
|
+
console.log(stdout);
|
|
23
|
+
}
|
|
24
|
+
if (stderr) {
|
|
25
|
+
console.error(stderr);
|
|
26
|
+
}
|
|
27
|
+
if (err) {
|
|
28
|
+
console.error(err);
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Command \`${cmd}\` failed [cwd: ${cwd}]`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
resolve(stdout);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return promise;
|
|
38
|
+
}
|
|
39
|
+
exports.runCommandShortLived = runCommandShortLived;
|