diff --git a/test/test-class-implement.js b/test/test-class-implement.js index 00cf1b3..0ee77ba 100644 --- a/test/test-class-implement.js +++ b/test/test-class-implement.js @@ -45,207 +45,217 @@ var Type = Interface.extend( { ; -( function testClassExportsContainImplementMethodToExtendFromNoBaseClass() +require( 'common' ).testCase( { - assert.ok( - ( Class.implement instanceof Function ), - "Class provides method to implement interfaces" - ); -} )(); - - -( function testClassObjectsContainImplementMethodToImplementUsingItselfAsABase() -{ - assert.ok( - ( PlainFoo.implement instanceof Function ), - "Classes contain an implement() method" - ); -} )(); - - -( function testCanImplementInterfaceFromAnEmptyBase() -{ - assert.doesNotThrow( function() + 'Class exports contain implement method for no base class': function() { - Class.implement( Type, Type2 ); - }, Error, "Class can implement interfaces" ); -} )(); + this.assertOk( + ( Class.implement instanceof Function ), + "Class provides method to implement interfaces" + ); + }, -/** - * Initially, the implement() method returned an abstract class. However, it - * doesn't make sense to create a class without any actual definition (and - * there's other implementation considerations that caused this route to be - * taken). One wouldn't do "class Foo implements Type", and not provide any - * body. - * - * Therefore, implement() should return nothing useful until extend() is called - * on it. - */ -( function testResultOfImplementIsNotUsableAsAClass() -{ - var result = Class.implement( Type ); - - assert.equal( - ( Class.isClass( result ) ), - false, - "Result of implement operation on class is not usable as a Class" - ); -} )(); - - -/** - * As a consequence of the above, we must extend with an empty definition - * (base) in order to get our abstract class. - */ -( function testAbstractMethodsCopiedIntoNewClassUsingEmptyBase() -{ - Foo = AbstractClass.implement( Type, Type2 ).extend( {} ); - - assert.ok( - ( ( Foo.prototype.foo instanceof Function ) - && ( Foo.prototype.foo2 instanceof Function ) - ), - "Abstract methods are copied into the new class prototype (empty base)" - ); -} )(); - - -( function testCanImplementInterfaceAtopAnExistingClass() -{ - assert.doesNotThrow( function() + 'Clsss object contains implement method for self as base': function() { - PlainFoo.implement( Type, Type2 ); - }, Error, "Classes can implement interfaces" ); -} )(); + this.assertOk( + ( PlainFoo.implement instanceof Function ), + "Classes contain an implement() method" + ); + }, -/** - * Ensure the same system mentioned above also applies to the extend() method on - * existing classes - */ -( function testImplementingInterfaceAtopExistingClassIsNotUsableByDefault() -{ - var result = PlainFoo.implement( Type ); - - assert.equal( - ( Class.isClass( result ) ), - false, - "Result of implementing interfaces on an existing base is not " + - "usable as a Class" - ); -} )(); - - -( function testAbstractMethodsCopiedIntoNewClassUsingExistingBase() -{ - PlainFoo2 = AbstractClass.implement( Type, Type2 ).extend( PlainFoo, {} ); - - assert.ok( - ( ( PlainFoo2.prototype.foo instanceof Function ) - && ( PlainFoo2.prototype.foo2 instanceof Function ) - ), - "Abstract methods are copied into the new class prototype (concrete base)" - ); -} )(); - - -/** - * Since interfaces can contain only abstract methods, it stands to reason that - * any class implementing an interface without providing any concrete methods - * should be abstract by default. - */ -( function testClassesImplementingInterfacesAreConsideredAbstractByDefault() -{ - assert.equal( - ( Foo.isAbstract() && PlainFoo2.isAbstract() ), - true, - "Classes that implements interface(s) are considered abstract if the " + - "implemented methods have no concrete implementations" - ); -} ) (); - - -( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces() -{ - // concrete implementation so that we can instantiate it - var ConcreteFoo = Foo.extend( - { - 'foo': function() {}, - 'foo2': function() {}, - }), - - concrete_inst = ConcreteFoo() - ; - - assert.ok( - ( concrete_inst.isInstanceOf( Type ) - && concrete_inst.isInstanceOf( Type2 ) - ), - "Instances of classes implementing interfaces are considered to be " + - "instances of the implemented interfaces" - ); - - assert.equal( - ConcreteFoo.isAbstract(), - false, - "Concrete implementations are not considered to be abstract" - ); -} )(); - - -/** - * Consider the following scenario: - * - * MyClass.implement( Type ).extend( MyOtherClass, {} ); - * - * What the above is essentially saying is: "I'd like to extend MyClass by - * implementing Type. Oh, no, wait, I'd actually like it to extend - * MyOtherClass." That doesn't make sense! Likely, it's unintended. Prevent - * confusion and bugs. Throw an error. - */ -( function testCannotSpecifyParentAfterImplementingAtopExistingClass() -{ - assert.throws( function() - { - // should not be permitted - PlainFoo.implement( Type, Type2 ).extend( PlainFoo2, {} ); - }, - Error, - "Cannot specify new parent for extend() when implementing from " + - "existing class" - ); -} )(); - - -/** - * Opposite of the above test. If a parent wasn't specified to begin with, then - * we're fine to specify it in extend(). - */ -( function testCanSpecifyParentIfImplementingAtopEmptyClass() -{ - assert.doesNotThrow( - function() - { - // this /should/ work - AbstractClass.implement( Type ).extend( PlainFoo, {} ); - }, - Error, - "Can specify parent for exetnd() when implementing atop an " + - "empty base" - ); -} )(); - - -/** - * If more than two arguments are given to extend(), then the developer likely - * does not understand the API. Throw an error to prevent some bugs/confusion. - */ -( function testThrowsExceptionIfExtendContainsTooManyArguments() -{ - assert.throws( function() + 'Can implement interface from an empty base': function() { - Class.implement( Type ).extend( PlainFoo, {}, 'extra' ); - }, Error, "extend() after implementing accepts no more than two args" ); -} )(); + this.assertDoesNotThrow( function() + { + Class.implement( Type, Type2 ); + }, Error, "Class can implement interfaces" ); + }, + + + /** + * Initially, the implement() method returned an abstract class. However, it + * doesn't make sense to create a class without any actual definition (and + * there's other implementation considerations that caused this route to be + * taken). One wouldn't do "class Foo implements Type", and not provide any + * body. + * + * Therefore, implement() should return nothing useful until extend() is + * called on it. + */ + 'Result of implement is not usable as a class': function() + { + var result = Class.implement( Type ); + + this.assertEqual( + ( Class.isClass( result ) ), + false, + "Result of implement operation on class is not usable as a Class" + ); + }, + + + /** + * As a consequence of the above, we must extend with an empty definition + * (base) in order to get our abstract class. + */ + 'Abstract methods are copied into new class using empty base': function() + { + Foo = AbstractClass.implement( Type, Type2 ).extend( {} ); + + this.assertOk( + ( ( Foo.prototype.foo instanceof Function ) + && ( Foo.prototype.foo2 instanceof Function ) + ), + "Abstract methods are copied into the new class prototype " + + "(empty base)" + ); + }, + + + 'Can implement interface atop an exist class': function() + { + this.assertDoesNotThrow( function() + { + PlainFoo.implement( Type, Type2 ); + }, Error, "Classes can implement interfaces" ); + }, + + + /** + * Ensure the same system mentioned above also applies to the extend() + * method on existing classes + */ + 'Implementing interface atop existing class not usable by default': + function() + { + var result = PlainFoo.implement( Type ); + + this.assertEqual( + ( Class.isClass( result ) ), + false, + "Result of implementing interfaces on an existing base is not " + + "usable as a Class" + ); + }, + + + 'Abstract method copied into new class using existing base': function() + { + PlainFoo2 = AbstractClass.implement( Type, Type2 ) + .extend( PlainFoo, {} ); + + this.assertOk( + ( ( PlainFoo2.prototype.foo instanceof Function ) + && ( PlainFoo2.prototype.foo2 instanceof Function ) + ), + "Abstract methods are copied into the new class prototype " + + "(concrete base)" + ); + }, + + + /** + * Since interfaces can contain only abstract methods, it stands to reason + * that any class implementing an interface without providing any concrete + * methods should be abstract by default. + */ + 'Classes implementing interfaces are considered abstract by default': + function() + { + this.assertEqual( + ( Foo.isAbstract() && PlainFoo2.isAbstract() ), + true, + "Classes that implements interface(s) are considered abstract if " + + "the implemented methods have no concrete implementations" + ); + }, + + + 'Instances of classes are instances of their implemented interfaces': + function() + { + // concrete implementation so that we can instantiate it + var ConcreteFoo = Foo.extend( + { + 'foo': function() {}, + 'foo2': function() {}, + }), + + concrete_inst = ConcreteFoo() + ; + + this.assertOk( + ( concrete_inst.isInstanceOf( Type ) + && concrete_inst.isInstanceOf( Type2 ) + ), + "Instances of classes implementing interfaces are considered to " + + "be instances of the implemented interfaces" + ); + + this.assertEqual( + ConcreteFoo.isAbstract(), + false, + "Concrete implementations are not considered to be abstract" + ); + }, + + + /** + * Consider the following scenario: + * + * MyClass.implement( Type ).extend( MyOtherClass, {} ); + * + * What the above is essentially saying is: "I'd like to extend MyClass by + * implementing Type. Oh, no, wait, I'd actually like it to extend + * MyOtherClass." That doesn't make sense! Likely, it's unintended. Prevent + * confusion and bugs. Throw an error. + */ + 'Cannot specify parent after implementing atop existing class': function() + { + this.assertThrows( function() + { + // should not be permitted + PlainFoo.implement( Type, Type2 ).extend( PlainFoo2, {} ); + }, + Error, + "Cannot specify new parent for extend() when implementing from " + + "existing class" + ); + }, + + + /** + * Opposite of the above test. If a parent wasn't specified to begin with, + * then we're fine to specify it in extend(). + */ + 'Can specify parent if implementing atop empty class': function() + { + this.assertDoesNotThrow( + function() + { + // this /should/ work + AbstractClass.implement( Type ).extend( PlainFoo, {} ); + }, + Error, + "Can specify parent for exetnd() when implementing atop an " + + "empty base" + ); + }, + + + /** + * If more than two arguments are given to extend(), then the developer + * likely does not understand the API. Throw an error to prevent some + * bugs/confusion. + */ + 'Throws exception if extend contains too many arguments': function() + { + this.assertThrows( function() + { + Class.implement( Type ).extend( PlainFoo, {}, 'extra' ); + }, Error, "extend() after implementing accepts no more than two args" ); + }, +} );