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

Commit

Permalink
feat: emit async INIT event instead of async plugins load
Browse files Browse the repository at this point in the history
  • Loading branch information
j0tunn committed Nov 29, 2017
1 parent a9c5eed commit b21aa58
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 46 deletions.
4 changes: 3 additions & 1 deletion doc/events.md
@@ -1,6 +1,8 @@
# Gemini events

* `AFTER_TESTS_READ` - emitted after all tests were read (during `run`, `update` or `readTests` call). The event is emitted with 1 argument `data`:
* `INIT` - emitted before any job start (`test`, `update` or `readTests`). If handler returns a promise then job will start only after the promise will be resolved. Emitted only once no matter how many times job will be performed.

* `AFTER_TESTS_READ` - emitted after all tests were read (during `test`, `update` or `readTests` call). The event is emitted with 1 argument `data`:
* `data.suiteCollection` - suite collection with all suites parsed from test files

* `UPDATE_RESULT` — emitted always during update. The event is emitted with 1 argument `result`:
Expand Down
2 changes: 2 additions & 0 deletions lib/constants/events.js
@@ -1,6 +1,8 @@
'use strict';

module.exports = {
INIT: 'init',

AFTER_TESTS_READ: 'afterTestsRead',

START_RUNNER: 'startRunner',
Expand Down
11 changes: 8 additions & 3 deletions lib/gemini.js
Expand Up @@ -46,6 +46,7 @@ module.exports = class Gemini extends PassthroughEmitter {
this.SuiteCollection = SuiteCollection;

setupLog(this.config.system.debug);
this._loadPlugins();
}

getScreenshotPath(suite, stateName, browserId) {
Expand Down Expand Up @@ -89,12 +90,16 @@ module.exports = class Gemini extends PassthroughEmitter {
}

_exec(fn) {
return this._loadPlugins().then(() => fn());
return this._init().then(() => fn());
}

_init() {
this._init = () => Promise.resolve(); // init only once
return this.emitAndWait(Events.INIT);
}

_loadPlugins() {
this._loadPlugins = () => Promise.resolve(); // load plugins only once
return Promise.all(pluginsLoader.load(this, this.config.system.plugins, PREFIX));
pluginsLoader.load(this, this.config.system.plugins, PREFIX);
}

_readTests(paths, options) {
Expand Down
137 changes: 95 additions & 42 deletions test/unit/gemini.js
Expand Up @@ -67,7 +67,7 @@ describe('gemini', () => {
beforeEach(() => {
sandbox.stub(Runner.prototype, 'cancel').returns(Promise.resolve());
sandbox.stub(console, 'warn');
sandbox.stub(pluginsLoader, 'load').returns([]);
sandbox.stub(pluginsLoader, 'load');
sandbox.stub(temp, 'init');
});

Expand Down Expand Up @@ -126,63 +126,36 @@ describe('gemini', () => {
});

describe('load plugins', () => {
it('should load plugins', () => {
return runGeminiTest()
.then(() => assert.calledOnce(pluginsLoader.load));
});
it('should load plugins on construction', () => {
initGemini();

it('should load plugins before reading tests', () => {
return runGeminiTest()
.then(() => assert.callOrder(pluginsLoader.load, testReaderStub));
assert.calledOnce(pluginsLoader.load);
});

it('should load plugins for gemini instance', () => {
const gemini = initGemini();

return gemini.test()
.then(() => assert.calledWith(pluginsLoader.load, gemini));
});

it('should load plugins from config', () => {
return runGeminiTest({plugins: {'some-plugin': true}})
.then(() => assert.calledWith(pluginsLoader.load, sinon.match.any, {'some-plugin': true}));
assert.calledWith(pluginsLoader.load, gemini);
});

it('should load plugins with appropriate prefix', () => {
const prefix = require('../../package').name + '-';
it('should fail on plugin load error', () => {
pluginsLoader.load.throws(new Error('o.O'));

return runGeminiTest()
.then(() => assert.calledWith(pluginsLoader.load, sinon.match.any, sinon.match.any, prefix));
assert.throws(() => initGemini(), /o.O/);
});

it('should wait until plugins loaded', () => {
const afterPluginLoad = sinon.spy();
pluginsLoader.load.callsFake(() => {
return [Promise.delay(20).then(afterPluginLoad)];
});
sandbox.stub(Runner.prototype, 'run').returns(Promise.resolve());
it('should load plugins from config', () => {
initGemini({plugins: {'some-plugin': true}});

return runGeminiTest()
.then(() => assert.callOrder(afterPluginLoad, Runner.prototype.run));
assert.calledWith(pluginsLoader.load, sinon.match.any, {'some-plugin': true});
});

it('should not run tests if plugin failed on load', () => {
const err = new Error('o.O');
pluginsLoader.load.callsFake(() => [Promise.reject(err)]);
sandbox.stub(Runner.prototype, 'run').returns(Promise.resolve());
it('should load plugins with appropriate prefix', () => {
const prefix = require('../../package').name + '-';

const result = runGeminiTest();
initGemini();

return assert.isRejected(result, err)
.then(() => assert.notCalled(Runner.prototype.run));
});

it('should load plugins only once', () => {
const gemini = initGemini();

return gemini.readTests()
.then((collection) => gemini.test(collection))
.then(() => assert.calledOnce(pluginsLoader.load));
assert.calledWith(pluginsLoader.load, sinon.match.any, sinon.match.any, prefix);
});
});

Expand All @@ -199,6 +172,46 @@ describe('gemini', () => {
Config.prototype.getBrowserIds.returns([]);
});

describe('INIT', () => {
it('should emit INIT event on reading', () => {
const onInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, onInit);

return gemini.readTests()
.then(() => assert.calledOnce(onInit));
});

it('should wait INIT handler before reading any test', () => {
const afterInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, () => Promise.delay(10).then(afterInit));

return gemini.readTests()
.then(() => assert.callOrder(afterInit, testReaderStub));
});

it('should fail on INIT handler reject', () => {
gemini = initGemini()
.on(Events.INIT, () => Promise.reject('o.O'));

return assert.isRejected(gemini.readTests(), /o.O/);
});

it('should emit INIT only once', () => {
const onInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, onInit);

return gemini.readTests()
.then(() => gemini.readTests())
.then(() => assert.calledOnce(onInit));
});
});

it('should pass sets from cli to test-reader', () => {
const opts = {
cliOpts: {sets: ['set1']}
Expand Down Expand Up @@ -363,6 +376,46 @@ describe('gemini', () => {
return gemini.test()
.then(() => assert.calledOnceWith(onAfterTestRead, {suiteCollection: sinon.match.instanceOf(SuiteCollection)}));
});

describe('INIT', () => {
it('should emit INIT event on run', () => {
const onInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, onInit);

return gemini.test()
.then(() => assert.calledOnce(onInit));
});

it('should wait INIT handler before run', () => {
const afterInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, () => Promise.delay(10).then(afterInit));

return gemini.test()
.then(() => assert.callOrder(afterInit, Runner.prototype.run));
});

it('should fail on INIT handler reject', () => {
gemini = initGemini()
.on(Events.INIT, () => Promise.reject('o.O'));

return assert.isRejected(gemini.test(), /o.O/);
});

it('should emit INIT only once', () => {
const onInit = sinon.spy();

gemini = initGemini()
.on(Events.INIT, onInit);

return gemini.test()
.then(() => gemini.test())
.then(() => assert.calledOnce(onInit));
});
});
});

describe('environment variables', () => {
Expand Down

0 comments on commit b21aa58

Please sign in to comment.