2011-08-11 23:11:37 -04:00
|
|
|
/**
|
|
|
|
* Tests visibility object factory
|
|
|
|
*
|
2013-12-20 01:11:26 -05:00
|
|
|
* Copyright (C) 2011, 2012, 2013 Mike Gerwitz
|
2011-08-11 23:11:37 -04:00
|
|
|
*
|
|
|
|
* This file is part of ease.js.
|
|
|
|
*
|
|
|
|
* ease.js is free software: you can redistribute it and/or modify it under the
|
Relicensed under the GPLv3+
This project was originally LGPLv+-licensed to encourage its use in a community
that is largely copyleft-phobic. After further reflection, that was a mistake,
as adoption is not the important factor here---software freedom is.
When submitting ease.js to the GNU project, it was asked if I would be willing
to relicense it under the GPLv3+; I agreed happily, because there is no reason
why we should provide proprietary software any sort of edge. Indeed, proprietary
JavaScript is a huge problem since it is automatically downloaded on the user's
PC generally without them even knowing, and is a current focus for the FSF. As
such, to remain firm in our stance against proprietary JavaScript, relicensing
made the most sense for GNU.
This is likely to upset current users of ease.js. I am not sure of their
number---I have only seen download counts periodically on npmjs.org---but I know
there are at least a small number. These users are free to continue using the
previous LGPL'd releases, but with the understanding that there will be no
further maintenance (not even bug fixes). If possible, users should use the
GPL-licensed versions and release their software as free software.
Here comes GNU ease.js.
2013-12-20 01:00:35 -05:00
|
|
|
* 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.
|
2011-08-11 23:11:37 -04:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
Relicensed under the GPLv3+
This project was originally LGPLv+-licensed to encourage its use in a community
that is largely copyleft-phobic. After further reflection, that was a mistake,
as adoption is not the important factor here---software freedom is.
When submitting ease.js to the GNU project, it was asked if I would be willing
to relicense it under the GPLv3+; I agreed happily, because there is no reason
why we should provide proprietary software any sort of edge. Indeed, proprietary
JavaScript is a huge problem since it is automatically downloaded on the user's
PC generally without them even knowing, and is a current focus for the FSF. As
such, to remain firm in our stance against proprietary JavaScript, relicensing
made the most sense for GNU.
This is likely to upset current users of ease.js. I am not sure of their
number---I have only seen download counts periodically on npmjs.org---but I know
there are at least a small number. These users are free to continue using the
previous LGPL'd releases, but with the understanding that there will be no
further maintenance (not even bug fixes). If possible, users should use the
GPL-licensed versions and release their software as free software.
Here comes GNU ease.js.
2013-12-20 01:00:35 -05:00
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
2011-08-11 23:11:37 -04:00
|
|
|
*
|
Relicensed under the GPLv3+
This project was originally LGPLv+-licensed to encourage its use in a community
that is largely copyleft-phobic. After further reflection, that was a mistake,
as adoption is not the important factor here---software freedom is.
When submitting ease.js to the GNU project, it was asked if I would be willing
to relicense it under the GPLv3+; I agreed happily, because there is no reason
why we should provide proprietary software any sort of edge. Indeed, proprietary
JavaScript is a huge problem since it is automatically downloaded on the user's
PC generally without them even knowing, and is a current focus for the FSF. As
such, to remain firm in our stance against proprietary JavaScript, relicensing
made the most sense for GNU.
This is likely to upset current users of ease.js. I am not sure of their
number---I have only seen download counts periodically on npmjs.org---but I know
there are at least a small number. These users are free to continue using the
previous LGPL'd releases, but with the understanding that there will be no
further maintenance (not even bug fixes). If possible, users should use the
GPL-licensed versions and release their software as free software.
Here comes GNU ease.js.
2013-12-20 01:00:35 -05:00
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
2011-08-11 23:11:37 -04:00
|
|
|
*
|
|
|
|
* @author Mike Gerwitz
|
|
|
|
*/
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
require( 'common' ).testCase(
|
2011-08-11 23:11:37 -04:00
|
|
|
{
|
2012-05-03 17:21:37 -04:00
|
|
|
caseSetUp: function()
|
|
|
|
{
|
|
|
|
this.Sut = this.require( 'VisibilityObjectFactory' );
|
|
|
|
|
|
|
|
// properties are expected to be in a specific format
|
|
|
|
this.props = {
|
|
|
|
'public': {
|
|
|
|
pub: [ [ 'foo' ], {} ],
|
|
|
|
},
|
|
|
|
'protected': {
|
|
|
|
prot: [ [ 'bar' ], {} ],
|
|
|
|
},
|
|
|
|
'private': {
|
|
|
|
priv: [ [ 'baz' ], {} ],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
this.methods = {
|
|
|
|
'public': {
|
|
|
|
fpub: ( function()
|
|
|
|
{
|
|
|
|
var retval = function() {};
|
|
|
|
retval.___$$keywords$$ = { 'public': true };
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
} )(),
|
|
|
|
},
|
|
|
|
'protected': {
|
|
|
|
fprot: function() {},
|
|
|
|
},
|
|
|
|
'private': {
|
|
|
|
fpriv: function() {},
|
|
|
|
},
|
|
|
|
};
|
2011-08-11 23:11:37 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
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();
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
'Can instantiate with and without `new` keyword': function()
|
2011-08-11 23:11:37 -04:00
|
|
|
{
|
2012-05-03 17:21:37 -04:00
|
|
|
// with `new` keyword
|
|
|
|
this.assertOk(
|
|
|
|
( new this.Sut() ) instanceof this.Sut,
|
|
|
|
"Should be able to instantiate VisibilityObjectFactory with " +
|
|
|
|
"'new' keyword"
|
2011-08-11 23:11:37 -04:00
|
|
|
);
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// without `new` keyword
|
|
|
|
this.assertOk( this.Sut() instanceof this.Sut,
|
|
|
|
"Should be able to instantiate VisibilityObjectFactory without " +
|
|
|
|
"'new' keyword"
|
2011-08-11 23:11:37 -04:00
|
|
|
);
|
2012-05-03 17:21:37 -04:00
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
'Can create property proxy': function()
|
|
|
|
{
|
|
|
|
var _self = this,
|
|
|
|
base = {},
|
|
|
|
dest = {},
|
|
|
|
props = { one: true, two: true, three: true },
|
|
|
|
val = 'foo',
|
|
|
|
val2 = 'bar'
|
|
|
|
;
|
|
|
|
|
|
|
|
// create proxy of props to base on dest
|
|
|
|
this.sut.createPropProxy( base, dest, props );
|
|
|
|
|
|
|
|
// check to ensure the properties are properly proxied
|
|
|
|
for ( var prop in props )
|
|
|
|
{
|
|
|
|
dest[ prop ] = val;
|
|
|
|
|
|
|
|
// check proxy
|
|
|
|
_self.assertEqual( dest[ prop ], val,
|
|
|
|
"Property can be set/retrieved on destination object"
|
|
|
|
);
|
|
|
|
|
|
|
|
// check base
|
|
|
|
_self.assertEqual( base[ prop ], val,
|
|
|
|
"Property can be set via proxy and retrieved on base"
|
|
|
|
);
|
|
|
|
|
|
|
|
// set to new value
|
|
|
|
base[ prop ] = val2;
|
|
|
|
|
|
|
|
// re-check proxy
|
|
|
|
_self.assertEqual( dest[ prop ], val2,
|
|
|
|
"Property can be set on base and retrieved on dest object"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* An additional layer should be created, which will hold the private
|
|
|
|
* members.
|
|
|
|
*/
|
|
|
|
'Setup creates private layer': function()
|
|
|
|
{
|
|
|
|
var dest = { foo: [] },
|
|
|
|
obj = this.sut.setup( dest, this.props, this.methods );
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
this.assertNotEqual( obj, dest,
|
|
|
|
"Returned object should not be the destination object"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
this.assertStrictEqual( obj.foo, dest.foo,
|
|
|
|
"Destination object is part of the prototype chain of the " +
|
|
|
|
"returned obj"
|
|
|
|
);
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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.)
|
|
|
|
*/
|
|
|
|
'Private layer includes protected member proxy': function()
|
|
|
|
{
|
|
|
|
var dest = {},
|
|
|
|
obj = this.sut.setup( dest, this.props, this.methods ),
|
|
|
|
val = 'foo'
|
|
|
|
;
|
|
|
|
|
|
|
|
obj.prot = val;
|
|
|
|
this.assertEqual( dest.prot, val,
|
|
|
|
"Protected values are proxied from private layer"
|
|
|
|
);
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
'Public properties are copied to destination object': function()
|
|
|
|
{
|
|
|
|
var dest = {};
|
|
|
|
this.sut.setup( dest, this.props, this.methods );
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// values should match
|
|
|
|
this.assertEqual( dest.pub[ 0 ], this.props[ 'public' ].pub[ 0 ],
|
|
|
|
"Public properties are properly initialized"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// ensure references are not shared (should be cloned)
|
|
|
|
this.assertNotStrictEqual( dest.pub, this.props[ 'public' ].pub,
|
|
|
|
"Public properties should not be copied by reference"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// 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"
|
|
|
|
);
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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 and methods are added to dest object': function()
|
|
|
|
{
|
|
|
|
var dest = {};
|
|
|
|
this.sut.setup( dest, this.props, this.methods );
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// values should match
|
|
|
|
this.assertEqual( dest.prot[ 0 ], this.props[ 'protected' ].prot[ 0 ],
|
|
|
|
"Protected properties are properly initialized"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// ensure references are not shared (should be cloned)
|
|
|
|
this.assertNotStrictEqual( dest.prot, this.props[ 'protected' ].prot,
|
|
|
|
"Protected properties should not be copied by reference"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// protected method references should be copied
|
|
|
|
this.assertStrictEqual( dest.fprot, this.methods[ 'protected' ].fprot,
|
|
|
|
"Protected members should be copied by reference"
|
|
|
|
);
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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 methods are not overwritten by default': function()
|
|
|
|
{
|
|
|
|
// use the public method
|
|
|
|
var dest = { fpub: this.methods[ 'public' ].fpub };
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// add duplicate method to protected
|
|
|
|
this.methods[ 'protected' ].fpub = function() {};
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
this.sut.setup( dest, this.props, this.methods );
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// ensure our public method is still referenced
|
|
|
|
this.assertStrictEqual( dest.fpub, this.methods[ 'public' ].fpub,
|
|
|
|
"Public methods should not be overwritten by protected methods"
|
|
|
|
);
|
|
|
|
},
|
2011-08-11 23:11:37 -04:00
|
|
|
|
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
'Private properties and methods are added to dest object': function()
|
|
|
|
{
|
|
|
|
var dest = {},
|
|
|
|
obj = this.sut.setup( dest, this.props, this.methods );
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// values should match
|
|
|
|
this.assertEqual( obj.priv[ 0 ], this.props[ 'private' ].priv[ 0 ],
|
|
|
|
"Private properties are properly initialized"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// ensure references are not shared (should be cloned)
|
|
|
|
this.assertNotStrictEqual( obj.priv, this.props[ 'private' ].priv,
|
|
|
|
"Private properties should not be copied by reference"
|
|
|
|
);
|
2011-08-11 23:11:37 -04:00
|
|
|
|
2012-05-03 17:21:37 -04:00
|
|
|
// private method references should be copied
|
|
|
|
this.assertStrictEqual( obj.fpriv, this.methods[ 'private' ].fpriv,
|
|
|
|
"Private members should be copied by reference"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
} );
|