1
0
Fork 0

Converted a number of test cases to new XUnit-style format

perfodd
Mike Gerwitz 2012-05-03 17:21:37 -04:00
parent 0d306b63c8
commit 28bf9e6421
No known key found for this signature in database
GPG Key ID: F22BB8158EE30EAB
5 changed files with 790 additions and 766 deletions

View File

@ -21,91 +21,90 @@
* @author Mike Gerwitz * @author Mike Gerwitz
*/ */
require( 'common' ).testCase(
var common = require( './common' ), {
assert = require( 'assert' ), caseSetUp: function()
{
// SUT this.Sut = this.require( 'FallbackVisibilityObjectFactory' );
FallbackVisibilityObjectFactory =
common.require( 'FallbackVisibilityObjectFactory' ),
// parent of SUT // parent of SUT
VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ), this.VisibilityObjectFactory =
this.require( 'VisibilityObjectFactory' );
sut = FallbackVisibilityObjectFactory(), this.props = this.methods = {
props = methods = {
'public': {}, 'public': {},
'protected': {}, 'protected': {},
'private': {}, 'private': {},
} };
; },
/** /**
* To keep with the spirit of ease.js, we should be able to instantiate * To keep with the spirit of ease.js, we should be able to instantiate
* VisibilityObjectFactory both with and without the 'new' keyword * VisibilityObjectFactory both with and without the 'new' keyword
* *
* Consistency is key with these sorts of things. * Consistency is key with these sorts of things.
*/ */
( function testCanInstantiateWithAndWithoutNewKeyword() 'Can instantiate with and without `new` keyword': function()
{ {
// with 'new' keyword // with 'new' keyword
assert.ok( this.assertOk(
( new FallbackVisibilityObjectFactory() ) ( new this.Sut() ) instanceof this.Sut,
instanceof FallbackVisibilityObjectFactory, "Should be able to instantiate FallbackVisibilityObjectFactory " +
"Should be able to instantiate FallbackVisibilityObjectFactory with " + "with 'new' keyword"
"'new' keyword"
); );
// without 'new' keyword // without 'new' keyword
assert.ok( this.assertOk(
FallbackVisibilityObjectFactory() this.Sut() instanceof this.Sut,
instanceof FallbackVisibilityObjectFactory,
"Should be able to instantiate FallbackVisibilityObjectFactory " + "Should be able to instantiate FallbackVisibilityObjectFactory " +
"without 'new' keyword" "without 'new' keyword"
); );
} )(); },
/** /**
* VisibilityObjectFactory should be part of our prototype chain. * VisibilityObjectFactory should be part of our prototype chain.
*/ */
( function testInheritsFromVisibilityObjectFactory() 'Inherits from visibility object factory': function()
{ {
// check an instance, rather than __proto__, because older engines do not // check an instance, rather than __proto__, because older engines do
// support it // not support it
assert.ok( this.assertOk(
FallbackVisibilityObjectFactory() instanceof VisibilityObjectFactory, this.Sut() instanceof this.VisibilityObjectFactory,
"Fallback should inherit from VisibilityObjectFactory" "Fallback should inherit from VisibilityObjectFactory"
); );
} )(); },
/** /**
* We're falling back because we do not support the private visibility layer (or * We're falling back because we do not support the private visibility layer
* any layers, for that matter). Ensure it's not created. * (or any layers, for that matter). Ensure it's not created.
*/ */
( function testSetupMethodShouldNotAddPrivateLayer() 'Setup method should not add private layer': function()
{ {
var dest = {}, var dest = {},
obj = sut.setup( dest, props, methods ); obj = this.Sut().setup( dest, this.props, this.methods );
assert.strictEqual( dest, obj, this.assertStrictEqual( dest, obj,
"Private visibility layer is not added atop destination" "Private visibility layer is not added atop destination"
); );
} )(); },
( function testCreatingPropertyProxyShouldSimplyReturnSelf() /**
{ * Getters/setters are unsupported (thus the fallback).
*/
'Creating property proxy should simply return self': function()
{
var base = {}, var base = {},
dest = {}; dest = {};
assert.strictEqual( this.assertStrictEqual(
sut.createPropProxy( base, dest, props ), this.Sut().createPropProxy( base, dest, this.props ),
base, base,
"Creating property proxy should simply return original object" "Creating property proxy should simply return original object"
); );
} )(); },
} );

View File

