Skip to content
This repository was archived by the owner on Sep 21, 2022. It is now read-only.

Commit da68965

Browse files
author
Alexey Rybakov
committed
feat: introduce hermione.halt method
1 parent 9b067af commit da68965

3 files changed

Lines changed: 90 additions & 3 deletions

File tree

doc/programmatic-api.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ Returns promise that resolve to a stats object with following keys:
203203

204204
Rejects promise if critical error occurred.
205205

206+
## Halting
207+
208+
```js
209+
gemini.halt(error, [timeout=60000ms]);
210+
```
211+
212+
Method for abnormal termination of the test run in case of a terminal error. If process fails to gracefully shutdown in `timeout` milliseconds, it would be forcibly terminated (unless `timeout` is explicitly set to `0`).
213+
206214
## Utilites
207215

208216
* `gemini.getScreenshotPath(suite, stateName, browserId)` — returns path to

lib/gemini.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,29 @@ module.exports = class Gemini extends PassthroughEmitter {
9393
return this._exec(() => this._readTests(paths, options));
9494
}
9595

96+
halt(err, timeout = 60000) {
97+
this._runner.cancel();
98+
99+
this._criticalError = new GeminiError(err);
100+
101+
if (timeout === 0) {
102+
return;
103+
}
104+
105+
setTimeout(() => {
106+
console.error(chalk.red('Forcing shutdown...'));
107+
process.exit(1);
108+
}, timeout).unref();
109+
}
110+
96111
_exec(fn) {
97-
return this._init().then(() => fn());
112+
return this._init()
113+
.then(() => fn())
114+
.finally(() => {
115+
if (this._criticalError) {
116+
throw this._criticalError;
117+
}
118+
});
98119
}
99120

100121
_init() {
@@ -150,7 +171,7 @@ module.exports = class Gemini extends PassthroughEmitter {
150171

151172
temp.init(this.config.system.tempDir);
152173

153-
const runner = Runner.create(this.config, stateProcessor);
174+
const runner = this._runner = Runner.create(this.config, stateProcessor);
154175
const envBrowsers = parseBrowsers(process.env.GEMINI_BROWSERS);
155176
const envSkipBrowsers = parseBrowsers(process.env.GEMINI_SKIP_BROWSERS);
156177

test/unit/gemini.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ describe('gemini', () => {
6565
};
6666

6767
beforeEach(() => {
68-
sandbox.stub(Runner.prototype, 'cancel').returns(Promise.resolve());
68+
sandbox.stub(Runner.prototype, 'cancel');
6969
sandbox.stub(console, 'warn');
70+
sandbox.stub(console, 'error');
7071
sandbox.stub(pluginsLoader, 'load');
7172
sandbox.stub(temp, 'init');
7273
});
@@ -432,6 +433,63 @@ describe('gemini', () => {
432433
});
433434
});
434435

436+
describe('halt', () => {
437+
let gemini;
438+
439+
beforeEach(() => {
440+
sandbox.stub(process, 'exit');
441+
sandbox.stub(Runner.prototype, 'run').callsFake(() => gemini.emitAndWait(Events.START_RUNNER));
442+
443+
gemini = initGemini();
444+
});
445+
446+
it('should throw provided error', () => {
447+
gemini.on(Events.START_RUNNER, () => {
448+
gemini.halt(new Error('test error'));
449+
});
450+
451+
return assert.isRejected(gemini.test(), /Error: test error/);
452+
});
453+
454+
it('should cancel test runner', () => {
455+
gemini.on(Events.START_RUNNER, () => {
456+
gemini.halt(new Error('test error'));
457+
});
458+
459+
return gemini.test()
460+
.catch(() => {
461+
assert.calledOnce(Runner.prototype.cancel);
462+
});
463+
});
464+
465+
describe('shutdown timeout', () => {
466+
it('should force exit if timeout is reached', () => {
467+
gemini.on(Events.START_RUNNER, () => {
468+
gemini.halt(new Error('test error'), 250);
469+
});
470+
471+
return gemini.test()
472+
.catch(() => Promise.delay(300))
473+
.then(() => {
474+
assert.calledWithMatch(console.error, /Forcing shutdown.../);
475+
assert.calledOnceWith(process.exit, 1);
476+
});
477+
});
478+
479+
it('should do nothing if timeout is set to zero', () => {
480+
sandbox.spy(global, 'setTimeout');
481+
gemini.on(Events.START_RUNNER, () => {
482+
gemini.halt(new Error('test error'), 0);
483+
});
484+
485+
return gemini.test()
486+
.catch(() => {
487+
assert.notCalled(global.setTimeout);
488+
});
489+
});
490+
});
491+
});
492+
435493
describe('environment variables', () => {
436494
beforeEach(() => {
437495
sandbox.stub(SuiteCollection.prototype, 'skipBrowsers');

0 commit comments

Comments
 (0)