diff --git a/test/Class/AbstractTest.js b/test/Class/AbstractTest.js
new file mode 100644
index 0000000..66dc06f
--- /dev/null
+++ b/test/Class/AbstractTest.js
@@ -0,0 +1,534 @@
+/**
+ * Tests abstract classes
+ *
+ * Copyright (C) 2010, 2011, 2012, 2013 Mike Gerwitz
+ *
+ * This file is part of GNU ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+require( 'common' ).testCase(
+{
+ caseSetUp: function()
+ {
+ this.Sut = this.require( 'class_abstract' );
+ this.Class = this.require( 'class' );
+ },
+
+
+ /**
+ * In order to ensure that the code documents itself, we should require
+ * that all classes containing abstract members must themselves be
+ * declared as abstract. Otherwise, you are at the mercy of the
+ * developer's documentation/comments to know whether or not the class
+ * is indeed abstract without looking through its definition.
+ */
+ 'Must declare classes with abstract members as abstract': function()
+ {
+ try
+ {
+ // should fail; class not declared as abstract
+ this.Class( 'Foo',
+ {
+ 'abstract foo': [],
+ } );
+ }
+ catch ( e )
+ {
+ this.assertOk(
+ e.message.search( 'Foo' ) !== -1,
+ "Abstract class declaration error should contain class name"
+ );
+
+ return;
+ }
+
+ this.assertFail(
+ "Should not be able to declare abstract members unless " +
+ "class is also declared as abstract"
+ );
+ },
+
+
+ /**
+ * Abstract members should be permitted if the class itself is declared
+ * as abstract; converse of above test.
+ */
+ 'Can declare class as abstract': function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut(
+ {
+ 'abstract foo': [],
+ } );
+ }, Error );
+ },
+
+
+ /**
+ * If a class is declared as abstract, it should contain at least one
+ * abstract method. Otherwise, the abstract definition is pointless and
+ * unnecessarily confusing---the whole point of the declaration is
+ * to produce self-documenting code.
+ */
+ 'Abstract classes must contain abstract methods': function()
+ {
+ try
+ {
+ // should fail; class not declared as abstract
+ this.Sut( 'Foo', {} );
+ }
+ catch ( e )
+ {
+ this.assertOk(
+ e.message.search( 'Foo' ) !== -1,
+ "Abstract class declaration error should contain class name"
+ );
+
+ return;
+ }
+
+ this.assertFail(
+ "Abstract classes should contain at least one abstract method"
+ );
+ },
+
+
+ /**
+ * Abstract methods should remain virtual until they are defined.
+ * That is, if a subtype doesn't provide a concrete implementation, it
+ * should still be considered virtual.
+ */
+ 'Abstract methods can be defined concretely by sub-subtypes': function()
+ {
+ var AbstractFoo = this.Sut( 'Foo',
+ {
+ 'abstract foo': [],
+ } ),
+
+ SubAbstractFoo = this.Sut.extend( AbstractFoo, {} );
+
+ var Class = this.Class;
+ this.assertDoesNotThrow( function()
+ {
+ Class.extend( SubAbstractFoo,
+ {
+ // we should NOT need the override keyword for concrete
+ // implementations of abstract super methods
+ 'foo': function() {},
+ } )
+ }, Error );
+ },
+
+
+ /**
+ * Just as Class contains an extend method, so should AbstractClass.
+ */
+ 'Abstract class extend method returns new class': function()
+ {
+ this.assertEqual( typeof this.Sut.extend, 'function',
+ "AbstractClass contains extend method"
+ );
+
+ this.assertOk(
+ this.Class.isClass(
+ this.Sut.extend( { 'abstract foo': [] } )
+ ),
+ "Abstract class extend method returns class"
+ );
+ },
+
+
+ /**
+ * Just as Class contains an implement method, so should AbstractClass.
+ * We test implementation further on in this test case.
+ */
+ 'Abstract class contains implement method': function()
+ {
+ this.assertEqual( typeof this.Sut.implement, 'function',
+ "AbstractClass contains implement method"
+ );
+ },
+
+
+ /**
+ * All classes should have a method to determine if they are abstract.
+ * We test specific cases below.
+ */
+ 'All classes have an isAbstract() method': function()
+ {
+ this.assertEqual(
+ typeof ( this.Class( {} ).isAbstract ),
+ 'function'
+ );
+ },
+
+
+ /**
+ * For this test, note that (as was tested above) a class containing
+ * abstract members must be declared as abstract; therefore, this test
+ * extends to assert that classes with no abstract methods are not
+ * considered to be abstract.
+ */
+ 'Concrete classes are not considered to be abstract': function()
+ {
+ this.assertOk( !( this.Class( {} ).isAbstract() ) );
+ },
+
+
+ /**
+ * In the same spirit as the preceding test, this extends to asserting
+ * that a class containing abstract methods must be considered to be
+ * abstract.
+ */
+ 'Abstract classes are considered to be abstract': function()
+ {
+ this.assertOk(
+ this.Sut( { 'abstract method': [] } ).isAbstract()
+ );
+ },
+
+
+ /**
+ * In the spirit of the aforementioned, subtypes that do not provide
+ * concrete definitions for *all* abstract methods of their supertype
+ * must too be considered to be abstract.
+ */
+ 'Subtypes are abstract if no concrete method is provided': function()
+ {
+ var Base = this.Sut(
+ {
+ 'abstract foo': [],
+ 'abstract bar': [],
+ } );
+
+ this.assertOk(
+ this.Sut.extend( Base,
+ {
+ // only provide concrete impl. for a single method; `bar' is
+ // still abstract
+ foo: function() {}
+ } ).isAbstract()
+ );
+ },
+
+
+ /**
+ * Ensure that a subtype is not considered to be abstract if it provides
+ * concrete definitions of each of its supertype's abstract methods.
+ */
+ 'Subtypes are not considered abstract if concrete methods are provided':
+ function()
+ {
+ var Base = this.Sut(
+ {
+ 'abstract foo': [],
+ 'abstract bar': [],
+ } );
+
+ this.assertOk(
+ this.Class.extend( Base,
+ {
+ // provide concrete impls. for both
+ foo: function() {},
+ bar: function() {},
+ } ).isAbstract() === false
+ );
+ },
+
+
+ /**
+ * Since an abstract class does not provide a complete object
+ * description, it cannot be instantiated.
+ */
+ 'Abstract classes cannot be instantiated': function()
+ {
+ var Sut = this.Sut;
+ this.assertThrows( function()
+ {
+ Sut( { 'abstract foo': [] } )();
+ }, Error );
+ },
+
+
+ /**
+ * However, a concrete subtype of an abstract class may be instantiated.
+ * Otherwise abstract classes would be pretty useless.
+ */
+ 'Concrete subtypes of abstract classes can be instantiated': function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut( { 'abstract foo': [] } )
+ .extend( { foo: function() {} } )
+ ();
+ }, Error );
+ },
+
+
+ /**
+ * Even though an abstract class itself cannot be instantiated, its
+ * constructor may still be inherited (and therefore invoked) through
+ * concrete subtypes.
+ */
+ 'Can call constructors of abstract supertypes': function()
+ {
+ var ctor_called = false;
+ this.Sut(
+ {
+ __construct: function() { ctor_called = true; },
+ 'abstract foo': [],
+ } ).extend( { foo: function() {} } )();
+
+ this.assertOk( ctor_called );
+ },
+
+
+ /**
+ * Abstract methods declare an API strictly for the purpose of ensuring
+ * that subtypes are all compatible with respect to that particular
+ * field; parameter count, therefore, should be enforced to point out
+ * potential bugs to developers. Whether or not the subtype makes use of
+ * a particular argument is a separate and unrelated issue.
+ */
+ 'Concrete methods must implement the proper number of parameters':
+ function()
+ {
+ var Sut = this.Sut;
+ this.assertThrows( function()
+ {
+ // concrete implementation missing parameter `two'
+ Sut( { 'abstract foo': [ 'one', 'two' ] } )
+ .extend( { foo: function( one ) {} } );
+ }, Error );
+ },
+
+
+ /**
+ * It may be the case that a subtype wishes to provide a new definition
+ * for a particular abstract method---without providing a concrete
+ * implementation---to add additional parameters. However, to remain
+ * compatible with the supertype, that implementation must provide at
+ * least the same number of arguments as the respective method of the
+ * supertype.
+ *
+ * This tests the error condition; see below for the complement.
+ */
+ 'Abstract methods of subtypes must declare compatible parameter count':
+ function()
+ {
+ var Sut = this.Sut;
+ this.assertThrows( function()
+ {
+ Sut.extend( Sut( { 'abstract foo': [ 'one' ] } ),
+ {
+ // incorrect number of arguments
+ 'abstract foo': [],
+ } );
+ }, TypeError );
+ },
+
+
+ /**
+ * Complements the above test to ensure that compatible abstract
+ * overrides are permitted.
+ */
+ 'Abstract members may implement more parameters than supertype':
+ function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut.extend( Sut( { 'abstract foo': [ 'one' ] } ),
+ {
+ // one greater
+ 'abstract foo': [ 'one', 'two' ],
+ } );
+ }, Error );
+ },
+
+
+ /**
+ * While this may not necessarily be sensical in all situations, it may
+ * be useful for documentation.
+ */
+ 'Abstract members may implement equal parameters to supertype':
+ function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut.extend( Sut( { 'abstract foo': [ 'one' ] } ),
+ {
+ // same number
+ 'abstract foo': [ 'one' ],
+ } );
+ }, Error );
+ },
+
+
+ /**
+ * This test just ensures consistency by ensuring that an empty
+ * parameter definition for abstract methods imposes no parameter count
+ * requirement on its concrete definition.
+ */
+ 'Concrete methods have no parameter requirement with empty definition':
+ function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut( { 'abstract foo': [] } ).extend(
+ {
+ foo: function() {}
+ } );
+ }, Error );
+ },
+
+
+ /**
+ * An abstract method is represented by an array listing its parameters
+ * (that must be implemented by concrete definitions).
+ */
+ 'Abstract methods must be declared as arrays': function()
+ {
+ var Class = this.Class;
+
+ this.assertThrows( function()
+ {
+ // likely demonstrates misunderstanding of the syntax
+ Class.extend( { 'abstract foo': function() {} } );
+ }, TypeError, "Abstract method cannot be declared as a function" );
+
+ this.assertThrows( function()
+ {
+ // might be common mistake for attempting to denote a single
+ // parameter; pure speculation.
+ Class.extend( { 'abstract foo': 'scalar' } );
+ }, TypeError, "Abstract method cannot be declared as a scalar" );
+ },
+
+
+ /**
+ * There was an issue where the object holding the abstract methods list
+ * was not checking for methods by using hasOwnProperty(). Therefore, if
+ * a method such as toString() was defined, it would be matched in the
+ * abstract methods list. As such, the abstract methods count would be
+ * decreased, even though it was not an abstract method to begin with
+ * (nor was it removed from the list, because it was never defined in
+ * the first place outside of the prototype).
+ *
+ * This negative number !== 0, which causes a problem when checking to
+ * ensure that there are 0 abstract methods. We check explicitly for 0
+ * because, if it's non-zero, then it's either abstract or something is
+ * wrong. Negative is especially wrong. It should never be negative!
+ */
+ 'Does not recognize object prototype members as abstract': function()
+ {
+ var Sut = this.Sut;
+ this.assertDoesNotThrow( function()
+ {
+ Sut( { 'abstract method': [] } ).extend(
+ {
+ // concrete, so the result would otherwise not be abstract
+ method: function() {},
+
+ // the problem---this exists in the prototype chain of every
+ // object
+ 'toString': function() {},
+ })();
+ }, Error );
+ },
+
+
+ /**
+ * Ensure we support named abstract class extending
+ */
+ 'Can create named abstract subtypes': function()
+ {
+ this.assertOk(
+ this.Sut( 'Named' ).extend(
+ this.Sut( { 'abstract foo': [] } ),
+ {}
+ ).isAbstract()
+ );
+ },
+
+
+ /**
+ * Abstract classes, when extended, should yield a concrete class by
+ * default. Otherwise, the user should once again use AbstractClass to
+ * clearly state that the subtype is abstract. Remember:
+ * self-documenting.
+ */
+ 'Calling extend() on abstract class yields concrete class': function()
+ {
+ var Foo = this.Sut( { 'abstract foo': [] } ),
+ cls_named = this.Sut( 'NamedSubFoo' ).extend( Foo, {} ),
+ cls_anon = this.Sut.extend( Foo, {} );
+
+ var Class = this.Class;
+
+ // named
+ this.assertThrows(
+ function()
+ {
+ // should throw an error, since we're not declaring it as
+ // abstract and we're not providing a concrete impl
+ Class.isAbstract( cls_named.extend( {} ) );
+ },
+ TypeError,
+ "Extending named abstract classes should be concrete"
+ );
+
+ // anonymous
+ this.assertThrows(
+ function()
+ {
+ // should throw an error, since we're not declaring it as abstract
+ // and we're not providing a concrete impl
+ Class.isAbstract( cls_anon.extend( {} ) );
+ },
+ TypeError,
+ "Extending anonymous abstract classes should be concrete"
+ );
+ },
+
+
+ /**
+ * Extending an abstract class after an implement() should still result
+ * in an abstract class. Essentially, we are testing to ensure that the
+ * extend() method is properly wrapped to flag the resulting class as
+ * abstract. This was a bug.
+ */
+ 'Implementing interfaces will preserve abstract class definition':
+ function()
+ {
+ var Sut = this.Sut,
+ Interface = this.require( 'interface' );
+
+ this.assertOk(
+ // if not considered abstract, extend() will fail, as it will
+ // contain abstract member foo
+ Sut( 'TestImplExtend' )
+ .implement( Interface( { foo: [] } ) )
+ .extend( {} )
+ .isAbstract()
+ );
+ },
+} );
diff --git a/test/test-class-abstract.js b/test/test-class-abstract.js
deleted file mode 100644
index 9837be2..0000000
--- a/test/test-class-abstract.js
+++ /dev/null
@@ -1,468 +0,0 @@
-/**
- * Tests abstract classes
- *
- * Copyright (C) 2010, 2011, 2012, 2013 Mike Gerwitz
- *
- * This file is part of GNU ease.js.
- *
- * ease.js is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-var common = require( './common' ),
- assert = require( 'assert' ),
- util = common.require( 'util' ),
-
- Class = common.require( 'class' ),
- AbstractClass = common.require( 'class_abstract' ),
- Interface = common.require( 'interface' )
-;
-
-
-/**
- * In order to ensure the code documents itself, we should require that all
- * classes containing abstract members must themselves be declared as abstract.
- * Otherwise, you are at the mercy of the developer's documentation/comments to
- * know whether or not the class is indeed abstract without looking through its
- * definition.
- */
-( function testMustDeclareClassesWithAbstractMembersAsAbstract()
-{
- try
- {
- // should fail; class not declared as abstract
- Class( 'Foo',
- {
- 'abstract foo': [],
- } );
- }
- catch ( e )
- {
- assert.ok(
- e.message.search( 'Foo' ) !== -1,
- "Abstract class declaration error should contain class name"
- );
-
- return;
- }
-
- assert.fail(
- "Should not be able to declare abstract members unless class is also " +
- "declared as abstract"
- );
-} )();
-
-
-/**
- * Abstract members should be permitted if the class itself is declared as
- * abstract
- */
-( function testCanDeclareClassAsAbstract()
-{
- AbstractClass(
- {
- 'abstract foo': [],
- } );
-} )();
-
-
-/**
- * If a class is declared as abstract, it should contain at least one abstract
- * method. Otherwise, the abstract definition is pointless and unnecessarily
- * confusing. The whole point of the declaration is self-documenting code.
- */
-( function testAbstractClassesMustContainAbstractMethods()
-{
- try
- {
- // should fail; class not declared as abstract
- AbstractClass( 'Foo', {} );
- }
- catch ( e )
- {
- assert.ok(
- e.message.search( 'Foo' ) !== -1,
- "Abstract class declaration error should contain class name"
- );
-
- return;
- }
-
- assert.fail(
- "Abstract classes should contain at least one abstract method"
- );
-} )();
-
-
-/**
- * Abstract methods should remain virtual until they are overridden. That is, if
- * a subtype doesn't provide a concrete implementation, it should still be
- * considered virtual.
- */
-( function testAbstractMethodsCanBeOverriddenBySubSubTypes()
-{
- var AbstractFoo = AbstractClass( 'Foo',
- {
- 'abstract foo': [],
- } ),
-
- SubAbstractFoo = AbstractClass.extend( AbstractFoo, {} ),
-
- ConcreteFoo = Class.extend( SubAbstractFoo,
- {
- // we should NOT need the override keyword for concrete
- // implementations of abstract super methods
- 'foo': function() {},
- } )
- ;
-} )();
-
-
-/**
- * Just as Class contains an extend method, so should AbstractClass.
- */
-( function testAbstractClassExtendMethodReturnsNewClass()
-{
- assert.ok( typeof AbstractClass.extend === 'function',
- "AbstractClass contains extend method"
- );
-
- assert.ok(
- Class.isClass(
- AbstractClass.extend( { 'abstract foo': [] } )
- ),
- "Abstract class extend method returns class"
- );
-} )();
-
-
-/**
- * Just as Class contains an implement method, so should AbstractClass.
- */
-( function testAbstractClassContainsImplementMethod()
-{
- assert.ok( typeof AbstractClass.implement === 'function',
- "AbstractClass contains implement method"
- );
-} )();
-
-
-// not abstract
-var Foo = Class.extend( {} );
-
-// abstract (ctor_called is not a class member to ensure that visibility bugs do
-// not impact our test)
-var ctor_called = false,
- AbstractFoo = AbstractClass.extend(
- {
- __construct: function()
- {
- ctor_called = true;
- },
-
- 'abstract method': [ 'one', 'two', 'three' ],
-
- 'abstract second': [],
- })
-;
-
-// still abstract (didn't provide a concrete implementation of both abstract
-// methods)
-var SubAbstractFoo = AbstractClass.extend( AbstractFoo,
-{
- second: function()
- {
- },
-});
-
-// concrete
-var ConcreteFoo = Class.extend( AbstractFoo,
-{
- method: function( one, two, three )
- {
- // prevent Closure Compiler from optimizing the arguments away, causing
- // a definition failure
- return [ one, two, three ];
- },
-
- second: function()
- {
- },
-});
-
-
-/**
- * All classes should have a method to determine if they are abstract.
- */
-( function testAllClassesHaveAMethodToDetmineIfAbstract()
-{
- assert.ok(
- ( Class( {} ).isAbstract instanceof Function ),
- "All classes should have an isAbstract() method"
- );
-} )();
-
-
-( function testClassesAreNotConsideredToBeAbstractIfTheyHaveNoAbstractMethods()
-{
- assert.equal(
- Class( {} ).isAbstract(),
- false,
- "Classes are not abstract if they contain no abstract methods"
- );
-} )();
-
-
-( function testClassesShouldBeConsideredAbstractIfTheyContainAbstractMethods()
-{
- assert.equal(
- AbstractFoo.isAbstract(),
- true,
- "Classes should be considered abstract if they contain any abstract methods"
- );
-} )();
-
-
-( function testSubtypesAreAbstractIfNoConcreteMethodIsProvided()
-{
- assert.equal(
- SubAbstractFoo.isAbstract(),
- true,
- "Subtypes of abstract types are abstract if they don't provide a " +
- "concrete implementation for all abstract methods"
- );
-} )();
-
-
-( function testSubtypesAreNotConisderedAbstractIfConcreteImplIsProvided()
-{
- assert.equal(
- ConcreteFoo.isAbstract(),
- false,
- "Subtypes of abstract types are not abstract if they provide concrete " +
- "implementations of all abstract methods"
- );
-} )();
-
-
-( function testAbstractClassesCannotBeInstantiated()
-{
- assert['throws']( function()
- {
- // both should fail
- AbstractFoo();
- SubAbstractFoo();
- }, Error, "Abstract classes cannot be instantiated" );
-} )();
-
-
-( function testConcreteSubclassesCanBeInstantiated()
-{
- assert.ok(
- ConcreteFoo(),
- "Concrete subclasses can be instantiated"
- );
-} )();
-
-
-( function testCanCallConstructorsOfAbstractSupertypes()
-{
- ctor_called = false;
- ConcreteFoo();
-
- assert.equal(
- ctor_called,
- true,
- "Can call constructors of abstract supertypes"
- );
-} )();
-
-
-( function testConcreteMethodsMustImplementTheProperNumberOfArguments()
-{
- assert['throws']( function()
- {
- AbstractFoo.extend(
- {
- // incorrect number of arguments
- method: function()
- {
- },
- });
- }, Error, "Concrete methods must implement the proper number of argments" );
-} )();
-
-
-( function testAbstractMethodsOfSubtypesMustImplementProperNumberOfArguments()
-{
- assert['throws'](
- function()
- {
- AbstractFoo.extend(
- {
- // incorrect number of arguments
- 'abstract method': [],
- });
- },
- TypeError,
- "Abstract methods of subtypes must implement the proper number of " +
- "argments"
- );
-} )();
-
-
-( function testAbstractMembersMayImplementMoreArgumentsThanSupertype()
-{
- assert.doesNotThrow(
- function()
- {
- AbstractClass.extend( AbstractFoo,
- {
- // incorrect number of arguments
- 'abstract method': [ 'one', 'two', 'three', 'four' ],
- });
- },
- Error,
- "Abstract methods of subtypes may implement additional arguments, " +
- "so long as they implement at least the required number of " +
- "arguments as defined by it supertype"
- );
-} )();
-
-
-( function testConcreteMethodsHaveNoArgumentRequirementsIfNoDefinitionGiven()
-{
- assert.doesNotThrow(
- function()
- {
- AbstractClass.extend( AbstractFoo,
- {
- second: function( foo )
- {
- },
- });
- },
- Error,
- "Concrete methods needn't implement the proper number of arguments " +
- "if no definition was provided"
- );
-} )();
-
-
-( function testAbstractMethodsMustBeDeclaredAsArrays()
-{
- assert['throws']( function()
- {
- Class.extend(
- {
- // not an array (invalid)
- 'abstract foo': 'scalar',
- } );
- }, TypeError, "Abstract methods must be declared as arrays" );
-} )();
-
-
-/**
- * There was an issue where the object holding the abstract methods list was not
- * checking for methods by using hasOwnProperty(). Therefore, if a method such
- * as toString() was defined, it would be matched in the abstract methods list.
- * As such, the abstract methods count would be decreased, even though it was
- * not an abstract method to begin with (nor was it removed from the list,
- * because it was never defined in the first place outside of the prototype).
- *
- * This negative number !== 0, which causes a problem when checking to ensure
- * that there are 0 abstract methods. We check explicitly for 0 for two reasons:
- * (a) it's faster than <, and (b - most importantly) if it's non-zero, then
- * it's either abstract or something is wrong. Negative is especially wrong. It
- * should never be negative!
- */
-( function testDoesNotRecognizeObjectPrototypeMembersAsAbstractWhenDefining()
-{
- assert.doesNotThrow( function()
- {
- Class.extend( SubAbstractFoo,
- {
- // concrete, so the result would otherwise not be abstract (return
- // args so they're not optimized away during compile)
- 'method': function( _, __, ___ ) { return [ _, __, ___ ]; },
-
- // the problem
- 'toString': function() {},
- })();
- }, Error, "Should not throw error if overriding a prototype method" );
-} )();
-
-
-/**
- * Ensure we support named abstract class extending
- */
-( function testCanCreateNamedAbstractSubtypes()
-{
- assert.doesNotThrow( function()
- {
- var cls = AbstractClass( 'NamedSubFoo' ).extend( AbstractFoo, {} );
- }, Error, "Can create named abstract subtypes" );
-} )();
-
-
-/**
- * Abstract classes, when extended, should yield a concrete class by default.
- * Otherwise, the user should once again use AbstractClass to clearly state that
- * the subtype is abstract.
- */
-( function testExtendingAbstractClassIsNotAbstractByDefault()
-{
- var cls_named = AbstractClass( 'NamedSubFoo' ).extend( AbstractFoo, {} ),
- anon_named = AbstractClass.extend( AbstractFoo, {} );
-
- // named
- assert['throws'](
- function()
- {
- // should throw an error, since we're not declaring it as abstract
- // and we're not providing a concrete impl
- Class.isAbstract( cls_named.extend( {} ) );
- },
- TypeError,
- "Extending named abstract classes should be concrete by default"
- );
-
- // anonymous
- assert['throws'](
- function()
- {
- // should throw an error, since we're not declaring it as abstract
- // and we're not providing a concrete impl
- Class.isAbstract( AbstractFoo.extend( {} ) );
- },
- TypeError,
- "Extending anonymous abstract classes should be concrete by default"
- );
-} )();
-
-
-/**
- * Extending an abstract class after an implement() should still result in an
- * abstract class. Essentially, we are testing to ensure that the extend()
- * method is properly wrapped to flag the resulting class as abstract. This was
- * a bug.
- */
-( function testImplementingInterfacesWillPreserveAbstractClassDeclaration()
-{
- // if not considered abstract, extend() will fail, as it will contain
- // abstract member foo
- AbstractClass( 'TestImplExtend' )
- .implement( Interface( { foo: [] } ) )
- .extend( {} );
-} )()
-