event-mixer
Version:
another event emitter
323 lines (256 loc) • 12.2 kB
JavaScript
var { mixer, handler } = require('../index');
describe('The application', function()
{
var cap = (s) => s.slice(0,1).toUpperCase() + s.slice(1);
var events = 'creation birth graduation first-job marriage children retirement death eternity'.split(/\s+/);
var fnRegs = 'on off'.split(/\s+/);
beforeEach(function()
{
this.listeners =
{
celebrate: () => null,
cry: () => ({ prevent: true }),
dance: () => 'Dance!',
laugh: () => ({ stop: true, prevent: true }),
party: () => ({ stop: true }),
rejoice: () => ({ remove: true }),
sing: () => 'Sing!',
smile: () => 'Smile!',
think: () => 'Think!'
};
// spies for tests
Object.keys(this.listeners).forEach(k => spyOn(this.listeners, k).and.callThrough());
// mixer target, and emitter elements
this.target = {}; this.emitter = {}; this.mixer = mixer(this.target, this.emitter);
// handlers for all events
this.handlers = events.reduce((a,e) => { a[e] = this.mixer.create(e, { name: e }); return a; }, {});
// registration
this.handlers.creation.append([this.listeners.rejoice, this.listeners.celebrate]);
this.handlers.birth.append([this.listeners.laugh, this.listeners.dance]);
this.handlers.graduation.append([this.listeners.party, this.listeners.smile]);
this.handlers['first-job'].append(this.listeners.smile);
this.handlers.marriage.append(this.listeners.dance);
this.handlers.children.append(this.listeners.dance);
this.handlers.death.append(this.listeners.cry);
this.handlers.retirement.append(this.listeners.laugh);
this.handlers.eternity.append([this.listeners.dance, this.listeners.sing, this.listeners.rejoice]);
});
it('must expose event manager creation as a function', function()
{
expect(mixer).toBeFunction();
});
it('must expose event handler creation as a function', function()
{
expect(handler).toBeFunction();
});
describe('event mixer', function()
{
fnRegs.forEach(f =>
{
it(`defines .${f}() register method on its target`, function()
{
expect(this.target[f]).toBeFunction();
});
it('returns target object from the .${f}() register method', function()
{
expect(this.target[f]('first-job', this.listeners.think)).toBe(this.target);
});
});
it('defines .fire() on its emitter', function()
{
expect(this.emitter.fire).toBeFunction();
});
it('defines individual listener registration methods on its target', function()
{
events.forEach(e => expect(this.target['on' + cap(e)]).toBeFunction());
});
it('defines individual listener unregistration methods on its target', function()
{
events.forEach(e => expect(this.target['off' + cap(e)]).toBeFunction());
});
it('defines individual event trigger methods on its emitter', function()
{
events.forEach(e => expect(this.emitter['fire' + cap(e)]).toBeFunction());
});
it('acknowledges the presence of an existing event handler', function()
{
events.forEach(e => expect(this.mixer.has(e)).toBe(true));
});
it('acknowledges the absence of a non-existing event', function()
{
expect(this.mixer.has('illness')).toBe(false);
});
it('returns the handler for an existing event', function()
{
events.forEach(e => expect(this.mixer.event(e)).toBeDefined());
});
it('returns nothing for a non-existing event', function()
{
expect(this.mixer.event('faxes')).not.toBeDefined();
});
it('returns event names as string array', function()
{
expect(this.mixer.names()).toBeArrayOfStrings();
});
it('returns original target object', function()
{
expect(this.mixer.target()).toBe(this.target);
});
it('returns original emitter object', function()
{
expect(this.mixer.emitter()).toBe(this.emitter);
});
it('can create new events with the proper register and trigger methods', function()
{
this.mixer.create('road-trip');
expect(this.mixer.event('road-trip')).toBeDefined();
expect(this.target['onRoad-trip']).toBeFunction();
expect(this.target['offRoad-trip']).toBeFunction();
expect(this.emitter['fireRoad-trip']).toBeFunction();
});
it('can remove existing events and associated register and trigger methods', function()
{
this.mixer.destroy('retirement');
expect(this.mixer.event('retirement')).not.toBeDefined();
expect(this.target['onRetirement']).not.toBeDefined();
expect(this.target['offRetirement']).not.toBeDefined();
expect(this.emitter['fireRetirement']).not.toBeDefined();
});
it('allows listener to be added to a single event from target', function()
{
this.target.on('birth', this.listeners.celebrate);
expect(this.mixer.event('birth').has(this.listeners.celebrate)).toBe(true);
});
it('allows listener to be added to multiple events from target', function()
{
this.target.on(['birth', 'death'], this.listeners.celebrate);
expect(this.mixer.event('birth').has(this.listeners.celebrate)).toBe(true);
expect(this.mixer.event('death').has(this.listeners.celebrate)).toBe(true);
});
it('allows listener to be removed from a single event from target', function()
{
this.target.off('death', this.listeners.cry);
expect(this.mixer.event('death').has(this.listeners.cry)).toBe(false);
});
it('allows listener to be removed from multiple events from target', function()
{
this.target.off(['eternity', 'marriage'], this.listeners.dance);
expect(this.mixer.event('eternity').has(this.listeners.dance)).toBe(false);
expect(this.mixer.event('marriage').has(this.listeners.dance)).toBe(false);
});
it('allows listener to be added through an individual register method on target', function()
{
this.target.onMarriage(this.listeners.celebrate);
expect(this.mixer.event('marriage').has(this.listeners.celebrate)).toBeDefined(true);
});
it('allows listener to be removed through an individual unregister method on target', function()
{
this.target.offCreation(this.listeners.rejoice);
expect(this.mixer.event('creation').has(this.listeners.rejoice)).toBeDefined(false);
});
it('allows emitter to fire events successfully', function()
{
events.forEach(e => { this.emitter['fire' + cap(e)](); expect(this.mixer.event(e).at(0)).toHaveBeenCalled(); });
});
});
describe('event handler', function()
{
it('acknowledges the presence of an existing listener', function()
{
expect(this.handlers.creation.has(this.listeners.rejoice)).toBe(true);
});
it('acknowledges the absence of a non-existing listener', function()
{
expect(this.handlers.death.has(this.listeners.rejoice)).toBe(false);
});
it('returns an existing listener for a specified index', function()
{
expect(this.handlers.marriage.at(0)).toBeFunction();
});
it('returns null for an index at which no listener exists', function()
{
expect(this.handlers.marriage.at(5)).toBeNull();
});
it('allows a listener to be added', function()
{
this.handlers.marriage.add(this.listeners.celebrate);
expect(this.handlers.marriage.has(this.listeners.celebrate)).toBe(true);
});
it('allows a listener to be removed', function()
{
this.handlers.graduation.remove(this.listeners.party);
expect(this.handlers.graduation.has(this.listeners.party)).toBe(false);
});
it('returns the current number of listeners', function()
{
expect(this.handlers.graduation.count()).toBe(2);
});
it('allows all listeners to be removed', function()
{
this.handlers.graduation.clear();
expect(this.handlers.graduation.count()).toBe(0);
});
it('allows a listener to opt out of notification', function()
{
this.handlers.creation.fire();
this.handlers.creation.fire();
expect(this.listeners.rejoice).toHaveBeenCalledTimes(1);
});
it('allows a listener to stop continued notifications', function()
{
this.handlers.graduation.fire();
expect(this.listeners.smile).toHaveBeenCalledTimes(0);
});
it('allows a listener to request prevention of normal event result', function()
{
expect(this.handlers.creation.fire()).toBe(true);
expect(this.handlers.death.fire()).toBe(false);
});
it('allows a listener to request prevention when stopping notifications', function()
{
expect(this.handlers.birth.fire()).toBe(false);
});
it('allows a listener to stop notifications when requesting prevention', function()
{
this.handlers.birth.fire();
expect(this.listeners.dance).toHaveBeenCalledTimes(0);
});
it('returns the index of an included listener', function()
{
expect(this.handlers.graduation.indexOf(this.listeners.party)).toBeGreaterThanOrEqualTo(0);
expect(this.handlers.graduation.indexOf(this.listeners.cry)).not.toBeGreaterThanOrEqualTo(0);
});
it('allows a listener to be prepended', function()
{
this.handlers.birth.unshift(this.listeners.smile);
expect(this.handlers.birth.indexOf(this.listeners.smile)).toBe(0);
});
it('allows a listener to be appended', function()
{
this.handlers.birth.push(this.listeners.smile);
expect(this.handlers.birth.indexOf(this.listeners.smile)).toBe(this.handlers.birth.count() - 1);
});
it('allows multiple listeners to be prepended', function()
{
this.handlers.children.prepend([this.listeners.smile, this.listeners.rejoice, this.listeners.celebrate]);
expect(this.handlers.children.indexOf(this.listeners.smile)).toBe(0);
expect(this.handlers.children.indexOf(this.listeners.rejoice)).toBe(1);
expect(this.handlers.children.indexOf(this.listeners.celebrate)).toBe(2);
});
it('allows multiple listeners to be appended', function()
{
this.handlers.children.append([this.listeners.smile, this.listeners.rejoice, this.listeners.celebrate]);
expect(this.handlers.children.indexOf(this.listeners.smile)).toBe(this.handlers.children.count() - 3);
expect(this.handlers.children.indexOf(this.listeners.rejoice)).toBe(this.handlers.children.count() - 2);
expect(this.handlers.children.indexOf(this.listeners.celebrate)).toBe(this.handlers.children.count() - 1);
});
it('allows filtering and changing of its listeners', function()
{
var count = this.handlers.eternity.count();
this.handlers.eternity.each(l => l === this.listeners.sing ? null : eo => l(eo));
expect(this.handlers.eternity.count()).toBe(count - 1);
expect(this.handlers.eternity.indexOf(this.listeners.sing)).toBe(-1);
expect(this.handlers.eternity.indexOf(this.listeners.dance)).toBe(-1);
});
});
});