testem 3.19.0 → 3.19.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.
- package/lib/api.js +1 -1
- package/lib/app.js +42 -49
- package/lib/browser_launcher.js +5 -6
- package/lib/config.js +57 -44
- package/lib/launcher.js +7 -11
- package/lib/log.js +92 -0
- package/lib/process-ctl.js +4 -4
- package/lib/reporters/dev/index.js +2 -1
- package/lib/reporters/dev/split_log_panel.js +1 -1
- package/lib/runners/browser_test_runner.js +12 -7
- package/lib/runners/hook_runner.js +13 -11
- package/lib/runners/process_test_runner.js +4 -5
- package/lib/runners/tap_process_test_runner.js +5 -6
- package/lib/runners/to-result.js +1 -1
- package/lib/server/index.js +31 -30
- package/lib/tap_consumer.js +1 -1
- package/lib/utils/browser-args.js +1 -1
- package/lib/utils/esbuild-browser-polyfills.js +46 -0
- package/lib/utils/esbuild-buffer-inject.js +6 -0
- package/lib/utils/fileutils.js +4 -4
- package/lib/utils/known-browsers.js +12 -11
- package/lib/utils/process.js +3 -4
- package/lib/utils/promises.js +130 -0
- package/lib/utils/report-file.js +1 -3
- package/lib/utils/reporter.js +3 -3
- package/lib/utils/run-timeout.js +4 -4
- package/lib/utils/signal-listeners.js +9 -7
- package/package.json +11 -12
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
const log = require('
|
|
3
|
+
const log = require('../log');
|
|
4
4
|
const BrowserTapConsumer = require('../browser_tap_consumer');
|
|
5
|
-
const Bluebird = require('bluebird');
|
|
6
|
-
|
|
7
5
|
const LogEntry = require('../utils/log-entry');
|
|
6
|
+
const { asCallback } = require('../utils/promises');
|
|
8
7
|
const toResult = require('./to-result');
|
|
9
8
|
|
|
10
9
|
module.exports = class BrowserTestRunner {
|
|
@@ -33,7 +32,7 @@ module.exports = class BrowserTestRunner {
|
|
|
33
32
|
this.finished = false;
|
|
34
33
|
this.pending = true;
|
|
35
34
|
|
|
36
|
-
return new
|
|
35
|
+
return new Promise(resolve => {
|
|
37
36
|
this.onFinish = resolve;
|
|
38
37
|
|
|
39
38
|
if (this.socket) {
|
|
@@ -48,19 +47,19 @@ module.exports = class BrowserTestRunner {
|
|
|
48
47
|
this.onProcessError(err);
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
|
-
}).asCallback(onFinish);
|
|
50
|
+
}).then(...asCallback(onFinish));
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
stop(cb) {
|
|
55
54
|
if (this.socket) {
|
|
56
55
|
this.socket.emit('stop-run');
|
|
57
56
|
}
|
|
58
|
-
return
|
|
57
|
+
return Promise.resolve().then(...asCallback(cb));
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
exit() {
|
|
62
61
|
if (!this.process) {
|
|
63
|
-
return
|
|
62
|
+
return Promise.resolve();
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
// mark that somebody (generally app.js) has requested that the
|
|
@@ -119,6 +118,12 @@ module.exports = class BrowserTestRunner {
|
|
|
119
118
|
socket.on('test-metadata', this.onTestMetadata.bind(this));
|
|
120
119
|
socket.on('top-level-error', this.onGlobalError.bind(this));
|
|
121
120
|
|
|
121
|
+
socket.on('tap-debug', function(data) {
|
|
122
|
+
if (process.env.TESTEM_VERBOSE) {
|
|
123
|
+
log.verbose('tap-debug', data && data.msg);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
122
127
|
socket.on('browser-console', function(/* ...args */) {
|
|
123
128
|
let args = Array.prototype.slice.call(arguments);
|
|
124
129
|
let type = args.shift();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const log = require('
|
|
3
|
+
const { fromCallback, disposer } = require('../utils/promises');
|
|
4
|
+
const log = require('../log');
|
|
5
5
|
|
|
6
6
|
const template = require('../utils/strutils').template;
|
|
7
7
|
const ProcessCtl = require('../process-ctl');
|
|
@@ -11,13 +11,13 @@ module.exports = class HookRunner {
|
|
|
11
11
|
this.config = config;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
run(hook, data
|
|
14
|
+
run(hook, data) {
|
|
15
15
|
let hookCfg = this.config.get(hook);
|
|
16
16
|
if (!hookCfg) {
|
|
17
|
-
return
|
|
17
|
+
return Promise.resolve();
|
|
18
18
|
}
|
|
19
19
|
if (typeof hookCfg === 'function') {
|
|
20
|
-
return
|
|
20
|
+
return fromCallback(callback => {
|
|
21
21
|
hookCfg(this.config, data, callback);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
@@ -57,7 +57,7 @@ module.exports = class HookRunner {
|
|
|
57
57
|
hookProcessPromise = this.processCtl.spawn(exe, args, options);
|
|
58
58
|
command = exe + ' ' + args.join(' ');
|
|
59
59
|
} else {
|
|
60
|
-
return
|
|
60
|
+
return Promise.reject(new Error('No command or exe/args specified for hook ' + hook));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
waitForTextTimeout = waitForTextTimeout || 10000;
|
|
@@ -65,7 +65,7 @@ module.exports = class HookRunner {
|
|
|
65
65
|
|
|
66
66
|
let exited = false;
|
|
67
67
|
let finished;
|
|
68
|
-
let p = new
|
|
68
|
+
let p = new Promise((resolve, reject) => {
|
|
69
69
|
finished = (err, stdout, stderr) => {
|
|
70
70
|
if (exited) {
|
|
71
71
|
return;
|
|
@@ -109,7 +109,7 @@ module.exports = class HookRunner {
|
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
return p
|
|
112
|
+
return p;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
varsubParams() {
|
|
@@ -135,17 +135,19 @@ module.exports = class HookRunner {
|
|
|
135
135
|
return this.hookProcess.kill();
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
return
|
|
138
|
+
return Promise.resolve();
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
static with(config, hook, data) {
|
|
142
142
|
let hookRunner = new HookRunner(config);
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
let runPromise = hookRunner.run(hook, data).catch(err => {
|
|
145
145
|
log.error(hook + ' error', err);
|
|
146
146
|
|
|
147
147
|
throw err;
|
|
148
|
-
})
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return disposer(runPromise, () => hookRunner.stop());
|
|
149
151
|
}
|
|
150
152
|
};
|
|
151
153
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
var Bluebird = require('bluebird');
|
|
3
|
-
|
|
4
2
|
var toResult = require('./to-result');
|
|
3
|
+
const { asCallback } = require('../utils/promises');
|
|
5
4
|
|
|
6
5
|
class ProcessTestRunner {
|
|
7
6
|
constructor(launcher, reporter) {
|
|
@@ -15,7 +14,7 @@ class ProcessTestRunner {
|
|
|
15
14
|
this.onStart();
|
|
16
15
|
this.finished = false;
|
|
17
16
|
|
|
18
|
-
return new
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
19
18
|
this.onFinish = resolve;
|
|
20
19
|
this.launcher.start().then(testProcess => {
|
|
21
20
|
this.process = testProcess;
|
|
@@ -23,12 +22,12 @@ class ProcessTestRunner {
|
|
|
23
22
|
this.process.once('processError', this.onProcessError.bind(this));
|
|
24
23
|
return Promise.resolve(testProcess);
|
|
25
24
|
}).catch(reject);
|
|
26
|
-
}).asCallback(onFinish);
|
|
25
|
+
}).then(...asCallback(onFinish));
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
exit() {
|
|
30
29
|
if (!this.process) {
|
|
31
|
-
return
|
|
30
|
+
return Promise.resolve();
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
return this.process.kill();
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
var TapConsumer = require('../tap_consumer');
|
|
4
|
-
var log = require('
|
|
5
|
-
var Bluebird = require('bluebird');
|
|
6
|
-
|
|
4
|
+
var log = require('../log');
|
|
7
5
|
var toResult = require('./to-result');
|
|
6
|
+
const { asCallback } = require('../utils/promises');
|
|
8
7
|
|
|
9
8
|
class TapProcessTestRunner {
|
|
10
9
|
constructor(launcher, reporter) {
|
|
@@ -23,19 +22,19 @@ class TapProcessTestRunner {
|
|
|
23
22
|
this.tapConsumer.on('test-result', this.onTestResult.bind(this));
|
|
24
23
|
this.tapConsumer.on('all-test-results', this.onAllTestResults.bind(this));
|
|
25
24
|
|
|
26
|
-
return new
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
27
26
|
this.onFinish = resolve;
|
|
28
27
|
this.launcher.start().then(tapProcess => {
|
|
29
28
|
this.process = tapProcess;
|
|
30
29
|
this.process.once('processError', this.onProcessError.bind(this));
|
|
31
30
|
this.process.process.stdout.pipe(this.tapConsumer.stream);
|
|
32
31
|
}).catch(reject);
|
|
33
|
-
}).asCallback(onFinish);
|
|
32
|
+
}).then(...asCallback(onFinish));
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
exit() {
|
|
37
36
|
if (!this.process) {
|
|
38
|
-
return
|
|
37
|
+
return Promise.resolve();
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
return this.process.kill();
|
package/lib/runners/to-result.js
CHANGED
package/lib/server/index.js
CHANGED
|
@@ -14,15 +14,13 @@ var socketIO = require('socket.io');
|
|
|
14
14
|
var compression = require('compression');
|
|
15
15
|
var fs = require('fs');
|
|
16
16
|
var path = require('path');
|
|
17
|
-
var log = require('
|
|
17
|
+
var log = require('../log');
|
|
18
18
|
var EventEmitter = require('events').EventEmitter;
|
|
19
19
|
var Mustache = require('consolidate').mustache;
|
|
20
20
|
var http = require('http');
|
|
21
21
|
var https = require('https');
|
|
22
22
|
var httpProxy = require('http-proxy');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
var readFileAsync = Bluebird.promisify(fs.readFile);
|
|
23
|
+
const { fromCallback, each } = require('../utils/promises');
|
|
26
24
|
|
|
27
25
|
class Server extends EventEmitter {
|
|
28
26
|
constructor(config) {
|
|
@@ -37,7 +35,7 @@ class Server extends EventEmitter {
|
|
|
37
35
|
this.nextSocketId = 0;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
start(
|
|
38
|
+
start() {
|
|
41
39
|
this.createExpress();
|
|
42
40
|
|
|
43
41
|
// Start the server!
|
|
@@ -50,7 +48,7 @@ class Server extends EventEmitter {
|
|
|
50
48
|
});
|
|
51
49
|
});
|
|
52
50
|
|
|
53
|
-
return new
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
54
52
|
this.server.on('listening', () => {
|
|
55
53
|
this.config.set('port', this.server.address().port);
|
|
56
54
|
resolve();
|
|
@@ -63,23 +61,23 @@ class Server extends EventEmitter {
|
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
this.server.listen(this.config.get('port'));
|
|
66
|
-
})
|
|
64
|
+
});
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
stop(
|
|
67
|
+
stop() {
|
|
70
68
|
if (this.server && !this.stopped) {
|
|
71
69
|
this.stopped = true;
|
|
72
70
|
|
|
73
|
-
return
|
|
71
|
+
return fromCallback(closeCallback => {
|
|
74
72
|
this.server.close(closeCallback);
|
|
75
73
|
|
|
76
74
|
// Destroy all open sockets
|
|
77
75
|
for (var socketId in this.sockets) {
|
|
78
76
|
this.sockets[socketId].destroy();
|
|
79
77
|
}
|
|
80
|
-
})
|
|
78
|
+
});
|
|
81
79
|
} else {
|
|
82
|
-
return
|
|
80
|
+
return Promise.resolve();
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
|
|
@@ -281,11 +279,13 @@ class Server extends EventEmitter {
|
|
|
281
279
|
var url = base + test_page;
|
|
282
280
|
res.redirect(url);
|
|
283
281
|
} else {
|
|
284
|
-
config.getServeFiles(
|
|
285
|
-
|
|
286
|
-
this.renderRunnerPage(
|
|
282
|
+
Promise.all([config.getServeFiles(), config.getFooterScripts()])
|
|
283
|
+
.then(([files, footerScripts]) => {
|
|
284
|
+
this.renderRunnerPage(null, files, footerScripts, res);
|
|
285
|
+
})
|
|
286
|
+
.catch(err => {
|
|
287
|
+
this.renderRunnerPage(err, [], [], res);
|
|
287
288
|
});
|
|
288
|
-
});
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -314,11 +314,11 @@ class Server extends EventEmitter {
|
|
|
314
314
|
'mocha_adapter.js',
|
|
315
315
|
'testem_client.js'
|
|
316
316
|
];
|
|
317
|
-
|
|
317
|
+
each(files, file => {
|
|
318
318
|
if (file.indexOf(path.sep) === -1) {
|
|
319
319
|
file = `${__dirname}/../../public/testem/${file}`;
|
|
320
320
|
}
|
|
321
|
-
return
|
|
321
|
+
return fs.promises.readFile(file).then(data => {
|
|
322
322
|
res.write(`\n//============== ${path.basename(file)} ==================\n\n`);
|
|
323
323
|
res.write(data);
|
|
324
324
|
}).catch(err => {
|
|
@@ -376,20 +376,21 @@ class Server extends EventEmitter {
|
|
|
376
376
|
this.emit('file-requested', filePath);
|
|
377
377
|
});
|
|
378
378
|
} else {
|
|
379
|
-
fs.stat(filePath
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
});
|
|
389
|
-
} else {
|
|
379
|
+
fs.promises.stat(filePath)
|
|
380
|
+
.then(stat => {
|
|
381
|
+
this.emit('file-requested', filePath);
|
|
382
|
+
if (stat.isDirectory()) {
|
|
383
|
+
return fs.promises.readdir(filePath).then(files => {
|
|
384
|
+
var dirListingPage = `${__dirname}/../../views/directorylisting.mustache`;
|
|
385
|
+
res.render(dirListingPage, { files: files });
|
|
386
|
+
});
|
|
387
|
+
}
|
|
390
388
|
res.sendFile(filePath);
|
|
391
|
-
}
|
|
392
|
-
|
|
389
|
+
})
|
|
390
|
+
.catch(() => {
|
|
391
|
+
this.emit('file-requested', filePath);
|
|
392
|
+
res.sendFile(filePath);
|
|
393
|
+
});
|
|
393
394
|
}
|
|
394
395
|
}
|
|
395
396
|
|
package/lib/tap_consumer.js
CHANGED
|
@@ -4,7 +4,7 @@ const yaml = require('js-yaml');
|
|
|
4
4
|
const extend = Object.assign;
|
|
5
5
|
const EventEmitter = require('events').EventEmitter;
|
|
6
6
|
const TapParser = require('tap-parser');
|
|
7
|
-
const log = require('
|
|
7
|
+
const log = require('./log');
|
|
8
8
|
|
|
9
9
|
module.exports = class TapConsumer extends EventEmitter {
|
|
10
10
|
constructor() {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared esbuild config for bundling Node.js-dependent test code to run in a
|
|
3
|
+
* browser via testem.
|
|
4
|
+
*
|
|
5
|
+
* esbuild's `@esbuild-plugins/node-modules-polyfill` stores the `process`
|
|
6
|
+
* polyfill as a scoped variable inside the IIFE it emits, but CJS module
|
|
7
|
+
* factories (wrapped in `__commonJS`) reference `process`, `__dirname`, and
|
|
8
|
+
* `setImmediate` as free globals — causing ReferenceErrors at runtime.
|
|
9
|
+
*
|
|
10
|
+
* These two options fix that:
|
|
11
|
+
* - `banner.js`: sets `globalThis.process`, `globalThis.setImmediate`, and
|
|
12
|
+
* `globalThis.Buffer` before the bundle executes, so CJS factories find
|
|
13
|
+
* them as globals.
|
|
14
|
+
* - `define`: replaces `__dirname` / `__filename` literals, which esbuild
|
|
15
|
+
* does not inject when targeting the browser platform.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var banner = '(function(){' +
|
|
19
|
+
'if(typeof process==="undefined"){' +
|
|
20
|
+
'var p={env:{},browser:true,platform:"browser",' +
|
|
21
|
+
'nextTick:function(fn){Promise.resolve().then(fn);},' +
|
|
22
|
+
'on:function(){},once:function(){},off:function(){},' +
|
|
23
|
+
'emit:function(){},removeListener:function(){},' +
|
|
24
|
+
'removeAllListeners:function(){},addListener:function(){},' +
|
|
25
|
+
'binding:function(){throw new Error("process.binding not supported");},' +
|
|
26
|
+
'cwd:function(){return "/";},' +
|
|
27
|
+
'exit:function(){}' +
|
|
28
|
+
'};' +
|
|
29
|
+
'if(typeof globalThis!=="undefined")globalThis.process=p;' +
|
|
30
|
+
'else if(typeof window!=="undefined")window.process=p;' +
|
|
31
|
+
'}' +
|
|
32
|
+
'if(typeof setImmediate==="undefined"){' +
|
|
33
|
+
'var si=function(fn){Promise.resolve().then(fn);};' +
|
|
34
|
+
'if(typeof globalThis!=="undefined")globalThis.setImmediate=si;' +
|
|
35
|
+
'else if(typeof window!=="undefined")window.setImmediate=si;' +
|
|
36
|
+
'}' +
|
|
37
|
+
'})();';
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
banner: { js: banner },
|
|
41
|
+
bufferInject: require.resolve('./esbuild-buffer-inject.js'),
|
|
42
|
+
define: {
|
|
43
|
+
__dirname: '""',
|
|
44
|
+
__filename: '""'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// esbuild inject shim: exposes Buffer as a global for browser bundles.
|
|
2
|
+
// The @esbuild-plugins/node-modules-polyfill plugin resolves `require('buffer')`
|
|
3
|
+
// to a browser polyfill, but does not assign it to globalThis automatically.
|
|
4
|
+
import { Buffer } from 'buffer';
|
|
5
|
+
if (typeof globalThis !== 'undefined') { globalThis.Buffer = Buffer; }
|
|
6
|
+
else if (typeof window !== 'undefined') { window.Buffer = Buffer; }
|
package/lib/utils/fileutils.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const execa = require('execa').execa;
|
|
5
|
-
const
|
|
6
|
-
const log = require('
|
|
5
|
+
const { promisify } = require('util');
|
|
6
|
+
const log = require('../log');
|
|
7
7
|
|
|
8
|
-
const fsStatAsync =
|
|
8
|
+
const fsStatAsync = promisify(fs.stat);
|
|
9
9
|
|
|
10
10
|
const isWin = require('./is-win')();
|
|
11
11
|
|
|
12
12
|
exports.fileExists = function fileExists(path) {
|
|
13
|
-
return fsStatAsync(path).then(stat => stat.isFile()).
|
|
13
|
+
return fsStatAsync(path).then(stat => stat.isFile()).catch(() => false);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
exports.executableExists = function executableExists(exe, options) {
|
|
@@ -95,24 +95,25 @@ function firefoxSetup(browserTmpDir, config, done) {
|
|
|
95
95
|
let perfJS = path.join(browserTmpDir, 'user.js');
|
|
96
96
|
|
|
97
97
|
if (config.get('firefox_user_js')) {
|
|
98
|
-
return fs.readFile(config.get('firefox_user_js')
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
fs.writeFile(perfJS, content, done);
|
|
104
|
-
});
|
|
98
|
+
return fs.promises.readFile(config.get('firefox_user_js'))
|
|
99
|
+
.then(content => fs.promises.writeFile(perfJS, content))
|
|
100
|
+
.then(() => done(null), done);
|
|
105
101
|
}
|
|
106
102
|
|
|
107
|
-
// using user.js to suppress the check default browser popup
|
|
108
|
-
//
|
|
103
|
+
// using user.js to suppress the check default browser popup,
|
|
104
|
+
// the welcome start page, and first-run helper installation prompts
|
|
109
105
|
let prefs = [
|
|
110
106
|
'user_pref("browser.shell.checkDefaultBrowser", false);',
|
|
111
107
|
'user_pref("browser.cache.disk.smart_size.first_run", false);',
|
|
112
108
|
'user_pref("datareporting.policy.dataSubmissionEnabled", false);',
|
|
113
|
-
'user_pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1481830156314");'
|
|
109
|
+
'user_pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1481830156314");',
|
|
110
|
+
'user_pref("app.update.auto", false);',
|
|
111
|
+
'user_pref("app.update.enabled", false);',
|
|
112
|
+
'user_pref("browser.EULA.override", true);',
|
|
113
|
+
'user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);'
|
|
114
114
|
];
|
|
115
|
-
fs.writeFile(perfJS, prefs.join(os.EOL)
|
|
115
|
+
fs.promises.writeFile(perfJS, prefs.join(os.EOL))
|
|
116
|
+
.then(() => done(null), done);
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
// Return the catalogue of the browsers that Testem supports for the platform. Each 'browser object'
|
package/lib/utils/process.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const execa = require('execa').execa;
|
|
2
|
-
const log = require('
|
|
3
|
-
const Bluebird = require('bluebird');
|
|
2
|
+
const log = require('../log');
|
|
4
3
|
const EventEmitter = require('events').EventEmitter;
|
|
5
4
|
|
|
6
5
|
const isWin = require('./is-win')();
|
|
@@ -95,14 +94,14 @@ module.exports = class Process extends EventEmitter {
|
|
|
95
94
|
if (!this.process) {
|
|
96
95
|
log.info('Process ' + this.name + ' already killed.');
|
|
97
96
|
|
|
98
|
-
return
|
|
97
|
+
return Promise.resolve(this.exitCode);
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
sig = sig || 'SIGTERM';
|
|
102
101
|
|
|
103
102
|
let self = this;
|
|
104
103
|
|
|
105
|
-
return new
|
|
104
|
+
return new Promise(resolve => {
|
|
106
105
|
self.process.once('close', (code, sig) => {
|
|
107
106
|
self.process = null;
|
|
108
107
|
if (self._killTimer) {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
function fromCallback(fn) {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
fn((err, result) => {
|
|
4
|
+
if (err) {
|
|
5
|
+
reject(err);
|
|
6
|
+
} else {
|
|
7
|
+
resolve(result);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function filter(arr, predicate) {
|
|
14
|
+
const results = await Promise.all(arr.map(item => predicate(item)));
|
|
15
|
+
return arr.filter((_, i) => results[i]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function reduce(arr, reducer, initialValue) {
|
|
19
|
+
return arr.reduce(
|
|
20
|
+
(acc, item) => acc.then(a => reducer(a, item)),
|
|
21
|
+
Promise.resolve(initialValue)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function each(arr, fn) {
|
|
26
|
+
return arr.reduce(
|
|
27
|
+
(p, item) => p.then(() => fn(item)),
|
|
28
|
+
Promise.resolve()
|
|
29
|
+
).then(() => arr);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class Disposer {
|
|
33
|
+
constructor(promise, cleanup) {
|
|
34
|
+
this.promise = Promise.resolve(promise);
|
|
35
|
+
this.cleanup = cleanup;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function disposer(promise, cleanup) {
|
|
40
|
+
return new Disposer(promise, cleanup);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function using(resource, fn) {
|
|
44
|
+
const isDisposer = resource instanceof Disposer;
|
|
45
|
+
const value = await (isDisposer ? resource.promise : resource);
|
|
46
|
+
|
|
47
|
+
let result, rejected = false, error;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
result = await fn(value);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
rejected = true;
|
|
53
|
+
error = err;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isDisposer) {
|
|
57
|
+
const inspection = {
|
|
58
|
+
isRejected: () => rejected,
|
|
59
|
+
reason: () => error,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await resource.cleanup(value, inspection);
|
|
64
|
+
} catch (cleanupErr) {
|
|
65
|
+
if (!rejected) {
|
|
66
|
+
// Callback succeeded but cleanup failed — surface cleanup error
|
|
67
|
+
throw cleanupErr;
|
|
68
|
+
}
|
|
69
|
+
// Both callback and cleanup failed — suppress cleanup error, re-throw original below
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (rejected) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function mapLimit(arr, concurrency, fn) {
|
|
80
|
+
if (!isFinite(concurrency) || concurrency >= arr.length) {
|
|
81
|
+
return Promise.all(arr.map(fn));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let index = 0;
|
|
85
|
+
let results = new Array(arr.length);
|
|
86
|
+
|
|
87
|
+
function next() {
|
|
88
|
+
let i = index++;
|
|
89
|
+
if (i >= arr.length) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return Promise.resolve(fn(arr[i])).then(result => {
|
|
94
|
+
results[i] = result;
|
|
95
|
+
return next();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let workers = [];
|
|
100
|
+
for (let w = 0; w < Math.min(concurrency, arr.length); w++) {
|
|
101
|
+
workers.push(next());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Promise.all(workers).then(() => results);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function retry(fn, { max_tries = 3 } = {}) {
|
|
108
|
+
let lastErr;
|
|
109
|
+
for (let i = 0; i < max_tries; i++) {
|
|
110
|
+
try {
|
|
111
|
+
return await fn();
|
|
112
|
+
} catch (e) {
|
|
113
|
+
lastErr = e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw lastErr;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function delay(ms) {
|
|
120
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function asCallback(cb) {
|
|
124
|
+
return [
|
|
125
|
+
result => { if (cb) { cb(null, result); } return result; },
|
|
126
|
+
err => { if (cb) { cb(err); } throw err; }
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { fromCallback, filter, reduce, each, mapLimit, retry, delay, asCallback, Disposer, disposer, using };
|
package/lib/utils/report-file.js
CHANGED
|
@@ -4,8 +4,6 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const mkdirp = require('mkdirp');
|
|
6
6
|
const PassThrough = require('stream').PassThrough;
|
|
7
|
-
const Bluebird = require('bluebird');
|
|
8
|
-
|
|
9
7
|
module.exports = class ReportFile {
|
|
10
8
|
constructor(reportFile) {
|
|
11
9
|
this.file = reportFile;
|
|
@@ -27,7 +25,7 @@ module.exports = class ReportFile {
|
|
|
27
25
|
this.outputStream.on('end', finish);
|
|
28
26
|
this.outputStream.on('error', finish);
|
|
29
27
|
|
|
30
|
-
this.closePromise = new
|
|
28
|
+
this.closePromise = new Promise((resolve, reject) => {
|
|
31
29
|
this.outputStream.on('finish', resolve);
|
|
32
30
|
this.outputStream.on('error', reject);
|
|
33
31
|
});
|
package/lib/utils/reporter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const log = require('
|
|
3
|
+
const { disposer } = require('./promises');
|
|
4
|
+
const log = require('../log');
|
|
5
5
|
|
|
6
6
|
const reporters = require('../reporters');
|
|
7
7
|
const isa = require('./isa');
|
|
@@ -107,7 +107,7 @@ class Reporter {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
Reporter.with = (app, stdout, path) =>
|
|
110
|
+
Reporter.with = (app, stdout, path) => disposer(Promise.resolve(new Reporter(app, stdout, path)), (reporter, promise) => {
|
|
111
111
|
if (promise.isRejected()) {
|
|
112
112
|
let err = promise.reason();
|
|
113
113
|
|