1
0
Fork 0

[#25] Began moving test-class-implement over to new test case system

closure/master
Mike Gerwitz 2011-11-02 22:32:45 -04:00
parent f15fa03a3b
commit 79d0c4a62c
1 changed files with 205 additions and 195 deletions

View File

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