diff --git a/lib/gemini.js b/lib/gemini.js index 4933791d8..526d6893c 100644 --- a/lib/gemini.js +++ b/lib/gemini.js @@ -88,7 +88,8 @@ module.exports = class Gemini extends PassthroughEmitter { const stats = new RunnerStats(runner); - return runner.run(suiteCollection).thenReturn(stats.get()); + return runner.run(suiteCollection) + .then(() => stats.get()); }); } diff --git a/lib/stats.js b/lib/stats.js index e18e7b0a6..da629402b 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -2,39 +2,39 @@ const _ = require('lodash'); -const Events = require('./constants/events'); +const RunnerEvents = require('./constants/events'); -/** - * @constructor - * @param {Runner} [runner] - */ -module.exports = function(runner) { - const data = {}; +module.exports = class Stats { + constructor(runner) { + this._tests = {}; - const add = (key) => { - data[key] = data[key] || 0; - data[key]++; - }; - - if (runner) { runner - .on(Events.BEGIN_STATE, _.partial(add, 'total')) - .on(Events.SKIP_STATE, () => { - add('total'); - add('skipped'); - }) - .on(Events.WARNING, _.partial(add, 'warned')) - .on(Events.ERROR, _.partial(add, 'errored')) - - .on(Events.UPDATE_RESULT, (res) => { - add(res && res.updated ? 'updated' : 'passed'); - }) - .on(Events.TEST_RESULT, (res) => { - add(res && res.equal ? 'passed' : 'failed'); - }); + .on(RunnerEvents.SKIP_STATE, (test) => this._add('skipped', test)) + .on(RunnerEvents.WARNING, (test) => this._add('warned', test)) + .on(RunnerEvents.ERROR, (test) => this._add('errored', test)) + .on(RunnerEvents.UPDATE_RESULT, (test) => this._add(test.updated ? 'updated' : 'passed', test)) + .on(RunnerEvents.TEST_RESULT, (test) => this._add(test.equal ? 'passed' : 'failed', test)); + } + + _add(type, test) { + this._tests[type] = this._tests[type] || []; + + this._tests = _.mapValues(this._tests, (fullNames) => _.without(fullNames, test.state.fullName)); + + this._tests[type].push(test.state.fullName); } - this.add = add; + get(type) { + const stats = this._getStats(); + + return type === undefined ? stats : stats[type]; + } - this.get = (name) => name === undefined ? data : data[name]; + _getStats() { + return _(this._tests) + .mapValues((tests) => tests.length) + .thru((stats) => _.extend(stats, {total: _(stats).values().sum()})) + .omitBy((count) => !count) + .value(); + } }; diff --git a/test/unit/stats.js b/test/unit/stats.js index a75b28e4d..4bc5ebf9d 100644 --- a/test/unit/stats.js +++ b/test/unit/stats.js @@ -1,100 +1,95 @@ 'use strict'; const EventEmitter = require('events').EventEmitter; -const inherit = require('inherit'); +const RunnerEvents = require('../../lib/constants/events'); +const Stats = require('../../lib/stats'); -const Stats = require('lib/stats'); -const Events = require('lib/constants/events'); - -describe('stats', () => { +describe('Stats', () => { let stats; + let runner; + + const stubTest = (opts) => { + opts = opts || {}; + + return { + state: {fullName: opts.name || 'default-name'}, + updated: opts.updated, + equal: opts.equal + }; + }; beforeEach(() => { - stats = new Stats(); + runner = new EventEmitter(); + stats = new Stats(runner); }); - it('should return \'undefined\' before adding keys', () => { - assert.isUndefined(stats.get('counter')); - }); + it('should count skipped tests', () => { + runner.emit(RunnerEvents.SKIP_STATE, stubTest()); - it('should allow to add new key', () => { - stats.add('counter'); - assert.equal(stats.get('counter'), 1); + assert.equal(stats.get('skipped'), 1); }); - it('should increment existing keys', () => { - stats.add('counter'); - stats.add('counter'); - assert.equal(stats.get('counter'), 2); - }); + it('should count warned tests', () => { + runner.emit(RunnerEvents.WARNING, stubTest()); - it('should return all full stat', () => { - stats.add('counter'); - stats.add('counter'); - stats.add('counter'); - stats.add('counter2'); - stats.add('counter3'); - stats.add('counter3'); - assert.deepEqual(stats.get(), { - counter: 3, - counter2: 1, - counter3: 2 - }); + assert.equal(stats.get('warned'), 1); }); -}); -describe('stats listener', () => { - let stats; - let Runner = inherit(EventEmitter, {}); - let runner; + it('should count errored tests', () => { + runner.emit(RunnerEvents.ERROR, stubTest()); - beforeEach(() => { - runner = new Runner(); - stats = new Stats(runner); + assert.equal(stats.get('errored'), 1); }); - it('should return undefined before triggering any events', () => { - assert.isUndefined(stats.get('total')); - }); + it('should count updated tests', () => { + runner.emit(RunnerEvents.UPDATE_RESULT, stubTest({updated: true})); - it('should count on beginState event', () => { - runner.emit(Events.BEGIN_STATE); - assert.equal(stats.get('total'), 1); + assert.equal(stats.get('updated'), 1); }); - it('should count on skipState event', () => { - runner.emit(Events.SKIP_STATE); - assert.equal(stats.get('total'), 1); - assert.equal(stats.get('skipped'), 1); - }); + it('should count passed tests on "UPDATE_RESULT" event', () => { + runner.emit(RunnerEvents.UPDATE_RESULT, stubTest({updated: false})); - it('should count on warning event', () => { - runner.emit(Events.WARNING); - assert.equal(stats.get('warned'), 1); + assert.equal(stats.get('passed'), 1); }); - it('should count on error event', () => { - runner.emit(Events.ERROR); - assert.equal(stats.get('errored'), 1); - }); + it('should count failed tests on "TEST_RESULT" event', () => { + runner.emit(RunnerEvents.TEST_RESULT, stubTest({equal: false})); - it('should count on updated images', () => { - runner.emit(Events.UPDATE_RESULT, {updated: true}); - assert.equal(stats.get('updated'), 1); + assert.equal(stats.get('failed'), 1); }); - it('should count on passed images', () => { - runner.emit(Events.UPDATE_RESULT, {updated: false}); + it('should count passed tests on "TEST_RESULT" event', () => { + runner.emit(RunnerEvents.TEST_RESULT, stubTest({equal: true})); + assert.equal(stats.get('passed'), 1); }); - it('should count test passed', () => { - runner.emit(Events.TEST_RESULT, {equal: true}); - assert.equal(stats.get('passed'), 1); + it('should count total test count', () => { + runner.emit(RunnerEvents.TEST_RESULT, stubTest({equal: false, name: 'first'})); + runner.emit(RunnerEvents.TEST_RESULT, stubTest({equal: true, name: 'second'})); + + assert.equal(stats.get('total'), 2); }); - it('should count test failed', () => { - runner.emit(Events.TEST_RESULT, {equal: false}); - assert.equal(stats.get('failed'), 1); + it('should get full stat', () => { + runner.emit(RunnerEvents.ERROR, stubTest({name: 'first'})); + runner.emit(RunnerEvents.TEST_RESULT, stubTest({equal: true, name: 'second'})); + + assert.deepEqual(stats.get(), { + total: 2, + errored: 1, + passed: 1 + }); + }); + + it('should handle cases when several events were emitted for the same test', () => { + runner.emit(RunnerEvents.SKIP_STATE, stubTest({name: 'some-state'})); + runner.emit(RunnerEvents.ERROR, stubTest({name: 'some-state'})); + + assert.deepEqual(stats.get(), { + total: 1, + errored: 1 + }); }); });