185 lines
5.6 KiB
JavaScript
185 lines
5.6 KiB
JavaScript
/**
|
|
* Tests for conforming Eventable implementation
|
|
*
|
|
* Copyright (C) 2014 Mike Gerwitz
|
|
*
|
|
* This file is part of jsTonic.
|
|
*
|
|
* jstonic is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* A conforming implementation must pass each of these tests. See the
|
|
* Eventable interface itself for additional requirements and documentation.
|
|
*
|
|
* To use these tests, invoke the exported function with a constructor
|
|
* function returning an instance of the SUT.
|
|
*/
|
|
|
|
let Class = require( 'easejs' ).Class,
|
|
Eventable = require( '../../' ).event.Eventable,
|
|
expect = require( 'chai' ).expect,
|
|
types = require( '../../' ).util.types;
|
|
|
|
let _fvoid = () => {},
|
|
_ev = 'ev',
|
|
|
|
_nonstr = types.typevals.nonstr,
|
|
_nonf = types.typevals.nonfunc;
|
|
|
|
// data passed to ctor to prepare for test
|
|
let _events = [ _ev ];
|
|
|
|
|
|
/**
|
|
* Invoke conformance tests
|
|
*
|
|
* CTOR will be passed an array of events that the conformance tests will
|
|
* make use of, allowing them to be registered if needed (for validation
|
|
* assertions).
|
|
*
|
|
* This test case will ideally be invoked after the top-level `describe` of
|
|
* the SUT test case so that the output reads as "SUT, conforming to
|
|
* Eventable, ...".
|
|
*
|
|
* @param {function(Array.<string>)} ctor SUT constructor function
|
|
*
|
|
* @return {undefined}
|
|
*/
|
|
module.exports = ctor =>
|
|
{
|
|
// shorthand for SUT construction
|
|
let meta_ctor = () => ctor( _events );
|
|
|
|
/**
|
|
* #on and #addListener are one and the same, but detecting that they
|
|
* alias each other is not reliable for various reasons; instead, we'll
|
|
* run the tests on both methods.
|
|
*/
|
|
describe( 'conforming to Eventable', () =>
|
|
{
|
|
/**
|
|
* As a general-purpose library, we would not want to restrict
|
|
* developers to a specific event implementation (for example, maybe the
|
|
* user would prefer to use Node.js' event system, or transparently
|
|
* integrate with libraries/systems that use it). Together with ease.js'
|
|
* interop support, this makes such a case trivial.
|
|
*/
|
|
it( 'implements Eventable', () =>
|
|
expect( Class.isA( Eventable, meta_ctor() ) ).to.be.true );
|
|
|
|
[ 'on', 'addListener' ].forEach( x =>
|
|
_onTests( meta_ctor, x ) );
|
|
|
|
_rmTests( meta_ctor );
|
|
} );
|
|
};
|
|
|
|
|
|
function _commonTests( ctor, method )
|
|
{
|
|
/**
|
|
* We must test both inputs at once since we cannot otherwise say
|
|
* with confidence which parameter is non-conforming.
|
|
*/
|
|
it( 'accepts string event id with listener function', () =>
|
|
expect( () => ctor()[ method ]( _ev, _fvoid ) )
|
|
.to.not.throw( Error ) );
|
|
|
|
|
|
/**
|
|
* A TypeError should be thrown when the event id is empty. The
|
|
* rationale behind this is that the event id may be dynamically
|
|
* determined at runtime, or may otherwise be stored in a variable,
|
|
* which probably is not supposed to be empty; if it is, this could
|
|
* represent a logic error (such as an incomplete conditional or
|
|
* table lookup failure). More likely, this would result in
|
|
* `undefined`, which is covered in below tests.
|
|
*
|
|
* Implementations wishing to use an empty string to indicate "all
|
|
* events" could instead use, for example, `*` (motivated by shell
|
|
* globbing).
|
|
*/
|
|
it( 'does not accept an empty event id', () =>
|
|
{
|
|
expect( () => ctor()[ method ]( '', _fvoid ) )
|
|
.to.throw( TypeError );
|
|
} );
|
|
|
|
|
|
/**
|
|
* All event identifiers should be strings; this provides
|
|
* consistency between all implementations and helps to weed out
|
|
* runtime bugs (see the "empty event id" test for examples).
|
|
*/
|
|
it( 'does not accept non-string event ids', () =>
|
|
{
|
|
_nonstr.forEach( badev =>
|
|
{
|
|
expect( () => ctor()[ method ]( badev, _fvoid ) )
|
|
.to.throw( TypeError );
|
|
} );
|
|
} );
|
|
|
|
|
|
/**
|
|
* Same rationale as the event string argument.
|
|
*/
|
|
it( 'does not accept non-function listeners', () =>
|
|
{
|
|
_nonf.forEach( badf =>
|
|
{
|
|
expect( () => ctor()[ method ]( _ev, badf ) )
|
|
.to.throw( TypeError );
|
|
} );
|
|
} );
|
|
|
|
|
|
/**
|
|
* When hooking multiple events on a single object, it is convenient
|
|
* to be able to do so concisely without having to repeat the name
|
|
* of the object reference.
|
|
*
|
|
* Since exceptions are used to indicate error conditions, there is
|
|
* also no other useful value to return that would not break
|
|
* encapsulation.
|
|
*/
|
|
it( 'returns self for method chaining', () =>
|
|
{
|
|
let inst = ctor();
|
|
|
|
expect( inst[ method ]( _ev, _fvoid ) )
|
|
.to.equal( inst );
|
|
} );
|
|
}
|
|
|
|
|
|
function _onTests( ctor, on )
|
|
{
|
|
/**
|
|
* Note that by using _fvoid, we are implicitly testing the requirement
|
|
* that the listener shall not be required to declare any number of
|
|
* parameters (because we defined no parameters).
|
|
*/
|
|
describe( `#${on}`, () =>
|
|
{
|
|
_commonTests( ctor, on );
|
|
} );
|
|
}
|
|
|
|
|
|
function _rmTests( ctor )
|
|
{
|
|
_commonTests( ctor, 'removeListener' );
|
|
}
|
|
|