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

View File

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

View File

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

View File

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

View File

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