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

699 lines
13 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
2013-02-25 12:15:24 -05:00
TopClass = (arg) ->
@prop = 'top-' + arg
this
2010-12-29 00:48:54 -05:00
2013-02-25 12:15:24 -05:00
SuperClass = (arg) ->
super 'super-' + arg
this
2010-12-29 00:48:54 -05:00
2013-02-25 12:15:24 -05:00
SubClass = ->
super 'sub'
this
2010-12-29 00:48:54 -05:00
2013-02-25 12:15:24 -05:00
SuperClass extends TopClass
SubClass extends SuperClass
2010-12-29 00:48:54 -05:00
2013-02-25 12:15:24 -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 "'@' 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
2011-12-24 07:04:34 -05:00
2011-12-15 12:29:03 -05:00
test "classes with static-level implicit objects", ->
2011-12-24 07:04:34 -05:00
2011-12-15 12:29:03 -05:00
class A
@static = one: 1
two: 2
2011-12-24 07:04:34 -05:00
2011-12-15 12:29:03 -05:00
class B
@static = one: 1,
two: 2
2011-12-24 07:04:34 -05:00
2011-12-15 12:29:03 -05:00
eq A.static.one, 1
eq A.static.two, undefined
eq (new A).two, 2
2011-12-24 07:04:34 -05:00
2011-12-15 12:29:03 -05:00
eq B.static.one, 1
eq B.static.two, 2
eq (new B).two, undefined
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}
this
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)
ok not /\beval\b/.test (class @eval).toString()
ok not /\barguments\b/.test (class @arguments).toString()
).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
test "#1598: super works for static methods too", ->
class Parent
method: ->
'NO'
@method: ->
'yes'
class Child extends Parent
@method: ->
'pass? ' + super
eq Child.method(), 'pass? yes'
2011-12-24 07:04:34 -05:00
2011-12-14 11:02:10 -05:00
test "#1842: Regression with bound functions within bound class methods", ->
2011-12-24 07:04:34 -05:00
2011-12-14 11:02:10 -05:00
class Store
@bound: =>
do =>
eq this, Store
2011-12-24 07:04:34 -05:00
2011-12-14 11:02:10 -05:00
Store.bound()
2011-12-24 07:04:34 -05:00
# And a fancier case:
2011-12-24 07:04:34 -05:00
class Store
2011-12-24 07:04:34 -05:00
eq this, Store
2011-12-24 07:04:34 -05:00
@bound: =>
do =>
eq this, Store
2011-12-24 07:04:34 -05:00
@unbound: ->
eq this, Store
2011-12-24 07:04:34 -05:00
instance: =>
ok this instanceof Store
2011-12-24 07:04:34 -05:00
Store.bound()
Store.unbound()
2011-12-14 12:17:21 -05:00
(new Store).instance()
2011-12-24 07:04:34 -05:00
2011-12-14 12:17:21 -05:00
test "#1876: Class @A extends A", ->
class A
class @A extends A
2011-12-24 07:04:34 -05:00
2011-12-14 12:17:21 -05:00
ok (new @A) instanceof A
2011-12-24 07:04:34 -05:00
2011-12-15 16:03:01 -05:00
test "#1813: Passing class definitions as expressions", ->
ident = (x) -> x
2011-12-24 07:04:34 -05:00
2011-12-15 16:03:01 -05:00
result = ident class A then x = 1
2011-12-24 07:04:34 -05:00
2011-12-15 16:03:01 -05:00
eq result, A
2011-12-24 07:04:34 -05:00
2011-12-15 16:03:01 -05:00
result = ident class B extends A
x = 1
2011-12-24 07:04:34 -05:00
2011-12-15 16:03:01 -05:00
eq result, B
2011-12-24 07:04:34 -05:00
2011-12-27 19:54:14 -05:00
test "#1980: regression with an inherited class with static function members", ->
2011-12-27 19:54:14 -05:00
class A
class B extends A
@static: => 'value'
2012-01-12 18:34:50 -05:00
eq B.static(), 'value'
2012-01-12 18:34:50 -05:00
test "#1534: class then 'use strict'", ->
# [14.1 Directive Prologues and the Use Strict Directive](http://es5.github.com/#x14.1)
2012-01-12 18:34:50 -05:00
nonce = {}
error = 'do -> ok this'
strictTest = "do ->'use strict';#{error}"
return unless (try CoffeeScript.run strictTest, bare: yes catch e then nonce) is nonce
throws -> CoffeeScript.run "class then 'use strict';#{error}", bare: yes
doesNotThrow -> CoffeeScript.run "class then #{error}", bare: yes
doesNotThrow -> CoffeeScript.run "class then #{error};'use strict'", bare: yes
# comments are ignored in the Directive Prologue
comments = ["""
class
### comment ###
'use strict'
#{error}""",
"""
class
### comment 1 ###
### comment 2 ###
'use strict'
#{error}""",
"""
class
### comment 1 ###
### comment 2 ###
'use strict'
#{error}
### comment 3 ###"""
]
throws (-> CoffeeScript.run comment, bare: yes) for comment in comments
# [ES5 §14.1](http://es5.github.com/#x14.1) allows for other directives
directives = ["""
class
'directive 1'
'use strict'
#{error}""",
"""
class
'use strict'
'directive 2'
#{error}""",
"""
class
### comment 1 ###
'directive 1'
'use strict'
#{error}""",
"""
class
### comment 1 ###
'directive 1'
### comment 2 ###
'use strict'
#{error}"""
]
throws (-> CoffeeScript.run directive, bare: yes) for directive in directives
test "#2052: classes should work in strict mode", ->
try
do ->
'use strict'
class A
catch e
ok no
test "#2630: class bodies can't reference arguments", ->
throws ->
CoffeeScript.compile('class Test then arguments')
test "#2359: instanceof should work when extending native objects", ->
class MyError extends Error
ok new MyError instanceof MyError
test '#2359: external constructors returning "other typed" objets', ->
ctor = -> {}
class A then constructor: ctor
ok new A instanceof A
ok new ctor not instanceof A
ok new ctor not instanceof ctor
test "#2359: constructors should not return an explicit value", ->
throws -> CoffeeScript.run "class then constructor: -> return 5"
throws -> CoffeeScript.run """
class
constructor: ->
if foo
return bar: 7
baz()
"""