/**
* Tests class naming
*
* Copyright (C) 2011, 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 .
*
* TODO: This would benefit from an assertion that combines an exception
* test with an assertion on is message.
*/
require( 'common' ).testCase(
{
caseSetUp: function()
{
this.Sut = this.require( 'class' );
this.AbstractClass = this.require( 'class_abstract' );
},
/**
* Classes may be named by passing the name as the first argument to the
* module
*/
'Class defined with name is returned as a valid class': function()
{
this.assertOk(
this.Sut.isClass( this.Sut( 'Foo', {} ) )
);
},
/**
* The class definition must be an object, which is equivalent to the
* class body
*/
'Named class definition requires that field definition be an object':
function()
{
var name = 'Foo';
try
{
this.Sut( name, 'Bar' );
// if all goes well, we'll never get to this point
this.assertFail(
"Second argument to named class must be the definition"
);
}
catch ( e )
{
this.assertNotEqual(
e.message.match( name ),
null,
"Error string contains class name"
);
}
},
/**
* Extraneous arguments likely indicate a misunderstanding of the API
*/
'Named class definition is strict on argument count': function()
{
var name = 'Foo',
args = [ name, {}, 'extra' ]
;
// we should be permitted only two arguments
try
{
this.Sut.apply( null, args );
// we should not get to this line (an exception should be thrown
// due to too many arguments)
this.assertFail(
"Should accept only two arguments when creating named class"
);
}
catch ( e )
{
var errstr = e.message;
this.assertNotEqual(
errstr.match( name ),
null,
"Named class error should provide name of class"
);
this.assertNotEqual(
errstr.match( args.length + ' given' ),
null,
"Named class error should provide number of given arguments"
);
}
},
/**
* By default, anonymous classes should just state that they are a class
* when they are converted to a string
*/
'Converting anonymous class to string yields class string': function()
{
// concrete
this.assertEqual(
this.Sut( {} ).toString(),
'(Class)'
);
},
/**
* Similar concept to above
*/
'Converting abstract anonymous class to string yields class string':
function()
{
this.assertEqual(
this.AbstractClass( { 'abstract foo': [] } ).toString(),
'(AbstractClass)'
);
},
/**
* If the class is named, then the name should be presented when it is
* converted to a string
*/
'Converting named class to string yields string containing name':
function()
{
var name = 'Foo';
// concrete
this.assertEqual(
this.Sut( name, {} ).toString(),
name
);
// abstract
this.assertEqual(
this.AbstractClass( name, { 'abstract foo': [] } ).toString(),
name
);
},
/**
* Class instances are displayed differently than uninstantiated
* classes. Mainly, they output that they are an object, in addition to
* the class name.
*/
'Converting class instance to string yields instance string':
function()
{
var name = 'Foo',
anon = this.Sut( {} )(),
named = this.Sut( name, {} )()
;
this.assertEqual( anon.toString(), '#' );
this.assertEqual( named.toString(), '#<' + name + '>' );
},
/**
* In order to accommodate syntax such as extending classes, ease.js
* supports staging class names. This will return an object that
* operates exactly like the normal Class module, but will result in a
* named class once the class is created.
*/
'Can create named class using staging method': function()
{
var name = 'Foo',
named = this.Sut( name ).extend( {} );
// ensure what was returned is a valid class
this.assertEqual(
this.Sut.isClass( named ),
true,
"Named class generated via staging method is considered to " +
"be a valid class"
);
// was the name set?
this.assertEqual(
named.toString(),
name,
"Name is set on named clas via staging method"
);
},
/**
* We should be able to continue to implement interfaces using the
* staging method just as we would without it.
*/
'Can implement interfaces using staging method': function()
{
var name = 'Foo',
Interface = this.require( 'interface' ),
namedi = this.Sut( name )
.implement( Interface( {} ) )
.extend( {} );
// we should also be able to implement interfaces
this.assertEqual(
this.Sut.isClass( namedi ),
true,
"Named class generated via staging method, implementing an " +
"interface, is considered to be a valid class"
);
this.assertEqual(
namedi.toString(),
name,
"Name is set on named class via staging method when implementing"
);
},
/**
* Similarily, the extend method should retain its ability to extend
* existing classes.
*/
'Can extend existing classes using staging method': function()
{
var name = 'Foo',
named = this.Sut( name ).extend( {} ),
namede = this.Sut( name ).extend( named, {} );
this.assertEqual( this.Sut.isClass( namede ), true );
this.assertOk( this.Sut.isInstanceOf( named, namede() ) );
this.assertEqual( namede.toString(), name );
},
/**
* The class name should be provided in the error thrown when attempting
* to instantiate an abstract class, if it's available
*/
'Class name is given when attempting to instantiate abstract class':
function()
{
var name = 'Foo';
try
{
this.Sut( name, { 'abstract foo': [] } )();
// we're not here to test to make sure it is thrown, but if it's
// not, then there's likely a problem
this.assertFail(
"Was expecting instantiation error; there's a bug somewhere"
);
}
catch ( e )
{
this.assertNotEqual(
e.message.match( name ),
null,
"Abstract class instantiation error should contain " +
"class name"
);
}
// if no name is provided, then (anonymous) should be indicated
try
{
this.Sut( { 'abstract foo': [] } )();
// we're not here to test to make sure it is thrown, but if it's
// not, then there's likely a problem
this.assertFail(
"Was expecting instantiation error; there's a bug somewhere"
);
}
catch ( e )
{
this.assertNotEqual(
e.message.match( '(anonymous)' ),
null,
"Abstract class instantiation error should recognize " +
"that class is anonymous if no name was given"
);
}
},
} );