@ -22,44 +22,45 @@
*/ */
var common = require( './common' ), require( 'common' ).testCase(
assert = require( 'assert' ), {
caseSetUp: function()
Sut = common.require( 'MethodWrapperFactory' ) {
; this.Sut = this.require( 'MethodWrapperFactory' );
},
/** /**
* To keep with the spirit of ease.js, we should be able to instantiate * To keep with the spirit of ease.js, we should be able to instantiate
* MethodWrapperFactory both with and without the 'new' keyword * MethodWrapperFactory both with and without the 'new' keyword
* *
* Consistency is key with these sorts of things. * Consistency is key with these sorts of things.
*/ */
( function testCanInstantiateWithAndWithoutNewKeyword() 'Can instantiate with and without new keyword': function()
{ {
// with 'new' keyword // with 'new' keyword
assert.ok( this.assertOk(
( new Sut() ) ( new this.Sut() ) instanceof this.Sut,
instanceof Sut,
"Should be able to instantiate MethodWrapperFactory with " + "Should be able to instantiate MethodWrapperFactory with " +
"'new' keyword" "'new' keyword"
); );
// without 'new' keyword // without 'new' keyword
assert.ok( ( Sut() instanceof Sut ), this.assertOk( ( this.Sut() instanceof this.Sut ),
"Should be able to instantiate MethodWrapperFactory " + "Should be able to instantiate MethodWrapperFactory " +
"without 'new' keyword" "without 'new' keyword"
); );
} )(); },
/** /**
* The factory itself is rather simple. The class should accept a factory * The factory itself is rather simple. The class should accept a factory
* function which should return the wrapped method. * function which should return the wrapped method.
*/ */
( function testProvidedFactoryFunctionIsProperlyCalled() 'Provided factory function is properly called': function()
{ {
var called = false, var _self = this,
called = false,
method = function() {}, method = function() {},
super_method = function() {}, super_method = function() {},
cid = 55, cid = 55,
@ -68,7 +69,7 @@ var common = require( './common' ),
keywords = { 'static': true, 'public': true }, keywords = { 'static': true, 'public': true },
retval = 'foobar'; retval = 'foobar';
var result = Sut( var result = this.Sut(
function( function(
given_method, given_super, given_cid, givenGetInst, given_name, given_method, given_super, given_cid, givenGetInst, given_name,
given_keywords given_keywords
@ -76,27 +77,27 @@ var common = require( './common' ),
{ {
called = true; called = true;
assert.equal( given_method, method, _self.assertEqual( given_method, method,
"Factory method should be provided with method to wrap" "Factory method should be provided with method to wrap"
); );
assert.equal( given_super, super_method, _self.assertEqual( given_super, super_method,
"Factory method should be provided with super method" "Factory method should be provided with super method"
); );
assert.equal( given_cid, cid, _self.assertEqual( given_cid, cid,
"Factory method should be provided with cid" "Factory method should be provided with cid"
); );
assert.equal( givenGetInst, getInst, _self.assertEqual( givenGetInst, getInst,
"Factory method should be provided with proper inst function" "Factory method should be provided with proper inst function"
); );
assert.equal( given_name, name, _self.assertEqual( given_name, name,
"Factory method should be provided with proper method name" "Factory method should be provided with proper method name"
); );
assert.equal( given_keywords, keywords, _self.assertEqual( given_keywords, keywords,
"Factory method should be provided with proper keywords" "Factory method should be provided with proper keywords"
); );
@ -106,12 +107,12 @@ var common = require( './common' ),
// we'll include this in addition to the following assertion (which is // we'll include this in addition to the following assertion (which is
// redundant) to make debugging more clear // redundant) to make debugging more clear
assert.equal( called, true, this.assertEqual( called, true,
"Given factory method should be called" "Given factory method should be called"
); );
assert.equal( result, retval, this.assertEqual( result, retval,
"Should return value from factory function" "Should return value from factory function"
); );
} )(); },
} );

View File

@ -21,20 +21,41 @@
* @author Mike Gerwitz * @author Mike Gerwitz
*/ */
var common = require( './common' ), require( 'common' ).testCase(
assert = require( 'assert' ),
util = common.require( 'util' ),
sut = common.require( 'MethodWrappers' )
;
/**
* The wrappers accept a function that should return the instance to be bound to
* 'this' when invoking a method. This has some important consequences, such as
* the ability to implement protected/private members.
*/
( function testMethodInvocationBindsThisToPassedInstance()
{ {
caseSetUp: function()
{
// common assertions between a couple of proxy tests
this.proxyErrorAssertCommon = function( e, prop, method )
{
this.assertOk(
e.message.search( 'Unable to proxy' ) > -1,
"Unexpected error received: " + e.message
);
this.assertOk(
( ( e.message.search( prop ) > -1 )
&& ( e.message.search( method ) > -1 )
),
"Error should contain property and method names"
);
};
},
setUp: function()
{
this._sut = this.require( 'MethodWrappers' );
},
/**
* The wrappers accept a function that should return the instance to be
* bound to 'this' when invoking a method. This has some important
* consequences, such as the ability to implement protected/private members.
*/
'Method invocation binds `this` to passed instance': function()
{
var instance = function() {}, var instance = function() {},
val = 'fooboo', val = 'fooboo',
val2 = 'fooboo2', val2 = 'fooboo2',
@ -47,7 +68,7 @@ var common = require( './common' ),
return instance; return instance;
}, },
method = sut.standard.wrapNew( method = this._sut.standard.wrapNew(
function() function()
{ {
return this.foo; return this.foo;
@ -55,7 +76,7 @@ var common = require( './common' ),
null, 0, getInst null, 0, getInst
), ),
override = sut.standard.wrapOverride( override = this._sut.standard.wrapOverride(
function() function()
{ {
return this.foo2; return this.foo2;
@ -68,32 +89,33 @@ var common = require( './common' ),
instance.foo = val; instance.foo = val;
instance.foo2 = val2; instance.foo2 = val2;
assert.equal( method(), val, this.assertEqual( method(), val,
"Calling method will bind 'this' to passed instance" "Calling method will bind 'this' to passed instance"
); );
assert.equal( override(), val2, this.assertEqual( override(), val2,
"Calling method override will bind 'this' to passed instance" "Calling method override will bind 'this' to passed instance"
); );
} )(); },
/** /**
* The __super property is defined for method overrides and permits invoking the * The __super property is defined for method overrides and permits invoking
* overridden method (method of the supertype). * the overridden method (method of the supertype).
* *
* In this test, we are not looking to assert that __super matches the super * In this test, we are not looking to assert that __super matches the super
* method. Rather, we want to ensure it /invokes/ it. This is because the super * method. Rather, we want to ensure it /invokes/ it. This is because the
* method may be wrapped to provide additional functionality. We don't know, we * super method may be wrapped to provide additional functionality. We don't
* don't care. We just want to make sure it's functioning properly. * know, we don't care. We just want to make sure it's functioning properly.
*/ */
( function testOverridenMethodShouldContainReferenceToSuperMethod() 'Overriden method should contain reference to super method': function()
{ {
var orig_called = false, var _self = this,
orig_called = false,
getInst = function() {}, getInst = function() {},
// "super" method // "super" method
method = sut.standard.wrapNew( method = this._sut.standard.wrapNew(
function() function()
{ {
orig_called = true; orig_called = true;
@ -102,17 +124,17 @@ var common = require( './common' ),
), ),
// override method // override method
override = sut.standard.wrapOverride( override = this._sut.standard.wrapOverride(
function() function()
{ {
assert.notEqual( _self.assertNotEqual(
this.__super, this.__super,
undefined, undefined,
"__super is defined for overridden method" "__super is defined for overridden method"
); );
this.__super(); this.__super();
assert.equal( _self.assertEqual(
orig_called, orig_called,
true, true,
"Invoking __super calls super method" "Invoking __super calls super method"
@ -124,19 +146,19 @@ var common = require( './common' ),
// invoke the method to run the above assertions // invoke the method to run the above assertions
override(); override();
} )(); },
/** /**
* If the method is called when bound to a different context (e.g. for * If the method is called when bound to a different context (e.g. for
* protected/private members), __super may not be properly bound. * protected/private members), __super may not be properly bound.
* *
* This test is in response to a bug found after implementing visibility * This test is in response to a bug found after implementing visibility
* support. The __super() method was previously defined on 'this', which may or * support. The __super() method was previously defined on 'this', which may
* may not be the context that is actually used. Likely, it's not. * or may not be the context that is actually used. Likely, it's not.
*/ */
( function testSuperMethodWorksProperlyWhenContextDiffers() 'Super method works properly when context differs': function()
{ {
var super_called = false, var super_called = false,
retobj = {}, retobj = {},
@ -146,7 +168,7 @@ var common = require( './common' ),
}, },
// super method to be overridden // super method to be overridden
method = sut.standard.wrapNew( method = this._sut.standard.wrapNew(
function() function()
{ {
super_called = true; super_called = true;
@ -155,7 +177,7 @@ var common = require( './common' ),
), ),
// the overriding method // the overriding method
override = sut.standard.wrapOverride( override = this._sut.standard.wrapOverride(
function() function()
{ {
this.__super(); this.__super();
@ -168,26 +190,26 @@ var common = require( './common' ),
override(); override();
// ensure that the super method was called // ensure that the super method was called
assert.equal( super_called, true, this.assertEqual( super_called, true,
"__super() method is called even when context differs" "__super() method is called even when context differs"
); );
// finally, ensure that __super is no longer set on the returned object // finally, ensure that __super is no longer set on the returned object
// after the call to ensure that the caller cannot break encapsulation by // after the call to ensure that the caller cannot break encapsulation
// stealing a method reference (sneaky, sneaky) // by stealing a method reference (sneaky, sneaky)
assert.equal( retobj.__super, undefined, this.assertEqual( retobj.__super, undefined,
"__super() method is unset after being called" "__super() method is unset after being called"
); );
} )(); },
/** /**
* The proxy wrapper should forward all arguments to the provided object's * The proxy wrapper should forward all arguments to the provided object's
* appropriate method. The return value should also be proxied back to the * appropriate method. The return value should also be proxied back to the
* caller. * caller.
*/ */
( function testProxyWillProperlyForwardCallToDestinationObject() 'Proxy will properly forward calls to destination object': function()
{ {
var name = 'someMethod', var name = 'someMethod',
propname = 'dest', propname = 'dest',
@ -211,30 +233,33 @@ var common = require( './common' ),
// acts like a class instance // acts like a class instance
inst = { dest: dest }, inst = { dest: dest },
proxy = sut.standard.wrapProxy( propname, null, 0, getInst, name ) proxy = this._sut.standard.wrapProxy(
propname, null, 0, getInst, name
)
; ;
assert.strictEqual( method_retval, proxy.apply( inst, args ), this.assertStrictEqual( method_retval, proxy.apply( inst, args ),
"Proxy call should return the value from the destination" "Proxy call should return the value from the destination"
); );
assert.deepEqual( args, args_given, this.assertDeepEqual( args, args_given,
"All arguments should be properly forwarded to the destination" "All arguments should be properly forwarded to the destination"
); );
} )(); },
/** /**
* If the destination object returns itself, then we should return the context * If the destination object returns itself, then we should return the
* in which the proxy was called; this ensures that we do not break * context in which the proxy was called; this ensures that we do not break
* encapsulation. Consequently, it also provides a more consistent and sensical * encapsulation. Consequently, it also provides a more consistent and
* API and permits method chaining. * sensical API and permits method chaining.
* *
* If this is not the desired result, then the user is free to forefit the proxy * If this is not the desired result, then the user is free to forefit the
* wrapper and instead use a normal method, manually proxying the call. * proxy wrapper and instead use a normal method, manually proxying the
* call.
*/ */
( function testProxyReturnValueIsReplacedWithContextIfDestinationReturnsSelf() 'Proxy retval is replaced with context if dest returns self': function()
{ {
var propname = 'foo', var propname = 'foo',
method = 'bar', method = 'bar',
@ -248,7 +273,7 @@ var common = require( './common' ),
inst = { foo: foo }, inst = { foo: foo },
ret = sut.standard.wrapProxy( ret = this._sut.standard.wrapProxy(
propname, null, 0, propname, null, 0,
function() function()
{ {
@ -258,43 +283,26 @@ var common = require( './common' ),
).call( inst ) ).call( inst )
; ;
assert.strictEqual( inst, ret, this.assertStrictEqual( inst, ret,
"Proxy should return instance in place of destination, if returned" "Proxy should return instance in place of destination, if returned"
); );
} )(); },
// common assertions between a couple of proxy tests /**
function proxyErrorAssertCommon( e, prop, method ) * Rather than allowing a cryptic error to be thrown by the engine, take
{ * some initiative and attempt to detect when a call will fail due to the
assert.ok( * destination not being an object.
e.message.search( 'Unable to proxy' ) > -1,
"Unexpected error received: " + e.message
);
assert.ok(
( ( e.message.search( prop ) > -1 )
&& ( e.message.search( method ) > -1 )
),
"Error should contain property and method names"
);
}
/**
* Rather than allowing a cryptic error to be thrown by the engine, take some
* initiative and attempt to detect when a call will fail due to the destination
* not being an object.
*/ */
( function testProxyThrowsErrorIfCallWillFailDueToNonObject() 'Proxy throws error if call will faill due to non-object': function()
{ {
var prop = 'noexist', var prop = 'noexist',
method = 'foo'; method = 'foo';
try try
{ {
// should fail because 'noexist' does not exist on the object // should fail because 'noexist' does not exist on the object
sut.standard.wrapProxy( this._sut.standard.wrapProxy(
prop, null, 0, prop, null, 0,
function() { return {}; }, function() { return {}; },
method method
@ -302,30 +310,30 @@ function proxyErrorAssertCommon( e, prop, method )
} }
catch ( e ) catch ( e )
{ {
proxyErrorAssertCommon( e, prop, method ); this.proxyErrorAssertCommon( e, prop, method );
return; return;
} }
assert.fail( this.assertFail(
"Error should be thrown if proxy would fail due to a non-object" "Error should be thrown if proxy would fail due to a non-object"
); );
} )(); },
/** /**
* Rather than allowing a cryptic error to be thrown by the engine, take some * Rather than allowing a cryptic error to be thrown by the engine, take
* initiative and attempt to detect when a call will fail due to the destination * some initiative and attempt to detect when a call will fail due to the
* method not being a function. * destination method not being a function.
*/ */
( function testProxyThrowsErrorIfCallWillFailDueToNonObject() 'Proxy throws error if call will fail due to non-function': function()
{ {
var prop = 'dest', var prop = 'dest',
method = 'foo'; method = 'foo';
try try
{ {
// should fail because 'noexist' does not exist on the object // should fail because 'noexist' does not exist on the object
sut.standard.wrapProxy( this._sut.standard.wrapProxy(
prop, null, 0, prop, null, 0,
function() { return { dest: { foo: 'notafunc' } }; }, function() { return { dest: { foo: 'notafunc' } }; },
method method
@ -333,22 +341,23 @@ function proxyErrorAssertCommon( e, prop, method )
} }
catch ( e ) catch ( e )
{ {
proxyErrorAssertCommon( e, prop, method ); this.proxyErrorAssertCommon( e, prop, method );
return; return;
} }
assert.fail( this.assertFail(
"Error should be thrown if proxy would fail due to a non-function" "Error should be thrown if proxy would fail due to a non-function"
); );
} )(); },
/** /**
* If the `static' keyword is provided, then the proxy mustn't operate on * If the `static' keyword is provided, then the proxy mustn't operate on
* instance properties. Instead, the static accessor method $() must be used. * instance properties. Instead, the static accessor method $() must be
* used.
*/ */
( function testCanProxyToStaticMembers() 'Can proxy to static members': function()
{ {
var getInst = function() var getInst = function()
{ {
// pretend that we're a static class with a static accessor method // pretend that we're a static class with a static accessor method
@ -375,9 +384,12 @@ function proxyErrorAssertCommon( e, prop, method )
} }
}; };
assert.strictEqual( val, this.assertStrictEqual( val,
sut.standard.wrapProxy( 'foo', null, 0, getInst, 'method', keywords )(), this._sut.standard.wrapProxy(
'foo', null, 0, getInst, 'method', keywords
)(),
"Should properly proxy to static membesr via static accessor method" "Should properly proxy to static membesr via static accessor method"
); );
} )(); },
} );

View File

@ -21,57 +21,62 @@
* @author Mike Gerwitz * @author Mike Gerwitz
*/ */
require( 'common' ).testCase(
{
caseSetUp: function()
{
this.sut = this.require( 'VisibilityObjectFactoryFactory' );
var common = require( './common' ), this.VisibilityObjectFactory =
assert = require( 'assert' ), this.require( 'VisibilityObjectFactory' );
util = common.require( 'util' ),
sut = common.require( 'VisibilityObjectFactoryFactory' ), this.FallbackVisibilityObjectFactory =
this.require( 'FallbackVisibilityObjectFactory' );
VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ), this.util = this.require( 'util' );
},
FallbackVisibilityObjectFactory =
common.require( 'FallbackVisibilityObjectFactory' )
;
/** /**
* By default, if supported by our environment, we should use the standard * By default, if supported by our environment, we should use the standard
* factory to provide proper visibility support. * factory to provide proper visibility support.
*/ */
( function testReturnsStandardIfNotFallingBack() 'Returns standard factory if not falling back': function()
{ {
// don't bother with the test if we don't support the standard visibility // don't bother with the test if we don't support the standard visibility
// object // object
if ( util.definePropertyFallback() ) if ( this.util.definePropertyFallback() )
{ {
return; return;
} }
assert.ok( this.assertOk(
( sut.fromEnvironment() instanceof VisibilityObjectFactory ), ( this.sut.fromEnvironment()
instanceof this.VisibilityObjectFactory ),
"Creates standard VisibilityObjectFactory if supported" "Creates standard VisibilityObjectFactory if supported"
); );
} )(); },
/** /**
* If not supported by our environment, we should be permitted to fall back to a * If not supported by our environment, we should be permitted to fall back to a
* working implementation that sacrifices visibility support. * working implementation that sacrifices visibility support.
*/ */
( function testReturnsFallbackFactoryIfFallingBack() 'Returns fallback factory if falling back': function()
{ {
var old = util.definePropertyFallback(); var old = this.util.definePropertyFallback();
// force fallback // force fallback
util.definePropertyFallback( true ); this.util.definePropertyFallback( true );
assert.ok( this.assertOk(
( sut.fromEnvironment() instanceof FallbackVisibilityObjectFactory ), ( this.sut.fromEnvironment()
instanceof this.FallbackVisibilityObjectFactory
),
"Creates fallback VisibilityObjectFactory if falling back" "Creates fallback VisibilityObjectFactory if falling back"
); );
// restore fallback // restore fallback
util.definePropertyFallback( old ); this.util.definePropertyFallback( old );
} )(); },
} );

View File

@ -21,23 +21,14 @@
* @author Mike Gerwitz * @author Mike Gerwitz
*/ */
require( 'common' ).testCase(
var common = require( './common' ),
assert = require( 'assert' );
// we cannot perform these tests if it's not supported by our environment
if ( common.require( 'util' ).definePropertyFallback() )
{ {
return; caseSetUp: function()
} {
this.Sut = this.require( 'VisibilityObjectFactory' );
// SUT
var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
sut = VisibilityObjectFactory(),
// properties are expected to be in a specific format // properties are expected to be in a specific format
props = { this.props = {
'public': { 'public': {
pub: [ [ 'foo' ], {} ], pub: [ [ 'foo' ], {} ],
}, },
@ -47,9 +38,9 @@ var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
'private': { 'private': {
priv: [ [ 'baz' ], {} ], priv: [ [ 'baz' ], {} ],
}, },
}, };
methods = { this.methods = {
'public': { 'public': {
fpub: ( function() fpub: ( function()
{ {
@ -65,42 +56,56 @@ var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
'private': { 'private': {
fpriv: function() {}, fpriv: function() {},
}, },
};
},
setUp: function()
{
// we cannot perform these tests if they are not supported by our
// environment
if ( this.require( 'util' ).definePropertyFallback() )
{
this.skip();
} }
;
this.sut = this.Sut();
},
/** /**
* To keep with the spirit of ease.js, we should be able to instantiate * To keep with the spirit of ease.js, we should be able to instantiate
* VisibilityObjectFactory both with and without the 'new' keyword * VisibilityObjectFactory both with and without the 'new' keyword
* *
* Consistency is key with these sorts of things. * Consistency is key with these sorts of things.
*/ */
( function testCanInstantiateWithAndWithoutNewKeyword() 'Can instantiate with and without `new` keyword': function()
{ {
// with 'new' keyword // with `new` keyword
assert.ok( this.assertOk(
( new VisibilityObjectFactory() ) instanceof VisibilityObjectFactory, ( new this.Sut() ) instanceof this.Sut,
"Should be able to instantiate VisibilityObjectFactory with 'new' " + "Should be able to instantiate VisibilityObjectFactory with " +
"keyword" "'new' keyword"
); );
// without 'new' keyword // without `new` keyword
assert.ok( VisibilityObjectFactory() instanceof VisibilityObjectFactory, this.assertOk( this.Sut() instanceof this.Sut,
"Should be able to instantiate VisibilityObjectFactory without 'new' " + "Should be able to instantiate VisibilityObjectFactory without " +
"keyword" "'new' keyword"
); );
} )(); },
/** /**
* One of the core requirements for proper visibility support is the ability to * One of the core requirements for proper visibility support is the ability
* create a proxy object. Proxy objects transfer gets/sets of a certain property * to create a proxy object. Proxy objects transfer gets/sets of a certain
* to another object. This allows objects to be layered atop each other while * property to another object. This allows objects to be layered atop each
* still permitting gets/sets to fall through. * other while still permitting gets/sets to fall through.
*/ */
( function testCanCreatePropertyProxy() 'Can create property proxy': function()
{ {
var base = {}, var _self = this,
base = {},
dest = {}, dest = {},
props = { one: true, two: true, three: true }, props = { one: true, two: true, three: true },
val = 'foo', val = 'foo',
@ -108,7 +113,7 @@ var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
; ;
// create proxy of props to base on dest // create proxy of props to base on dest
sut.createPropProxy( base, dest, props ); this.sut.createPropProxy( base, dest, props );
// check to ensure the properties are properly proxied // check to ensure the properties are properly proxied
for ( var prop in props ) for ( var prop in props )
@ -116,12 +121,12 @@ var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
dest[ prop ] = val; dest[ prop ] = val;
// check proxy // check proxy
assert.equal( dest[ prop ], val, _self.assertEqual( dest[ prop ], val,
"Property can be set/retrieved on destination object" "Property can be set/retrieved on destination object"
); );
// check base // check base
assert.equal( base[ prop ], val, _self.assertEqual( base[ prop ], val,
"Property can be set via proxy and retrieved on base" "Property can be set via proxy and retrieved on base"
); );
@ -129,162 +134,164 @@ var VisibilityObjectFactory = common.require( 'VisibilityObjectFactory' ),
base[ prop ] = val2; base[ prop ] = val2;
// re-check proxy // re-check proxy
assert.equal( dest[ prop ], val2, _self.assertEqual( dest[ prop ], val2,
"Property can be set on base and retrieved on dest object" "Property can be set on base and retrieved on dest object"
); );
} }
} )(); },
/** /**
* An additional layer should be created, which will hold the private members. * An additional layer should be created, which will hold the private
* members.
*/ */
( function testSetupCreatesPrivateLayer() 'Setup creates private layer': function()
{ {
var dest = { foo: [] }, var dest = { foo: [] },
obj = sut.setup( dest, props, methods ); obj = this.sut.setup( dest, this.props, this.methods );
assert.notEqual( obj, dest, this.assertNotEqual( obj, dest,
"Returned object should not be the destination object" "Returned object should not be the destination object"
); );
assert.strictEqual( obj.foo, dest.foo, this.assertStrictEqual( obj.foo, dest.foo,
"Destination object is part of the prototype chain of the returned obj" "Destination object is part of the prototype chain of the " +
"returned obj"
); );
} )(); },
/** /**
* All protected properties must be proxied from the private layer to the * All protected properties must be proxied from the private layer to the
* protected. Otherwise, sets would occur on the private object, which would * protected. Otherwise, sets would occur on the private object, which would
* prevent them from being accessed by subtypes if set by a parent method * prevent them from being accessed by subtypes if set by a parent method
* invocation. (The same is true in reverse.) * invocation. (The same is true in reverse.)
*/ */
( function testPrivateLayerIncludesProtectedMemberProxy() 'Private layer includes protected member proxy': function()
{ {
var dest = {}, var dest = {},
obj = sut.setup( dest, props, methods ), obj = this.sut.setup( dest, this.props, this.methods ),
val = 'foo' val = 'foo'
; ;
obj.prot = val; obj.prot = val;
assert.equal( dest.prot, val, this.assertEqual( dest.prot, val,
"Protected values are proxied from private layer" "Protected values are proxied from private layer"
); );
} )(); },
/** /**
* Public properties should be initialized on the destination object to ensure * Public properties should be initialized on the destination object to
* that references are not shared between instances (that'd be a pretty nasty * ensure that references are not shared between instances (that'd be a
* bug). * pretty nasty bug).
* *
* Note that we do not care about public methods, because they're assumed to * Note that we do not care about public methods, because they're assumed to
* already be part of the prototype chain. The visibility object is only * already be part of the prototype chain. The visibility object is only
* intended to handle levels of visibility that are not directly implemented in * intended to handle levels of visibility that are not directly implemented
* JS. Public methods are a direct consequence of adding a property to the * in JS. Public methods are a direct consequence of adding a property to
* prototype chain. * the prototype chain.
*/ */
( function testPublicPropertiesAreCopiedToDestinationObject() 'Public properties are copied to destination object': function()
{ {
var dest = {}; var dest = {};
sut.setup( dest, props, methods ); this.sut.setup( dest, this.props, this.methods );
// values should match // values should match
assert.equal( dest.pub[ 0 ], props[ 'public' ].pub[ 0 ], this.assertEqual( dest.pub[ 0 ], this.props[ 'public' ].pub[ 0 ],
"Public properties are properly initialized" "Public properties are properly initialized"
); );
// ensure references are not shared (should be cloned) // ensure references are not shared (should be cloned)
assert.notStrictEqual( dest.pub, props[ 'public' ].pub, this.assertNotStrictEqual( dest.pub, this.props[ 'public' ].pub,
"Public properties should not be copied by reference" "Public properties should not be copied by reference"
); );
// method references should NOT be transferred (they're assumed to already // method references should NOT be transferred (they're assumed to
// be a part of the prototype chain, since they're outside the scope of the // already be a part of the prototype chain, since they're outside the
// visibility object) // scope of the visibility object)
assert.equal( dest.fpub, undefined, this.assertEqual( dest.fpub, undefined,
"Public method references should not be copied" "Public method references should not be copied"
); );
} )(); },
/** /**
* Protected properties should be copied over for the same reason that public * Protected properties should be copied over for the same reason that
* properties should, in addition to the fact that the protected members are not * public properties should, in addition to the fact that the protected
* likely to be present on the destination object. In addition, methods will be * members are not likely to be present on the destination object. In
* copied over. * addition, methods will be copied over.
*/ */
( function testProtectedPropertiesAndMethodsAreAddedToDestinationObject() 'Protected properties and methods are added to dest object': function()
{ {
var dest = {}; var dest = {};
sut.setup( dest, props, methods ); this.sut.setup( dest, this.props, this.methods );
// values should match // values should match
assert.equal( dest.prot[ 0 ], props[ 'protected' ].prot[ 0 ], this.assertEqual( dest.prot[ 0 ], this.props[ 'protected' ].prot[ 0 ],
"Protected properties are properly initialized" "Protected properties are properly initialized"
); );
// ensure references are not shared (should be cloned) // ensure references are not shared (should be cloned)
assert.notStrictEqual( dest.prot, props[ 'protected' ].prot, this.assertNotStrictEqual( dest.prot, this.props[ 'protected' ].prot,
"Protected properties should not be copied by reference" "Protected properties should not be copied by reference"
); );
// protected method references should be copied // protected method references should be copied
assert.strictEqual( dest.fprot, methods[ 'protected' ].fprot, this.assertStrictEqual( dest.fprot, this.methods[ 'protected' ].fprot,
"Protected members should be copied by reference" "Protected members should be copied by reference"
); );
} )(); },
/** /**
* Public members should *always* take precedence over protected. The reason for * Public members should *always* take precedence over protected. The reason
* this is because, if a protected member is overridden and made public by a * for this is because, if a protected member is overridden and made public
* subtype, we need to ensure that the protected member of the supertype doesn't * by a subtype, we need to ensure that the protected member of the
* take precedence. The reason it would take precedence by default is because * supertype doesn't take precedence. The reason it would take precedence by
* the protected visibility object is laid *atop* the public, meaning it comes * default is because the protected visibility object is laid *atop* the
* first in the prototype chain. * public, meaning it comes first in the prototype chain.
*/ */
( function testPublicMethodsAreNotOverwrittenByProtected() 'Public methods are not overwritten by default': function()
{ {
// use the public method // use the public method
var dest = { fpub: methods[ 'public' ].fpub }; var dest = { fpub: this.methods[ 'public' ].fpub };
// add duplicate method to protected // add duplicate method to protected
methods[ 'protected' ].fpub = function() {}; this.methods[ 'protected' ].fpub = function() {};
sut.setup( dest, props, methods ); this.sut.setup( dest, this.props, this.methods );
// ensure our public method is still referenced // ensure our public method is still referenced
assert.strictEqual( dest.fpub, methods[ 'public' ].fpub, this.assertStrictEqual( dest.fpub, this.methods[ 'public' ].fpub,
"Public methods should not be overwritten by protected methods" "Public methods should not be overwritten by protected methods"
); );
} )(); },
/** /**
* Same situation with private members as protected, with the exception that we * Same situation with private members as protected, with the exception that
* do not need to worry about the overlay problem (in regards to methods). This * we do not need to worry about the overlay problem (in regards to
* is simply because private members are not inherited. * methods). This is simply because private members are not inherited.
*/ */
( function testPrivatePropertiesAndMethodsAreAddedToDestinationObject() 'Private properties and methods are added to dest object': function()
{ {
var dest = {}, var dest = {},
obj = sut.setup( dest, props, methods ); obj = this.sut.setup( dest, this.props, this.methods );
// values should match // values should match
assert.equal( obj.priv[ 0 ], props[ 'private' ].priv[ 0 ], this.assertEqual( obj.priv[ 0 ], this.props[ 'private' ].priv[ 0 ],
"Private properties are properly initialized" "Private properties are properly initialized"
); );
// ensure references are not shared (should be cloned) // ensure references are not shared (should be cloned)
assert.notStrictEqual( obj.priv, props[ 'private' ].priv, this.assertNotStrictEqual( obj.priv, this.props[ 'private' ].priv,
"Private properties should not be copied by reference" "Private properties should not be copied by reference"
); );
// private method references should be copied // private method references should be copied
assert.strictEqual( obj.fpriv, methods[ 'private' ].fpriv, this.assertStrictEqual( obj.fpriv, this.methods[ 'private' ].fpriv,
"Private members should be copied by reference" "Private members should be copied by reference"
); );
} )(); },
} );