1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00
jashkenas--coffeescript/test/classes.coffee

513 lines
9.3 KiB
CoffeeScript
Raw Normal View History

2010-12-29 00:48:54 -05:00
# Classes
# -------
2010-12-29 14:06:57 -05:00
# * Class Definition
# * Class Instantiation
# * Inheritance and Super
2011-03-11 21:55:26 -05:00
test "classes with a four-level inheritance chain", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Base
func: (string) ->
"zero/#{string}"
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
@static: (string) ->
"static/#{string}"
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class FirstChild extends Base
func: (string) ->
super('one/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
SecondChild = class extends FirstChild
func: (string) ->
super('two/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
thirdCtor = ->
@array = [1, 2, 3]
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class ThirdChild extends SecondChild
constructor: -> thirdCtor.call this
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
# Gratuitous comment for testing.
func: (string) ->
super('three/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
result = (new ThirdChild).func 'four'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok result is 'zero/one/two/three/four'
ok Base.static('word') is 'static/word'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
FirstChild::func = (string) ->
super('one/').length + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
result = (new ThirdChild).func 'four'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok result is '9two/three/four'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok (new ThirdChild).array.join(' ') is '1 2 3'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "constructors with inheritance and super", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
identity = (f) -> f
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class TopClass
constructor: (arg) ->
@prop = 'top-' + arg
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class SuperClass extends TopClass
constructor: (arg) ->
identity super 'super-' + arg
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class SubClass extends SuperClass
constructor: ->
identity super 'sub'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok (new SubClass).prop is 'top-super-sub'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "Overriding the static property new doesn't clobber Function::new", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class OneClass
@new: 'new'
function: 'function'
constructor: (name) -> @name = name
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class TwoClass extends OneClass
delete TwoClass.new
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
Function.prototype.new = -> new this arguments...
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok (TwoClass.new('three')).name is 'three'
ok (new OneClass).function is 'function'
ok OneClass.new is 'new'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
delete Function.prototype.new
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "basic classes, again, but in the manual prototype style", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
Base = ->
Base::func = (string) ->
'zero/' + string
Base::['func-func'] = (string) ->
"dynamic-#{string}"
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
FirstChild = ->
SecondChild = ->
ThirdChild = ->
@array = [1, 2, 3]
this
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ThirdChild extends SecondChild extends FirstChild extends Base
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
FirstChild::func = (string) ->
super('one/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
SecondChild::func = (string) ->
super('two/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ThirdChild::func = (string) ->
super('three/') + string
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
test "super with plain ol' functions as the original constructors", ->
2010-12-29 00:48:54 -05:00
TopClass = (arg) ->
@prop = 'top-' + arg
this
SuperClass = (arg) ->
super 'super-' + arg
this
SubClass = ->
super 'sub'
this
SuperClass extends TopClass
SubClass extends SuperClass
ok (new SubClass).prop is 'top-super-sub'
2011-03-11 21:55:26 -05:00
test "'@' referring to the current instance, and not being coerced into a call", ->
class ClassName
amI: ->
@ instanceof ClassName
obj = new ClassName
ok obj.amI()
test "super() calls in constructors of classes that are defined as object properties", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Hive
constructor: (name) -> @name = name
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Hive.Bee extends Hive
constructor: (name) -> super
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
maya = new Hive.Bee 'Maya'
ok maya.name is 'Maya'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "classes with JS-keyword properties", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Class
class: 'class'
name: -> @class
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
instance = new Class
ok instance.class is 'class'
ok instance.name() is 'class'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "Classes with methods that are pre-bound to the instance, or statically, to the class", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Dog
constructor: (name) ->
@name = name
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
bark: =>
"#{@name} woofs!"
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
@static = =>
new this('Dog')
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
spark = new Dog('Spark')
fido = new Dog('Fido')
fido.bark = spark.bark
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok fido.bark() is 'Spark woofs!'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
obj = func: Dog.static
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok obj.func().name is 'Dog'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "a bound function in a bound function", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Mini
num: 10
generate: =>
for i in [1..3]
=>
@num
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
m = new Mini
eq (func() for func in m.generate()).join(' '), '10 10 10'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "contructor called with varargs", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Connection
constructor: (one, two, three) ->
[@one, @two, @three] = [one, two, three]
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
out: ->
"#{@one}-#{@two}-#{@three}"
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
list = [3, 2, 1]
conn = new Connection list...
ok conn instanceof Connection
ok conn.out() is '3-2-1'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "calling super and passing along all arguments", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Parent
method: (args...) -> @args = args
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Child extends Parent
method: -> super
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
c = new Child
c.method 1, 2, 3, 4
ok c.args.join(' ') is '1 2 3 4'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "classes wrapped in decorators", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
func = (klass) ->
klass::prop = 'value'
klass
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
func class Test
prop2: 'value2'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok (new Test).prop is 'value'
ok (new Test).prop2 is 'value2'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "anonymous classes", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
obj =
klass: class
method: -> 'value'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
instance = new obj.klass
ok instance.method() is 'value'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "Implicit objects as static properties", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Static
@static =
one: 1
two: 2
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok Static.static.one is 1
ok Static.static.two is 2
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "nothing classes", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
c = class
ok c instanceof Function
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "classes with value'd constructors", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
counter = 0
classMaker = ->
inner = ++counter
2011-03-11 21:55:26 -05:00
->
@value = inner
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class One
constructor: classMaker()
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Two
constructor: classMaker()
2010-12-29 00:48:54 -05:00
eq (new One).value, 1
eq (new Two).value, 2
eq (new One).value, 1
eq (new Two).value, 2
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "exectuable class bodies", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class A
if true
b: 'b'
else
c: 'c'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
a = new A
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
eq a.b, 'b'
eq a.c, undefined
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "mild metaprogramming", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Base
@attr: (name) ->
@::[name] = (val) ->
if arguments.length > 0
@["_#{name}"] = val
else
@["_#{name}"]
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class Robot extends Base
@attr 'power'
@attr 'speed'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
robby = new Robot
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
ok robby.power() is undefined
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
robby.power 11
robby.speed Infinity
eq robby.power(), 11
eq robby.speed(), Infinity
test "namespaced classes do not reserve their function name in outside scope", ->
one = {}
two = {}
class one.Klass
@label = "one"
class two.Klass
@label = "two"
eq typeof Klass, 'undefined'
eq one.Klass.label, 'one'
eq two.Klass.label, 'two'
test "nested classes", ->
class Outer
constructor: ->
@label = 'outer'
class @Inner
constructor: ->
@label = 'inner'
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
test "variables in constructor bodies are correctly scoped", ->
class A
x = 1
2010-12-29 00:48:54 -05:00
constructor: ->
2011-03-11 21:55:26 -05:00
x = 10
y = 20
y = 2
captured: ->
{x, y}
a = new A
eq a.captured().x, 10
eq a.captured().y, 2
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "Issue #924: Static methods in nested classes", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class A
@B: class
@c = -> 5
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
eq A.B.c(), 5
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "`class extends this`", ->
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
class A
func: -> 'A'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
B = null
makeClass = ->
B = class extends this
func: -> super + ' B'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
makeClass.call A
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
eq (new B()).func(), 'A B'
2010-12-29 00:48:54 -05:00
2011-03-11 21:55:26 -05:00
test "ensure that constructors invoked with splats return a new object", ->
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
args = [1, 2, 3]
Type = (@args) ->
type = new Type args
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
ok type and type instanceof Type
ok type.args and type.args instanceof Array
ok v is args[i] for v, i in type.args
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
Type1 = (@a, @b, @c) ->
type1 = new Type1 args...
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
ok type1 instanceof Type1
eq type1.constructor, Type1
ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
# Ensure that constructors invoked with splats cache the function.
called = 0
get = -> if called++ then false else class Type
new get() args...
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
test "`new` shouldn't add extra parens", ->
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
ok new Date().constructor is Date
2010-12-30 22:48:31 -05:00
2011-03-11 21:55:26 -05:00
test "`new` works against bare function", ->
eq Date, new ->
eq this, new => this
Date
test "#1182: a subclass should be able to set its constructor to an external function", ->
ctor = ->
@val = 1
class A
class B extends A
constructor: ctor
eq (new B).val, 1
test "#1182: external constructors continued", ->
ctor = ->
class A
class B extends A
method: ->
constructor: ctor
ok B::method
test "#1313: misplaced __extends", ->
nonce = {}
class A
class B extends A
prop: nonce
constructor: ->
eq nonce, B::prop
test "#1182: execution order needs to be considered as well", ->
counter = 0
makeFn = (n) -> eq n, ++counter; ->
class B extends (makeFn 1)
@B: makeFn 2
constructor: makeFn 3
test "#1182: external constructors with bound functions", ->
fn = ->
2011-05-10 10:03:22 -04:00
{one: 1}
class B
class A
constructor: fn
method: => this instanceof A
ok (new A).method.call(new B)
test "#1372: bound class methods with reserved names", ->
2011-05-25 03:22:06 -04:00
class C
delete: =>
ok C::delete
test "#1380: `super` with reserved names", ->
class C
do: -> super
ok C::do
class B
0: -> super
ok B::[0]
test "#1464: bound class methods should keep context", ->
nonce = {}
nonce2 = {}
class C
constructor: (@id) ->
@boundStaticColon: => new this(nonce)
@boundStaticEqual= => new this(nonce2)
eq nonce, C.boundStaticColon().id
eq nonce2, C.boundStaticEqual().id
test "#1009: classes with reserved words as determined names", ->
eq 'function', typeof (-> class @for).call {}
test "#1482: classes can extend expressions", ->
id = (x) -> x
nonce = {}
class A then nonce: nonce
class B extends id A
eq nonce, (new B).nonce