From fc1a372e53caa02618d2f99030049954872cae7a Mon Sep 17 00:00:00 2001 From: egavr Date: Fri, 30 Jun 2017 13:34:09 +0300 Subject: [PATCH] feat: provide the ability to modify retries count from plugins --- lib/runner/mocha-runner/retry-mocha-runner.js | 25 +++-- .../runner/mocha-runner/retry-mocha-runner.js | 96 ++++++++++++++++++- 2 files changed, 111 insertions(+), 10 deletions(-) diff --git a/lib/runner/mocha-runner/retry-mocha-runner.js b/lib/runner/mocha-runner/retry-mocha-runner.js index 5f20dee4a..e9524c407 100644 --- a/lib/runner/mocha-runner/retry-mocha-runner.js +++ b/lib/runner/mocha-runner/retry-mocha-runner.js @@ -13,7 +13,8 @@ module.exports = class RetryMochaRunner extends EventEmitter { super(); this._mocha = mocha; - this._retriesLeft = config.retry; + this._config = config; + this._retriesPerformed = 0; this._handleFailEvent(mocha, RunnerEvents.TEST_FAIL); this._handleErrorEvent(mocha, RunnerEvents.ERROR); @@ -24,7 +25,7 @@ module.exports = class RetryMochaRunner extends EventEmitter { } _handleFail(event, failed) { - if (!this._retriesLeft) { + if (!this._hasRetriesLeft()) { this.emit(event, failed); return; } @@ -46,24 +47,32 @@ module.exports = class RetryMochaRunner extends EventEmitter { } _emitRetry(failed) { + this._shouldRetry = true; this.emit(RunnerEvents.RETRY, _.extend(failed, {retriesLeft: this._retriesLeft - 1})); } run() { + this._shouldRetry = false; + return this._mocha.run() - .then((stats) => Boolean(stats.failed) && this._retry()); + .then(() => this._retry()); } _retry() { - if (this._hasRetriesLeft()) { - --this._retriesLeft; - - this._mocha.reinit(); - return this.run(); + if (!this._shouldRetry) { + return; } + + ++this._retriesPerformed; + this._mocha.reinit(); + return this.run(); } _hasRetriesLeft() { return this._retriesLeft > 0; } + + get _retriesLeft() { + return this._config.retry - this._retriesPerformed; + } }; diff --git a/test/lib/runner/mocha-runner/retry-mocha-runner.js b/test/lib/runner/mocha-runner/retry-mocha-runner.js index 8a5fcef0d..dacc47cd3 100644 --- a/test/lib/runner/mocha-runner/retry-mocha-runner.js +++ b/test/lib/runner/mocha-runner/retry-mocha-runner.js @@ -35,6 +35,20 @@ describe('mocha-runner/retry-mocha-runner', () => { beforeEach(() => mochaAdapter = createMochaAdapter()); afterEach(() => sandbox.restore()); + describe('constructor', () => { + it('should provide the ability to mutate the passed config', () => { + const config = {retry: 1}; + const retryMochaRunner = createRetryMochaRunner(config); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.TEST_FAIL, createTestStub())); + + config.retry = 0; + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + }); + describe('run', () => { describe('on test fail', () => { it('should emit "TEST_FAIL" event if no retries were set', () => { @@ -49,6 +63,36 @@ describe('mocha-runner/retry-mocha-runner', () => { .then(() => assert.calledOnceWith(onTestFail, test)); }); + it('should not retry if no retries were set', () => { + const retryMochaRunner = createRetryMochaRunner({retry: 0}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.TEST_FAIL, createTestStub())); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + + it('should emit "TEST_FAIL" event if retries count is below zero', () => { + const retryMochaRunner = createRetryMochaRunner({retry: -1}); + const onTestFail = sinon.spy().named('onTestFail'); + const test = createTestStub(); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.TEST_FAIL, test)); + retryMochaRunner.on(RunnerEvents.TEST_FAIL, onTestFail); + + return retryMochaRunner.run() + .then(() => assert.calledOnceWith(onTestFail, test)); + }); + + it('should not retry if retries count is below zero', () => { + const retryMochaRunner = createRetryMochaRunner({retry: -1}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.TEST_FAIL)); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + it('should emit "RETRY" event if retries were set', () => { const retryMochaRunner = createRetryMochaRunner({retry: 1}); const onTestRetry = sinon.spy().named('onTestRetry'); @@ -128,6 +172,15 @@ describe('mocha-runner/retry-mocha-runner', () => { .then(() => assert.calledOnceWith(onErr, 'err')); }); + it('should not retry if no runnable passed', () => { + const retryMochaRunner = createRetryMochaRunner({retry: 1}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err')); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + it('should emit "ERROR" event if a failed runnable does not have a parent', () => { const retryMochaRunner = createRetryMochaRunner({retry: 1}); const onErr = sinon.spy().named('onErr'); @@ -140,6 +193,15 @@ describe('mocha-runner/retry-mocha-runner', () => { .then(() => assert.calledOnceWith(onErr, 'err', runnable)); }); + it('should not retry if a failed runnable does not have a parent', () => { + const retryMochaRunner = createRetryMochaRunner({retry: 1}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', createRunnableStub())); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + it('should emit "ERROR" event if no retries were set', () => { const retryMochaRunner = createRetryMochaRunner({retry: 0}); const onErr = sinon.spy().named('onErr'); @@ -152,6 +214,36 @@ describe('mocha-runner/retry-mocha-runner', () => { .then(() => assert.calledOnceWith(onErr, 'err', runnable)); }); + it('should not retry if no retries were set', () => { + const retryMochaRunner = createRetryMochaRunner({retry: 0}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', createTestStub({}))); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + + it('should emit "ERROR" event if retries count is below zero', () => { + const retryMochaRunner = createRetryMochaRunner({retry: -1}); + const onErr = sinon.spy().named('onErr'); + const runnable = createTestStub({}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', runnable)); + retryMochaRunner.on(RunnerEvents.ERROR, onErr); + + return retryMochaRunner.run() + .then(() => assert.calledOnceWith(onErr, 'err', runnable)); + }); + + it('should not retry if retries count is below zero', () => { + const retryMochaRunner = createRetryMochaRunner({retry: -1}); + + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', createTestStub({}))); + + return retryMochaRunner.run() + .then(() => assert.calledOnce(mochaAdapter.run)); + }); + it('should emit "RETRY" event if retries were set', () => { const retryMochaRunner = createRetryMochaRunner({retry: 1}); const onRetry = sinon.spy().named('onRetry'); @@ -202,7 +294,7 @@ describe('mocha-runner/retry-mocha-runner', () => { it('should submit for retry a failed mocha', () => { const retryMochaRunner = createRetryMochaRunner({retry: 1}); - mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR)); + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', createRunnableStub({}))); return retryMochaRunner.run() .then(() => assert.calledTwice(mochaAdapter.run)); @@ -211,7 +303,7 @@ describe('mocha-runner/retry-mocha-runner', () => { it('should reinit mocha before a retry', () => { const retryMochaRunner = createRetryMochaRunner({retry: 1}); - mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR)); + mochaAdapter.run.callsFake(emitEvent(RunnerEvents.ERROR, 'err', createRunnableStub({}))); return retryMochaRunner.run() .then(() => assert.callOrder(mochaAdapter.reinit, mochaAdapter.run));