diff --git a/test/Trait/ClassVirtualTest.js b/test/Trait/ClassVirtualTest.js index b5d72b6..dbe1242 100644 --- a/test/Trait/ClassVirtualTest.js +++ b/test/Trait/ClassVirtualTest.js @@ -43,135 +43,6 @@ require( 'common' ).testCase( }, - /** - * A trait may implement an interface I for a couple of reasons: to have - * the class mixed into be considered to of type I and to override - * methods. But, regardless of the reason, let's start with the - * fundamentals. - */ - 'Traits may implement an interface': function() - { - var _self = this; - - // simply make sure that the API is supported; nothing more. - this.assertDoesNotThrow( function() - { - _self.Sut.implement( _self.Interface( {} ) ).extend( {} ); - } ); - }, - - - /** - * We would expect that the default behavior of implementing an - * interface I into a trait would create a trait with all abstract - * methods defined by I. - */ - 'Traits implementing interfaces define abstract methods': function() - { - var I = this.Interface( { foo: [], bar: [] } ), - T = this.Sut.implement( I ).extend( {} ); - - var Class = this.Class, - AbstractClass = this.AbstractClass; - - // T should contain both foo and bar as abstract methods, which we - // will test indirectly in the assertions below - - // should fail because of abstract foo and bar - this.assertThrows( function() - { - Class.use( T ).extend( {} ); - } ); - - // should succeed, since we can have abstract methods within an - // abstract class - this.assertDoesNotThrow( function() - { - AbstractClass.use( T ).extend( {} ); - } ); - - // one remaining abstract method - this.assertDoesNotThrow( function() - { - AbstractClass.use( T ).extend( { foo: function() {} } ); - } ); - - // both concrete - this.assertDoesNotThrow( function() - { - Class.use( T ).extend( - { - foo: function() {}, - bar: function() {}, - } ); - } ); - }, - - - /** - * Just as classes implementing interfaces may choose to immediately - * provide concrete definitions for the methods declared in the - * interface (instead of becoming an abstract class), so too may traits. - */ - 'Traits may provide concrete methods for interfaces': function() - { - var called = false; - - var I = this.Interface( { foo: [] } ), - T = this.Sut.implement( I ).extend( - { - foo: function() - { - called = true; - }, - } ); - - var Class = this.Class; - this.assertDoesNotThrow( function() - { - // should invoke concrete foo; class definition should not fail, - // because foo is no longer abstract - Class.use( T ).extend( {} )().foo(); - } ); - - this.assertOk( called ); - }, - - - /** - * Instances of class C mixing in some trait T implementing I will be - * considered to be of type I, since any method of I would either be - * defined within T, or would be implicitly abstract in T, requiring its - * definition within C; otherwise, C would have to be declared astract. - */ - 'Instance of class mixing in trait implementing I is of type I': - function() - { - var I = this.Interface( {} ), - T = this.Sut.implement( I ).extend( {} ); - - this.assertOk( - this.Class.isA( I, this.Class.use( T ).extend( {} )() ) - ); - }, - - - /** - * The API for multiple interfaces should be the same for traits as it - * is for classes. - */ - 'Trait can implement multiple interfaces': function() - { - var Ia = this.Interface( {} ), - Ib = this.Interface( {} ), - T = this.Sut.implement( Ia, Ib ).extend( {} ), - o = this.Class.use( T ).extend( {} )(); - - this.assertOk( this.Class.isA( Ia, o ) ); - this.assertOk( this.Class.isA( Ib, o ) ); - }, - - /** * This is a concept borrowed from Scala: consider class C and trait T, * both implementing interface I which declares method M. T should be diff --git a/test/Trait/InterfaceTest.js b/test/Trait/InterfaceTest.js new file mode 100644 index 0000000..4695239 --- /dev/null +++ b/test/Trait/InterfaceTest.js @@ -0,0 +1,159 @@ +/** + * Tests implementing interfaces in traits + * + * Copyright (C) 2015 Free Software Foundation, Inc. + * + * 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( 'Trait' ); + this.Class = this.require( 'class' ); + this.AbstractClass = this.require( 'class_abstract' ); + this.Interface = this.require( 'interface' ); + }, + + + /** + * A trait may implement an interface I for a couple of reasons: to have + * the class mixed into be considered to of type I and to override + * methods. But, regardless of the reason, let's start with the + * fundamentals. + */ + 'Traits may implement an interface': function() + { + var _self = this; + + // simply make sure that the API is supported; nothing more. + this.assertDoesNotThrow( function() + { + _self.Sut.implement( _self.Interface( {} ) ).extend( {} ); + } ); + }, + + + /** + * We would expect that the default behavior of implementing an + * interface I into a trait would create a trait with all abstract + * methods defined by I. + */ + 'Traits implementing interfaces define abstract methods': function() + { + var I = this.Interface( { foo: [], bar: [] } ), + T = this.Sut.implement( I ).extend( {} ); + + var Class = this.Class, + AbstractClass = this.AbstractClass; + + // T should contain both foo and bar as abstract methods, which we + // will test indirectly in the assertions below + + // should fail because of abstract foo and bar + this.assertThrows( function() + { + Class.use( T ).extend( {} ); + } ); + + // should succeed, since we can have abstract methods within an + // abstract class + this.assertDoesNotThrow( function() + { + AbstractClass.use( T ).extend( {} ); + } ); + + // one remaining abstract method + this.assertDoesNotThrow( function() + { + AbstractClass.use( T ).extend( { foo: function() {} } ); + } ); + + // both concrete + this.assertDoesNotThrow( function() + { + Class.use( T ).extend( + { + foo: function() {}, + bar: function() {}, + } ); + } ); + }, + + + /** + * Just as classes implementing interfaces may choose to immediately + * provide concrete definitions for the methods declared in the + * interface (instead of becoming an abstract class), so too may traits. + */ + 'Traits may provide concrete methods for interfaces': function() + { + var called = false; + + var I = this.Interface( { foo: [] } ), + T = this.Sut.implement( I ).extend( + { + foo: function() + { + called = true; + }, + } ); + + var Class = this.Class; + this.assertDoesNotThrow( function() + { + // should invoke concrete foo; class definition should not fail, + // because foo is no longer abstract + Class.use( T ).extend( {} )().foo(); + } ); + + this.assertOk( called ); + }, + + + /** + * Instances of class C mixing in some trait T implementing I will be + * considered to be of type I, since any method of I would either be + * defined within T, or would be implicitly abstract in T, requiring its + * definition within C; otherwise, C would have to be declared astract. + */ + 'Instance of class mixing in trait implementing I is of type I': + function() + { + var I = this.Interface( {} ), + T = this.Sut.implement( I ).extend( {} ); + + this.assertOk( + this.Class.isA( I, this.Class.use( T ).extend( {} )() ) + ); + }, + + + /** + * The API for multiple interfaces should be the same for traits as it + * is for classes. + */ + 'Trait can implement multiple interfaces': function() + { + var Ia = this.Interface( {} ), + Ib = this.Interface( {} ), + T = this.Sut.implement( Ia, Ib ).extend( {} ), + o = this.Class.use( T ).extend( {} )(); + + this.assertOk( this.Class.isA( Ia, o ) ); + this.assertOk( this.Class.isA( Ib, o ) ); + }, +} );