2011-11-19 14:09:59 -05:00
|
|
|
/**
|
|
|
|
* Tests extending of interfaces
|
|
|
|
*
|
2013-12-20 01:11:26 -05:00
|
|
|
* Copyright (C) 2011, 2013 Mike Gerwitz
|
2011-11-19 14:09:59 -05:00
|
|
|
*
|
2013-12-22 09:37:21 -05:00
|
|
|
* This file is part of GNU ease.js.
|
2011-11-19 14:09:59 -05:00
|
|
|
*
|
|
|
|
* 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-11-19 14:09:59 -05: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-11-19 14:09:59 -05: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-11-19 14:09:59 -05:00
|
|
|
*
|
|
|
|
* @author Mike Gerwitz
|
|
|
|
*/
|
|
|
|
|
|
|
|
var common = require( 'common' ),
|
2011-11-19 22:05:18 -05:00
|
|
|
Interface = common.require( 'interface' ),
|
2011-11-19 14:09:59 -05:00
|
|
|
|
|
|
|
// get/set test support
|
|
|
|
gst = !( common.require( 'util' ).definePropertyFallback() )
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
|
|
common.testCase(
|
|
|
|
{
|
|
|
|
caseSetUp: function()
|
|
|
|
{
|
|
|
|
// There's a couple ways to create interfaces. Test 'em both.
|
|
|
|
this.baseTypes = [
|
|
|
|
Interface.extend(
|
|
|
|
{
|
2011-11-19 19:07:38 -05:00
|
|
|
method: [],
|
2011-11-19 14:09:59 -05:00
|
|
|
} ),
|
|
|
|
|
|
|
|
Interface( {
|
2011-11-19 19:07:38 -05:00
|
|
|
method: [],
|
2011-11-19 14:09:59 -05:00
|
|
|
} )
|
|
|
|
];
|
|
|
|
|
|
|
|
// non-object values to assert failures upon
|
|
|
|
this.invalidExtend = [ 'moo', 5, false, undefined ];
|
|
|
|
|
|
|
|
// bad access modifiers (cannot be used in interfaces)
|
|
|
|
this.badAm = [ 'protected', 'private' ];
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
'Properties are not permitted within interfaces': function()
|
|
|
|
{
|
|
|
|
this.assertThrows(
|
|
|
|
function()
|
|
|
|
{
|
|
|
|
Interface.extend(
|
|
|
|
{
|
|
|
|
// properties are not permitted
|
|
|
|
prop: 'not permitted',
|
|
|
|
});
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"Properties are not permitted within Interface definitions"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
'Getters are setters are not permitted within intefaces': function()
|
|
|
|
{
|
|
|
|
// don't perform get/set test if unsupported by environment
|
|
|
|
if ( !gst )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// so we don't break browsers that do not support getters/setters in object
|
|
|
|
// notation
|
|
|
|
var data = {};
|
|
|
|
Object.defineProperty( data, 'foo', {
|
|
|
|
get: function() {},
|
|
|
|
set: function() {},
|
|
|
|
|
|
|
|
enumerable: true,
|
|
|
|
} );
|
|
|
|
|
|
|
|
this.assertThrows( function()
|
|
|
|
{
|
|
|
|
Interface.extend( data );
|
|
|
|
}, TypeError, "Getters/setters not permitted within Interfaces" );
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
'Concrete methods are not permitted': function()
|
|
|
|
{
|
|
|
|
this.assertThrows(
|
|
|
|
function()
|
|
|
|
{
|
|
|
|
Interface.extend(
|
|
|
|
{
|
|
|
|
// concrete method
|
|
|
|
method: function() {}
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"Concrete methods are not permitted within Interface definitions"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2011-11-19 19:07:38 -05:00
|
|
|
/**
|
|
|
|
* Declaring (but not defining) methods by specifying their arguments as
|
|
|
|
* arrays is supported, much like one would would declare an abstract method
|
|
|
|
* in a class. We do not require the abstract keyword, as it would be
|
|
|
|
* redundant.
|
|
|
|
*/
|
|
|
|
'Method declarations (using arrays) are permitted': function()
|
2011-11-19 14:09:59 -05:00
|
|
|
{
|
|
|
|
this.assertDoesNotThrow(
|
|
|
|
function()
|
|
|
|
{
|
|
|
|
Interface.extend(
|
|
|
|
{
|
2011-11-19 19:07:38 -05:00
|
|
|
method: [],
|
2011-11-19 14:09:59 -05:00
|
|
|
} );
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"Abstract method declarations are allowed within Interface " +
|
|
|
|
"definitions"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The defined abstract methods should be included in the resulting
|
|
|
|
* interface
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Interface contains defined abstract methods':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
this.assertOk(
|
|
|
|
( typeof T.prototype.method === 'function' ),
|
|
|
|
"Interface should contain defined abstract methods"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The resulting interface should be considered, by the system's
|
|
|
|
* isInterface() call, to be an interface. Otherwise that would be a pretty
|
|
|
|
* useless call, now wouldn't it?
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Result is considered to be an interface': function( T )
|
|
|
|
{
|
|
|
|
this.assertEqual(
|
|
|
|
Interface.isInterface( T ),
|
|
|
|
true
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interfaces can be extended much like classes. In this case, however, we
|
|
|
|
* are only extending the API.
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Can extend interface using Interface.extend()':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
var SubType = Interface.extend( T, {} );
|
|
|
|
|
|
|
|
this.assertOk(
|
|
|
|
( SubType.prototype instanceof T ),
|
|
|
|
"Generic interface extend method should be able to extend from " +
|
|
|
|
"other interfaces"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* As the term 'extending' would apply, sub-interfaces should 'inherit'
|
|
|
|
* their parents' API.
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Interface subtypes inherit abstract methods':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
var SubType = Interface.extend( T, {} );
|
|
|
|
|
|
|
|
this.assertOk(
|
|
|
|
( SubType.prototype.method === T.prototype.method ),
|
|
|
|
"Interface subtypes inherit abstract methods"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* One should be able to add additional methods to the API of a
|
|
|
|
* sub-interface.
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Interfaces can extend the API with abstract methods':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
var SubType = Interface.extend( T,
|
|
|
|
{
|
2011-11-19 19:07:38 -05:00
|
|
|
second: [],
|
2011-11-19 14:09:59 -05:00
|
|
|
} );
|
|
|
|
|
|
|
|
this.assertOk(
|
|
|
|
( typeof SubType.prototype.second === 'function' ),
|
|
|
|
"Should be able to extend interfaces with additional abstract " +
|
|
|
|
"methods"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interfaces should contain a built-in extend() method as a short-hand for
|
|
|
|
* subtyping.
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Interfaces contain an extend() method': function( T )
|
|
|
|
{
|
|
|
|
this.assertOk(
|
|
|
|
( typeof T.extend === 'function' ),
|
|
|
|
"Interface should contain extend() method"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Similar to above, but using the interface itself's extend() method
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) extend() method on interface itself can extend':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
var SubType = T.extend( {} );
|
|
|
|
|
|
|
|
this.assertOk(
|
|
|
|
( SubType.prototype instanceof T ),
|
|
|
|
"Interface extend method can extend interfaces"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Similar to above, but using the interface itself's extend() method
|
|
|
|
*/
|
|
|
|
'@each(baseTypes) Interface\'s extend() method can add to the API':
|
|
|
|
function( T )
|
|
|
|
{
|
|
|
|
var SubType = T.extend(
|
|
|
|
{
|
2011-11-19 19:07:38 -05:00
|
|
|
second: [],
|
2011-11-19 14:09:59 -05:00
|
|
|
} );
|
|
|
|
|
|
|
|
this.assertOk(
|
|
|
|
( typeof SubType.prototype.second === 'function' ),
|
|
|
|
"Interfaces should be able to be extended with additional " +
|
|
|
|
"abstract methods using shorthand extend method"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The interface invocation action depends on what arguments are passed in.
|
|
|
|
* One use is to pass in an object as the first and only argument, creating
|
|
|
|
* a new interface with no supertype.
|
|
|
|
*/
|
|
|
|
'@each(invalidExtend) Invoking module to extend requires object':
|
|
|
|
function( val )
|
|
|
|
{
|
|
|
|
this.assertThrows( function()
|
|
|
|
{
|
|
|
|
Interface( val );
|
|
|
|
},
|
|
|
|
TypeError,
|
|
|
|
"Invoking interface module should require object as argument if " +
|
|
|
|
"extending from base interface"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If defining a new interface (object as the first argument on invocation),
|
|
|
|
* then only one argument is permitted.
|
|
|
|
*/
|
|
|
|
'Only one argment for interface definitions is permitted': function()
|
|
|
|
{
|
|
|
|
var args = [ {}, 'one', 'two', 'three' ];
|
|
|
|
|
|
|
|
// we must only provide one argument if the first argument is an object
|
|
|
|
// (the interface definition)
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Interface.apply( null, args );
|
|
|
|
|
|
|
|
// if all goes well, we don't get to this line
|
|
|
|
this.fail(
|
|
|
|
"Only one argument for interface definitions should be " +
|
|
|
|
"permitted"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
this.assertOk(
|
|
|
|
( e.message.search( args.length + " given" ) > -1 ),
|
|
|
|
"Interface invocation should give argument count on error"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interfaces represent a public API that must be implemented. It does not
|
|
|
|
* make sense to have members be anything but public. If protected members
|
|
|
|
* are required, that is appropriate only for an abstract class.
|
|
|
|
*/
|
|
|
|
'@each(badAm) Interface members must be public': function( am )
|
|
|
|
{
|
|
|
|
// protected
|
|
|
|
this.assertThrows( function()
|
|
|
|
{
|
|
|
|
// am = access modifier
|
|
|
|
var dfn = {};
|
2011-11-19 19:07:38 -05:00
|
|
|
dfn[ am + ' foo' ] = [];
|
2011-11-19 14:09:59 -05:00
|
|
|
|
|
|
|
Interface( dfn );
|
|
|
|
}, Error, "Interface members should not be able to be " + am );
|
|
|
|
},
|
2011-11-19 22:05:18 -05:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We only want to permit the extending of other interfaces.
|
|
|
|
*/
|
|
|
|
'Interfaces can only extend interfaces': function()
|
|
|
|
{
|
|
|
|
this.assertThrows( function()
|
|
|
|
{
|
|
|
|
Interface.extend( function() {}, {} );
|
|
|
|
}, TypeError, "Should not be able to extend from non-interface" );
|
|
|
|
},
|
2011-11-19 14:09:59 -05:00
|
|
|
} );
|
|
|
|
|