1# Classes
2# -------
3
4# * Class Definition
5# * Class Instantiation
6# * Inheritance and Super
7# * ES2015+ Class Interoperability
8
9test "classes with a four-level inheritance chain", ->
10
11  class Base
12    func: (string) ->
13      "zero/#{string}"
14
15    @static: (string) ->
16      "static/#{string}"
17
18  class FirstChild extends Base
19    func: (string) ->
20      super('one/') + string
21
22  SecondChild = class extends FirstChild
23    func: (string) ->
24      super('two/') + string
25
26  thirdCtor = ->
27    @array = [1, 2, 3]
28
29  class ThirdChild extends SecondChild
30    constructor: ->
31      super()
32      thirdCtor.call this
33
34    # Gratuitous comment for testing.
35    func: (string) ->
36      super('three/') + string
37
38  result = (new ThirdChild).func 'four'
39
40  ok result is 'zero/one/two/three/four'
41  ok Base.static('word') is 'static/word'
42
43  ok (new ThirdChild).array.join(' ') is '1 2 3'
44
45
46test "constructors with inheritance and super", ->
47
48  identity = (f) -> f
49
50  class TopClass
51    constructor: (arg) ->
52      @prop = 'top-' + arg
53
54  class SuperClass extends TopClass
55    constructor: (arg) ->
56      identity super 'super-' + arg
57
58  class SubClass extends SuperClass
59    constructor: ->
60      identity super 'sub'
61
62  ok (new SubClass).prop is 'top-super-sub'
63
64
65test "'super' with accessors", ->
66  class Base
67    m: -> 4
68    n: -> 5
69    o: -> 6
70
71  name = 'o'
72  class A extends Base
73    m: -> super()
74    n: -> super.n()
75    "#{name}": -> super()
76    p: -> super[name]()
77
78  a = new A
79  eq 4, a.m()
80  eq 5, a.n()
81  eq 6, a.o()
82  eq 6, a.p()
83
84
85test "soaked 'super' invocation", ->
86  class Base
87    method: -> 2
88
89  class A extends Base
90    method: -> super?()
91    noMethod: -> super?()
92
93  a = new A
94  eq 2, a.method()
95  eq undefined, a.noMethod()
96
97  name = 'noMethod'
98  class B extends Base
99    "#{'method'}": -> super?()
100    "#{'noMethod'}": -> super?() ? super['method']()
101
102  b = new B
103  eq 2, b.method()
104  eq 2, b.noMethod()
105
106test "'@' referring to the current instance, and not being coerced into a call", ->
107
108  class ClassName
109    amI: ->
110      @ instanceof ClassName
111
112  obj = new ClassName
113  ok obj.amI()
114
115
116test "super() calls in constructors of classes that are defined as object properties", ->
117
118  class Hive
119    constructor: (name) -> @name = name
120
121  class Hive.Bee extends Hive
122    constructor: (name) -> super name
123
124  maya = new Hive.Bee 'Maya'
125  ok maya.name is 'Maya'
126
127
128test "classes with JS-keyword properties", ->
129
130  class Class
131    class: 'class'
132    name: -> @class
133
134  instance = new Class
135  ok instance.class is 'class'
136  ok instance.name() is 'class'
137
138
139test "Classes with methods that are pre-bound to the instance, or statically, to the class", ->
140
141  class Dog
142    constructor: (name) ->
143      @name = name
144
145    bark: =>
146      "#{@name} woofs!"
147
148    @static = =>
149      new this('Dog')
150
151  spark = new Dog('Spark')
152  fido  = new Dog('Fido')
153  fido.bark = spark.bark
154
155  ok fido.bark() is 'Spark woofs!'
156
157  obj = func: Dog.static
158
159  ok obj.func().name is 'Dog'
160
161
162test "a bound function in a bound function", ->
163
164  class Mini
165    num: 10
166    generate: =>
167      for i in [1..3]
168        =>
169          @num
170
171  m = new Mini
172  eq (func() for func in m.generate()).join(' '), '10 10 10'
173
174
175test "contructor called with varargs", ->
176
177  class Connection
178    constructor: (one, two, three) ->
179      [@one, @two, @three] = [one, two, three]
180
181    out: ->
182      "#{@one}-#{@two}-#{@three}"
183
184  list = [3, 2, 1]
185  conn = new Connection list...
186  ok conn instanceof Connection
187  ok conn.out() is '3-2-1'
188
189
190test "calling super and passing along all arguments", ->
191
192  class Parent
193    method: (args...) -> @args = args
194
195  class Child extends Parent
196    method: -> super arguments...
197
198  c = new Child
199  c.method 1, 2, 3, 4
200  ok c.args.join(' ') is '1 2 3 4'
201
202
203test "classes wrapped in decorators", ->
204
205  func = (klass) ->
206    klass::prop = 'value'
207    klass
208
209  func class Test
210    prop2: 'value2'
211
212  ok (new Test).prop  is 'value'
213  ok (new Test).prop2 is 'value2'
214
215
216test "anonymous classes", ->
217
218  obj =
219    klass: class
220      method: -> 'value'
221
222  instance = new obj.klass
223  ok instance.method() is 'value'
224
225
226test "Implicit objects as static properties", ->
227
228  class Static
229    @static =
230      one: 1
231      two: 2
232
233  ok Static.static.one is 1
234  ok Static.static.two is 2
235
236
237test "nothing classes", ->
238
239  c = class
240  ok c instanceof Function
241
242
243test "classes with static-level implicit objects", ->
244
245  class A
246    @static = one: 1
247    two: 2
248
249  class B
250    @static = one: 1,
251    two: 2
252
253  eq A.static.one, 1
254  eq A.static.two, undefined
255  eq (new A).two, 2
256
257  eq B.static.one, 1
258  eq B.static.two, 2
259  eq (new B).two, undefined
260
261
262test "classes with value'd constructors", ->
263
264  counter = 0
265  classMaker = ->
266    inner = ++counter
267    ->
268      @value = inner
269      @
270
271  class One
272    constructor: classMaker()
273
274  class Two
275    constructor: classMaker()
276
277  eq (new One).value, 1
278  eq (new Two).value, 2
279  eq (new One).value, 1
280  eq (new Two).value, 2
281
282
283test "executable class bodies", ->
284
285  class A
286    if true
287      b: 'b'
288    else
289      c: 'c'
290
291  a = new A
292
293  eq a.b, 'b'
294  eq a.c, undefined
295
296
297test "#2502: parenthesizing inner object values", ->
298
299  class A
300    category:  (type: 'string')
301    sections:  (type: 'number', default: 0)
302
303  eq (new A).category.type, 'string'
304
305  eq (new A).sections.default, 0
306
307
308test "conditional prototype property assignment", ->
309  debug = false
310
311  class Person
312    if debug
313      age: -> 10
314    else
315      age: -> 20
316
317  eq (new Person).age(), 20
318
319
320test "mild metaprogramming", ->
321
322  class Base
323    @attr: (name) ->
324      @::[name] = (val) ->
325        if arguments.length > 0
326          @["_#{name}"] = val
327        else
328          @["_#{name}"]
329
330  class Robot extends Base
331    @attr 'power'
332    @attr 'speed'
333
334  robby = new Robot
335
336  ok robby.power() is undefined
337
338  robby.power 11
339  robby.speed Infinity
340
341  eq robby.power(), 11
342  eq robby.speed(), Infinity
343
344
345test "namespaced classes do not reserve their function name in outside scope", ->
346
347  one = {}
348  two = {}
349
350  class one.Klass
351    @label = "one"
352
353  class two.Klass
354    @label = "two"
355
356  eq typeof Klass, 'undefined'
357  eq one.Klass.label, 'one'
358  eq two.Klass.label, 'two'
359
360
361test "nested classes", ->
362
363  class Outer
364    constructor: ->
365      @label = 'outer'
366
367    class @Inner
368      constructor: ->
369        @label = 'inner'
370
371  eq (new Outer).label, 'outer'
372  eq (new Outer.Inner).label, 'inner'
373
374
375test "variables in constructor bodies are correctly scoped", ->
376
377  class A
378    x = 1
379    constructor: ->
380      x = 10
381      y = 20
382    y = 2
383    captured: ->
384      {x, y}
385
386  a = new A
387  eq a.captured().x, 10
388  eq a.captured().y, 2
389
390
391test "Issue #924: Static methods in nested classes", ->
392
393  class A
394    @B: class
395      @c = -> 5
396
397  eq A.B.c(), 5
398
399
400test "`class extends this`", ->
401
402  class A
403    func: -> 'A'
404
405  B = null
406  makeClass = ->
407    B = class extends this
408      func: -> super() + ' B'
409
410  makeClass.call A
411
412  eq (new B()).func(), 'A B'
413
414
415test "ensure that constructors invoked with splats return a new object", ->
416
417  args = [1, 2, 3]
418  Type = (@args) ->
419  type = new Type args
420
421  ok type and type instanceof Type
422  ok type.args and type.args instanceof Array
423  ok v is args[i] for v, i in type.args
424
425  Type1 = (@a, @b, @c) ->
426  type1 = new Type1 args...
427
428  ok type1 instanceof   Type1
429  eq type1.constructor, Type1
430  ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
431
432  # Ensure that constructors invoked with splats cache the function.
433  called = 0
434  get = -> if called++ then false else class Type
435  new (get()) args...
436
437test "`new` shouldn't add extra parens", ->
438
439  ok new Date().constructor is Date
440
441
442test "`new` works against bare function", ->
443
444  eq Date, new ->
445    Date
446
447test "`new` works against statement", ->
448
449  class A
450  (new try A) instanceof A
451
452test "#1182: a subclass should be able to set its constructor to an external function", ->
453  ctor = ->
454    @val = 1
455    return
456  class A
457  class B extends A
458    constructor: ctor
459  eq (new B).val, 1
460
461test "#1182: external constructors continued", ->
462  ctor = ->
463  class A
464  class B extends A
465    method: ->
466    constructor: ctor
467  ok B::method
468
469test "#1313: misplaced __extends", ->
470  nonce = {}
471  class A
472  class B extends A
473    prop: nonce
474    constructor: -> super()
475  eq nonce, B::prop
476
477test "#1182: execution order needs to be considered as well", ->
478  counter = 0
479  makeFn = (n) -> eq n, ++counter; ->
480  class B extends (makeFn 1)
481    @B: makeFn 2
482    constructor: makeFn 3
483
484test "#1182: external constructors with bound functions", ->
485  fn = ->
486    {one: 1}
487    this
488  class B
489  class A
490    constructor: fn
491    method: => this instanceof A
492  ok (new A).method.call(new B)
493
494test "#1372: bound class methods with reserved names", ->
495  class C
496    delete: =>
497  ok C::delete
498
499test "#1380: `super` with reserved names", ->
500  class C
501    do: -> super()
502  ok C::do
503
504  class B
505    0: -> super()
506  ok B::[0]
507
508test "#1464: bound class methods should keep context", ->
509  nonce  = {}
510  nonce2 = {}
511  class C
512    constructor: (@id) ->
513    @boundStaticColon: => new this(nonce)
514    @boundStaticEqual= => new this(nonce2)
515  eq nonce,  C.boundStaticColon().id
516  eq nonce2, C.boundStaticEqual().id
517
518test "#1009: classes with reserved words as determined names", -> (->
519  eq 'function', typeof (class @for)
520  ok not /\beval\b/.test (class @eval).toString()
521  ok not /\barguments\b/.test (class @arguments).toString()
522).call {}
523
524test "#1482: classes can extend expressions", ->
525  id = (x) -> x
526  nonce = {}
527  class A then nonce: nonce
528  class B extends id A
529  eq nonce, (new B).nonce
530
531test "#1598: super works for static methods too", ->
532
533  class Parent
534    method: ->
535      'NO'
536    @method: ->
537      'yes'
538
539  class Child extends Parent
540    @method: ->
541      'pass? ' + super()
542
543  eq Child.method(), 'pass? yes'
544
545test "#1842: Regression with bound functions within bound class methods", ->
546
547  class Store
548    @bound: =>
549      do =>
550        eq this, Store
551
552  Store.bound()
553
554  # And a fancier case:
555
556  class Store
557
558    eq this, Store
559
560    @bound: =>
561      do =>
562        eq this, Store
563
564    @unbound: ->
565      eq this, Store
566
567    instance: =>
568      ok this instanceof Store
569
570  Store.bound()
571  Store.unbound()
572  (new Store).instance()
573
574test "#1876: Class @A extends A", ->
575  class A
576  class @A extends A
577
578  ok (new @A) instanceof A
579
580test "#1813: Passing class definitions as expressions", ->
581  ident = (x) -> x
582
583  result = ident class A then x = 1
584
585  eq result, A
586
587  result = ident class B extends A
588    x = 1
589
590  eq result, B
591
592test "#1966: external constructors should produce their return value", ->
593  ctor = -> {}
594  class A then constructor: ctor
595  ok (new A) not instanceof A
596
597test "#1980: regression with an inherited class with static function members", ->
598
599  class A
600
601  class B extends A
602    @static: => 'value'
603
604  eq B.static(), 'value'
605
606test "#1534: class then 'use strict'", ->
607  # [14.1 Directive Prologues and the Use Strict Directive](http://es5.github.com/#x14.1)
608  nonce = {}
609  error = 'do -> ok this'
610  strictTest = "do ->'use strict';#{error}"
611  return unless (try CoffeeScript.run strictTest, bare: yes catch e then nonce) is nonce
612
613  throws -> CoffeeScript.run "class then 'use strict';#{error}", bare: yes
614  doesNotThrow -> CoffeeScript.run "class then #{error}", bare: yes
615  doesNotThrow -> CoffeeScript.run "class then #{error};'use strict'", bare: yes
616
617  # comments are ignored in the Directive Prologue
618  comments = ["""
619  class
620    ### comment ###
621    'use strict'
622    #{error}""",
623  """
624  class
625    ### comment 1 ###
626    ### comment 2 ###
627    'use strict'
628    #{error}""",
629  """
630  class
631    ### comment 1 ###
632    ### comment 2 ###
633    'use strict'
634    #{error}
635    ### comment 3 ###"""
636  ]
637  throws (-> CoffeeScript.run comment, bare: yes) for comment in comments
638
639  # [ES5 §14.1](http://es5.github.com/#x14.1) allows for other directives
640  directives = ["""
641  class
642    'directive 1'
643    'use strict'
644    #{error}""",
645  """
646  class
647    'use strict'
648    'directive 2'
649    #{error}""",
650  """
651  class
652    ### comment 1 ###
653    'directive 1'
654    'use strict'
655    #{error}""",
656  """
657  class
658    ### comment 1 ###
659    'directive 1'
660    ### comment 2 ###
661    'use strict'
662    #{error}"""
663  ]
664  throws (-> CoffeeScript.run directive, bare: yes) for directive in directives
665
666test "#2052: classes should work in strict mode", ->
667  try
668    do ->
669      'use strict'
670      class A
671  catch e
672    ok no
673
674test "directives in class with extends ", ->
675  strictTest = """
676    class extends Object
677      ### comment ###
678      'use strict'
679      do -> eq this, undefined
680  """
681  CoffeeScript.run strictTest, bare: yes
682
683test "#2630: class bodies can't reference arguments", ->
684  throwsCompileError 'class Test then arguments'
685
686  # #4320: Don't be too eager when checking, though.
687  class Test
688    arguments: 5
689  eq 5, Test::arguments
690
691test "#2319: fn class n extends o.p [INDENT] x = 123", ->
692  first = ->
693
694  base = onebase: ->
695
696  first class OneKeeper extends base.onebase
697    one = 1
698    one: -> one
699
700  eq new OneKeeper().one(), 1
701
702
703test "#2599: other typed constructors should be inherited", ->
704  class Base
705    constructor: -> return {}
706
707  class Derived extends Base
708
709  ok (new Derived) not instanceof Derived
710  ok (new Derived) not instanceof Base
711  ok (new Base) not instanceof Base
712
713test "extending native objects works with and without defining a constructor", ->
714  class MyArray extends Array
715    method: -> 'yes!'
716
717  myArray = new MyArray
718  ok myArray instanceof MyArray
719  ok 'yes!', myArray.method()
720
721  class OverrideArray extends Array
722    constructor: -> super()
723    method: -> 'yes!'
724
725  overrideArray = new OverrideArray
726  ok overrideArray instanceof OverrideArray
727  eq 'yes!', overrideArray.method()
728
729
730test "#2782: non-alphanumeric-named bound functions", ->
731  class A
732    'b:c': =>
733      'd'
734
735  eq (new A)['b:c'](), 'd'
736
737
738test "#2781: overriding bound functions", ->
739  class A
740    a: ->
741        @b()
742    b: =>
743        1
744
745  class B extends A
746    b: =>
747        2
748
749  b = (new A).b
750  eq b(), 1
751
752  b = (new B).b
753  eq b(), 2
754
755
756test "#2791: bound function with destructured argument", ->
757  class Foo
758    method: ({a}) => 'Bar'
759
760  eq (new Foo).method({a: 'Bar'}), 'Bar'
761
762
763test "#2796: ditto, ditto, ditto", ->
764  answer = null
765
766  outsideMethod = (func) ->
767    func.call message: 'wrong!'
768
769  class Base
770    constructor: ->
771      @message = 'right!'
772      outsideMethod @echo
773
774    echo: =>
775      answer = @message
776
777  new Base
778  eq answer, 'right!'
779
780test "#3063: Class bodies cannot contain pure statements", ->
781  throwsCompileError """
782    class extends S
783      return if S.f
784      @f: => this
785  """
786
787test "#2949: super in static method with reserved name", ->
788  class Foo
789    @static: -> 'baz'
790
791  class Bar extends Foo
792    @static: -> super()
793
794  eq Bar.static(), 'baz'
795
796test "#3232: super in static methods (not object-assigned)", ->
797  class Foo
798    @baz = -> true
799    @qux = -> true
800
801  class Bar extends Foo
802    @baz = -> super()
803    Bar.qux = -> super()
804
805  ok Bar.baz()
806  ok Bar.qux()
807
808test "#1392 calling `super` in methods defined on namespaced classes", ->
809  class Base
810    m: -> 5
811    n: -> 4
812  namespace =
813    A: ->
814    B: ->
815  class namespace.A extends Base
816    m: -> super()
817
818  eq 5, (new namespace.A).m()
819  namespace.B::m = namespace.A::m
820  namespace.A::m = null
821  eq 5, (new namespace.B).m()
822
823  class C
824    @a: class extends Base
825      m: -> super()
826  eq 5, (new C.a).m()
827
828
829test "#4436 immediately instantiated named class", ->
830  ok new class Foo
831
832
833test "dynamic method names", ->
834  class A
835    "#{name = 'm'}": -> 1
836  eq 1, new A().m()
837
838  class B extends A
839    "#{name = 'm'}": -> super()
840  eq 1, new B().m()
841
842  getName = -> 'm'
843  class C
844    "#{name = getName()}": -> 1
845  eq 1, new C().m()
846
847
848test "dynamic method names and super", ->
849  class Base
850    @m: -> 6
851    m: -> 5
852    m2: -> 4.5
853    n: -> 4
854
855  name = -> count++; 'n'
856  count = 0
857
858  m = 'm'
859  class A extends Base
860    "#{m}": -> super()
861    "#{name()}": -> super()
862
863  m = 'n'
864  eq 5, (new A).m()
865
866  eq 4, (new A).n()
867  eq 1, count
868
869  m = 'm'
870  m2 = 'm2'
871  count = 0
872  class B extends Base
873    @[name()] = -> super()
874    "#{m}": -> super()
875    "#{m2}": -> super()
876  b = new B
877  m = m2 = 'n'
878  eq 6, B.m()
879  eq 5, b.m()
880  eq 4.5, b.m2()
881  eq 1, count
882
883  class C extends B
884    m: -> super()
885  eq 5, (new C).m()
886
887# ES2015+ class interoperability
888# Based on https://github.com/balupton/es6-javascript-class-interop
889# Helper functions to generate true ES classes to extend:
890getBasicClass = ->
891  ```
892  class BasicClass {
893    constructor (greeting) {
894      this.greeting = greeting || 'hi'
895    }
896  }
897  ```
898  BasicClass
899
900getExtendedClass = (BaseClass) ->
901  ```
902  class ExtendedClass extends BaseClass {
903    constructor (greeting, name) {
904      super(greeting || 'hello')
905      this.name = name
906    }
907  }
908  ```
909  ExtendedClass
910
911test "can instantiate a basic ES class", ->
912  BasicClass = getBasicClass()
913  i = new BasicClass 'howdy!'
914  eq i.greeting, 'howdy!'
915
916test "can instantiate an extended ES class", ->
917  BasicClass = getBasicClass()
918  ExtendedClass = getExtendedClass BasicClass
919  i = new ExtendedClass 'yo', 'buddy'
920  eq i.greeting, 'yo'
921  eq i.name, 'buddy'
922
923test "can extend a basic ES class", ->
924  BasicClass = getBasicClass()
925  class ExtendedClass extends BasicClass
926    constructor: (@name) ->
927      super()
928  i = new ExtendedClass 'dude'
929  eq i.name, 'dude'
930
931test "can extend an extended ES class", ->
932  BasicClass = getBasicClass()
933  ExtendedClass = getExtendedClass BasicClass
934
935  class ExtendedExtendedClass extends ExtendedClass
936    constructor: (@value) ->
937      super()
938    getDoubledValue: ->
939      @value * 2
940
941  i = new ExtendedExtendedClass 7
942  eq i.getDoubledValue(), 14
943
944test "CoffeeScript class can be extended in ES", ->
945  class CoffeeClass
946    constructor: (@favoriteDrink = 'latte', @size = 'grande') ->
947    getDrinkOrder: ->
948      "#{@size} #{@favoriteDrink}"
949
950  ```
951  class ECMAScriptClass extends CoffeeClass {
952    constructor (favoriteDrink) {
953      super(favoriteDrink);
954      this.favoriteDrink = this.favoriteDrink + ' with a dash of semicolons';
955    }
956  }
957  ```
958
959  e = new ECMAScriptClass 'coffee'
960  eq e.getDrinkOrder(), 'grande coffee with a dash of semicolons'
961
962test "extended CoffeeScript class can be extended in ES", ->
963  class CoffeeClass
964    constructor: (@favoriteDrink = 'latte') ->
965
966  class CoffeeClassWithDrinkOrder extends CoffeeClass
967    constructor: (@favoriteDrink, @size = 'grande') ->
968      super()
969    getDrinkOrder: ->
970      "#{@size} #{@favoriteDrink}"
971
972  ```
973  class ECMAScriptClass extends CoffeeClassWithDrinkOrder {
974    constructor (favoriteDrink) {
975      super(favoriteDrink);
976      this.favoriteDrink = this.favoriteDrink + ' with a dash of semicolons';
977    }
978  }
979  ```
980
981  e = new ECMAScriptClass 'coffee'
982  eq e.getDrinkOrder(), 'grande coffee with a dash of semicolons'
983
984test "`this` access after `super` in extended classes", ->
985  class Base
986
987  class Test extends Base
988    constructor: (param, @param) ->
989      eq param, nonce
990
991      result = { super: super(), @param, @method }
992      eq result.super, this
993      eq result.param, @param
994      eq result.method, @method
995      ok result.method isnt Test::method
996
997    method: =>
998
999  nonce = {}
1000  new Test nonce, {}
1001
1002test "`@`-params and bound methods with multiple `super` paths (blocks)", ->
1003  nonce = {}
1004
1005  class Base
1006    constructor: (@name) ->
1007
1008  class Test extends Base
1009    constructor: (param, @param) ->
1010      if param
1011        super 'param'
1012        eq @name, 'param'
1013      else
1014        super 'not param'
1015        eq @name, 'not param'
1016      eq @param, nonce
1017      ok @method isnt Test::method
1018    method: =>
1019  new Test true, nonce
1020  new Test false, nonce
1021
1022
1023test "`@`-params and bound methods with multiple `super` paths (expressions)", ->
1024  nonce = {}
1025
1026  class Base
1027    constructor: (@name) ->
1028
1029  class Test extends Base
1030    constructor: (param, @param) ->
1031      # Contrived example: force each path into an expression with inline assertions
1032      if param
1033        result = (
1034          eq (super 'param'), @;
1035          eq @name, 'param';
1036          eq @param, nonce;
1037          ok @method isnt Test::method
1038        )
1039      else
1040        result = (
1041          eq (super 'not param'), @;
1042          eq @name, 'not param';
1043          eq @param, nonce;
1044          ok @method isnt Test::method
1045        )
1046    method: =>
1047  new Test true, nonce
1048  new Test false, nonce
1049
1050test "constructor super in arrow functions", ->
1051  class Test extends (class)
1052    constructor: (@param) ->
1053      do => super()
1054      eq @param, nonce
1055
1056  new Test nonce = {}
1057
1058# TODO Some of these tests use CoffeeScript.compile and CoffeeScript.run when they could use
1059# regular test mechanics.
1060# TODO Some of these tests might be better placed in `test/error_messages.coffee`.
1061# TODO Some of these tests are duplicates.
1062
1063# Ensure that we always throw if we experience more than one super()
1064# call in a constructor.  This ends up being a runtime error.
1065# Should be caught at compile time.
1066test "multiple super calls", ->
1067  throwsA = """
1068  class A
1069    constructor: (@drink) ->
1070    make: -> "Making a #{@drink}"
1071
1072  class MultiSuper extends A
1073    constructor: (drink) ->
1074      super(drink)
1075      super(drink)
1076      @newDrink = drink
1077  new MultiSuper('Late').make()
1078  """
1079  throws -> CoffeeScript.run throwsA, bare: yes
1080
1081# Basic test to ensure we can pass @params in a constuctor and
1082# inheritance works correctly
1083test "@ params", ->
1084  class A
1085    constructor: (@drink, @shots, @flavor) ->
1086    make: -> "Making a #{@flavor} #{@drink} with #{@shots} shot(s)"
1087
1088  a = new A('Machiato', 2, 'chocolate')
1089  eq a.make(),  "Making a chocolate Machiato with 2 shot(s)"
1090
1091  class B extends A
1092  b = new B('Machiato', 2, 'chocolate')
1093  eq b.make(),  "Making a chocolate Machiato with 2 shot(s)"
1094
1095# Ensure we can accept @params with default parameters in a constructor
1096test "@ params with defaults in a constructor", ->
1097  class A
1098    # Multiple @ params with defaults
1099    constructor: (@drink = 'Americano', @shots = '1', @flavor = 'caramel') ->
1100    make: -> "Making a #{@flavor} #{@drink} with #{@shots} shot(s)"
1101
1102  a = new A()
1103  eq a.make(),  "Making a caramel Americano with 1 shot(s)"
1104
1105# Ensure we can handle default constructors with class params
1106test "@ params with class params", ->
1107  class Beverage
1108    drink: 'Americano'
1109    shots: '1'
1110    flavor: 'caramel'
1111
1112  class A
1113    # Class creation as a default param with `this`
1114    constructor: (@drink = new Beverage()) ->
1115  a = new A()
1116  eq a.drink.drink, 'Americano'
1117
1118  beverage = new Beverage
1119  class B
1120    # class costruction with a default external param
1121    constructor: (@drink = beverage) ->
1122
1123  b = new B()
1124  eq b.drink.drink, 'Americano'
1125
1126  class C
1127    # Default constructor with anonymous empty class
1128    constructor: (@meta = class) ->
1129  c = new C()
1130  ok c.meta instanceof Function
1131
1132test "@ params without super, including errors", ->
1133  classA = """
1134  class A
1135    constructor: (@drink) ->
1136    make: -> "Making a #{@drink}"
1137  a = new A('Machiato')
1138  """
1139
1140  throwsB = """
1141  class B extends A
1142    #implied super
1143    constructor: (@drink) ->
1144  b = new B('Machiato')
1145  """
1146  throwsCompileError classA + throwsB, bare: yes
1147
1148test "@ params super race condition", ->
1149  classA = """
1150  class A
1151    constructor: (@drink) ->
1152    make: -> "Making a #{@drink}"
1153  """
1154
1155  throwsB = """
1156  class B extends A
1157    constructor: (@params) ->
1158
1159  b = new B('Machiato')
1160  """
1161  throwsCompileError classA + throwsB, bare: yes
1162
1163  # Race condition with @ and super
1164  throwsC = """
1165  class C extends A
1166    constructor: (@params) ->
1167      super(@params)
1168
1169  c = new C('Machiato')
1170  """
1171  throwsCompileError classA + throwsC, bare: yes
1172
1173
1174test "@ with super call", ->
1175  class D
1176    make: -> "Making a #{@drink}"
1177
1178  class E extends D
1179    constructor: (@drink) ->
1180      super()
1181
1182  e = new E('Machiato')
1183  eq e.make(),  "Making a Machiato"
1184
1185test "@ with splats and super call", ->
1186  class A
1187    make: -> "Making a #{@drink}"
1188
1189  class B extends A
1190    constructor: (@drink...) ->
1191      super()
1192
1193  B = new B('Machiato')
1194  eq B.make(),  "Making a Machiato"
1195
1196
1197test "super and external constructors", ->
1198  # external constructor with @ param is allowed
1199  ctorA = (@drink) ->
1200  class A
1201    constructor: ctorA
1202    make: -> "Making a #{@drink}"
1203  a = new A('Machiato')
1204  eq a.make(),  "Making a Machiato"
1205
1206  # External constructor with super
1207  throwsC = """
1208  class B
1209    constructor: (@drink) ->
1210    make: -> "Making a #{@drink}"
1211
1212  ctorC = (drink) ->
1213    super(drink)
1214
1215  class C extends B
1216    constructor: ctorC
1217  c = new C('Machiato')
1218  """
1219  throwsCompileError throwsC, bare: yes
1220
1221
1222test "bound functions without super", ->
1223  # Bound function with @
1224  # Throw on compile, since bound
1225  # constructors are illegal
1226  throwsA = """
1227  class A
1228    constructor: (drink) =>
1229      @drink = drink
1230
1231  """
1232  throwsCompileError throwsA, bare: yes
1233
1234test "super in a bound function in a constructor", ->
1235  throwsB = """
1236  class A
1237  class B extends A
1238    constructor: do => super
1239  """
1240  throwsCompileError throwsB, bare: yes
1241
1242test "super in a bound function", ->
1243  class A
1244    constructor: (@drink) ->
1245    make: -> "Making a #{@drink}"
1246
1247  class B extends A
1248    make: (@flavor) =>
1249      super() + " with #{@flavor}"
1250
1251  b = new B('Machiato')
1252  eq b.make('vanilla'),  "Making a Machiato with vanilla"
1253
1254  # super in a bound function in a bound function
1255  class C extends A
1256    make: (@flavor) =>
1257      func = () =>
1258        super() + " with #{@flavor}"
1259      func()
1260
1261  c = new C('Machiato')
1262  eq c.make('vanilla'), "Making a Machiato with vanilla"
1263
1264  # bound function in a constructor
1265  class D extends A
1266    constructor: (drink) ->
1267      super(drink)
1268      x = =>
1269        eq @drink,  "Machiato"
1270      x()
1271  d = new D('Machiato')
1272  eq d.make(),  "Making a Machiato"
1273
1274# duplicate
1275test "super in a try/catch", ->
1276  classA = """
1277  class A
1278    constructor: (param) ->
1279      throw "" unless param
1280  """
1281
1282  throwsB = """
1283  class B extends A
1284      constructor: ->
1285        try
1286          super()
1287  """
1288
1289  throwsC = """
1290  ctor = ->
1291    try
1292      super()
1293
1294  class C extends A
1295      constructor: ctor
1296  """
1297  throws -> CoffeeScript.run classA + throwsB, bare: yes
1298  throws -> CoffeeScript.run classA + throwsC, bare: yes
1299
1300test "mixed ES6 and CS6 classes with a four-level inheritance chain", ->
1301  # Extended test
1302  # ES2015+ class interoperability
1303
1304  ```
1305  class Base {
1306    constructor (greeting) {
1307      this.greeting = greeting || 'hi';
1308    }
1309    func (string) {
1310      return 'zero/' + string;
1311    }
1312    static  staticFunc (string) {
1313      return 'static/' + string;
1314    }
1315  }
1316  ```
1317
1318  class FirstChild extends Base
1319    func: (string) ->
1320      super('one/') + string
1321
1322
1323  ```
1324  class SecondChild extends FirstChild {
1325    func (string) {
1326      return super.func('two/' + string);
1327    }
1328  }
1329  ```
1330
1331  thirdCtor = ->
1332    @array = [1, 2, 3]
1333
1334  class ThirdChild extends SecondChild
1335    constructor: ->
1336      super()
1337      thirdCtor.call this
1338    func: (string) ->
1339      super('three/') + string
1340
1341  result = (new ThirdChild).func 'four'
1342  ok result is 'zero/one/two/three/four'
1343  ok Base.staticFunc('word') is 'static/word'
1344
1345# exercise extends in a nested class
1346test "nested classes with super", ->
1347  class Outer
1348    constructor: ->
1349      @label = 'outer'
1350
1351    class @Inner
1352      constructor: ->
1353        @label = 'inner'
1354
1355    class @ExtendedInner extends @Inner
1356      constructor: ->
1357        tmp = super()
1358        @label = tmp.label + ' extended'
1359
1360    @extender: () =>
1361      class ExtendedSelf extends @
1362        constructor: ->
1363          tmp = super()
1364          @label = tmp.label + ' from this'
1365      new ExtendedSelf
1366
1367  eq (new Outer).label, 'outer'
1368  eq (new Outer.Inner).label, 'inner'
1369  eq (new Outer.ExtendedInner).label, 'inner extended'
1370  eq (Outer.extender()).label, 'outer from this'
1371
1372test "Static methods generate 'static' keywords", ->
1373  compile = """
1374  class CheckStatic
1375    constructor: (@drink) ->
1376    @className: -> 'CheckStatic'
1377
1378  c = new CheckStatic('Machiato')
1379  """
1380  result = CoffeeScript.compile compile, bare: yes
1381  ok result.match(' static ')
1382
1383test "Static methods in nested classes", ->
1384  class Outer
1385    @name: -> 'Outer'
1386
1387    class @Inner
1388      @name: -> 'Inner'
1389
1390  eq Outer.name(), 'Outer'
1391  eq Outer.Inner.name(), 'Inner'
1392
1393
1394test "mixed constructors with inheritance and ES6 super", ->
1395  identity = (f) -> f
1396
1397  class TopClass
1398    constructor: (arg) ->
1399      @prop = 'top-' + arg
1400
1401  ```
1402  class SuperClass extends TopClass {
1403    constructor (arg) {
1404      identity(super('super-' + arg));
1405    }
1406  }
1407  ```
1408  class SubClass extends SuperClass
1409    constructor: ->
1410      identity super 'sub'
1411
1412  ok (new SubClass).prop is 'top-super-sub'
1413
1414test "ES6 static class methods can be overriden", ->
1415  class A
1416    @name: -> 'A'
1417
1418  class B extends A
1419    @name: -> 'B'
1420
1421  eq A.name(), 'A'
1422  eq B.name(), 'B'
1423
1424# If creating static by direct assignment rather than ES6 static keyword
1425test "ES6 Static methods should set `this` to undefined // ES6 ", ->
1426  class A
1427    @test: ->
1428      eq this, undefined
1429
1430# Ensure that our object prototypes work with ES6
1431test "ES6 prototypes can be overriden", ->
1432  class A
1433    className: 'classA'
1434
1435  ```
1436  class B {
1437    test () {return "B";};
1438  }
1439  ```
1440  b = new B
1441  a = new A
1442  eq a.className, 'classA'
1443  eq b.test(), 'B'
1444  Object.setPrototypeOf(b, a)
1445  eq b.className, 'classA'
1446  # This shouldn't throw,
1447  # as we only change inheritance not object construction
1448  # This may be an issue with ES, rather than CS construction?
1449  #eq b.test(), 'B'
1450
1451  class D extends B
1452  B::test = () -> 'D'
1453  eq (new D).test(), 'D'
1454
1455# TODO: implement this error check
1456# test "ES6 conformance to extending non-classes", ->
1457#   A = (@title) ->
1458#     'Title: ' + @
1459
1460#   class B extends A
1461#   b = new B('caffeinated')
1462#   eq b.title, 'caffeinated'
1463
1464#   # Check inheritance chain
1465#   A::getTitle = () -> @title
1466#   eq b.getTitle(), 'caffeinated'
1467
1468#   throwsC = """
1469#   C = {title: 'invalid'}
1470#   class D extends {}
1471#   """
1472#   # This should catch on compile and message should be "class can only extend classes and functions."
1473#   throws -> CoffeeScript.run throwsC, bare: yes
1474
1475# TODO: Evaluate future compliance with "strict mode";
1476# test "Class function environment should be in `strict mode`, ie as if 'use strict' was in use", ->
1477#   class A
1478#     # this might be a meaningless test, since these are likely to be runtime errors and different
1479#     # for every browser.  Thoughts?
1480#     constructor: () ->
1481#       # Ivalid: prop reassignment
1482#       @state = {prop: [1], prop: {a: 'a'}}
1483#       # eval reassignment
1484#       @badEval = eval;
1485
1486#   # Should throw, but doesn't
1487#   a = new A
1488
1489test "only one method named constructor allowed", ->
1490  throwsA = """
1491  class A
1492    constructor: (@first) ->
1493    constructor: (@last) ->
1494  """
1495  throwsCompileError throwsA, bare: yes
1496
1497test "If the constructor of a child class does not call super,it should return an object.", ->
1498  nonce = {}
1499
1500  class A
1501  class B extends A
1502    constructor: ->
1503      return nonce
1504
1505  eq nonce, new B
1506
1507
1508test "super can only exist in extended classes", ->
1509  throwsA = """
1510  class A
1511    constructor: (@name) ->
1512      super()
1513  """
1514  throwsCompileError throwsA, bare: yes
1515
1516# --- CS1 classes compatability breaks ---
1517test "CS6 Class extends a CS1 compiled class", ->
1518  ```
1519  // Generated by CoffeeScript 1.11.1
1520  var BaseCS1, ExtendedCS1,
1521    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
1522    hasProp = {}.hasOwnProperty;
1523
1524  BaseCS1 = (function() {
1525    function BaseCS1(drink) {
1526      this.drink = drink;
1527    }
1528
1529    BaseCS1.prototype.make = function() {
1530      return "making a " + this.drink;
1531    };
1532
1533    BaseCS1.className = function() {
1534      return 'BaseCS1';
1535    };
1536
1537    return BaseCS1;
1538
1539  })();
1540
1541  ExtendedCS1 = (function(superClass) {
1542    extend(ExtendedCS1, superClass);
1543
1544    function ExtendedCS1(flavor) {
1545      this.flavor = flavor;
1546      ExtendedCS1.__super__.constructor.call(this, 'cafe ole');
1547    }
1548
1549    ExtendedCS1.prototype.make = function() {
1550      return "making a " + this.drink + " with " + this.flavor;
1551    };
1552
1553    ExtendedCS1.className = function() {
1554      return 'ExtendedCS1';
1555    };
1556
1557    return ExtendedCS1;
1558
1559  })(BaseCS1);
1560
1561  ```
1562  class B extends BaseCS1
1563  eq B.className(), 'BaseCS1'
1564  b = new B('machiato')
1565  eq b.make(), "making a machiato"
1566
1567
1568test "CS6 Class extends an extended CS1 compiled class", ->
1569  ```
1570  // Generated by CoffeeScript 1.11.1
1571  var BaseCS1, ExtendedCS1,
1572    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
1573    hasProp = {}.hasOwnProperty;
1574
1575  BaseCS1 = (function() {
1576    function BaseCS1(drink) {
1577      this.drink = drink;
1578    }
1579
1580    BaseCS1.prototype.make = function() {
1581      return "making a " + this.drink;
1582    };
1583
1584    BaseCS1.className = function() {
1585      return 'BaseCS1';
1586    };
1587
1588    return BaseCS1;
1589
1590  })();
1591
1592  ExtendedCS1 = (function(superClass) {
1593    extend(ExtendedCS1, superClass);
1594
1595    function ExtendedCS1(flavor) {
1596      this.flavor = flavor;
1597      ExtendedCS1.__super__.constructor.call(this, 'cafe ole');
1598    }
1599
1600    ExtendedCS1.prototype.make = function() {
1601      return "making a " + this.drink + " with " + this.flavor;
1602    };
1603
1604    ExtendedCS1.className = function() {
1605      return 'ExtendedCS1';
1606    };
1607
1608    return ExtendedCS1;
1609
1610  })(BaseCS1);
1611
1612  ```
1613  class B extends ExtendedCS1
1614  eq B.className(), 'ExtendedCS1'
1615  b = new B('vanilla')
1616  eq b.make(), "making a cafe ole with vanilla"
1617
1618test "CS6 Class extends a CS1 compiled class with super()", ->
1619  ```
1620  // Generated by CoffeeScript 1.11.1
1621  var BaseCS1, ExtendedCS1,
1622    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
1623    hasProp = {}.hasOwnProperty;
1624
1625  BaseCS1 = (function() {
1626    function BaseCS1(drink) {
1627      this.drink = drink;
1628    }
1629
1630    BaseCS1.prototype.make = function() {
1631      return "making a " + this.drink;
1632    };
1633
1634    BaseCS1.className = function() {
1635      return 'BaseCS1';
1636    };
1637
1638    return BaseCS1;
1639
1640  })();
1641
1642  ExtendedCS1 = (function(superClass) {
1643    extend(ExtendedCS1, superClass);
1644
1645    function ExtendedCS1(flavor) {
1646      this.flavor = flavor;
1647      ExtendedCS1.__super__.constructor.call(this, 'cafe ole');
1648    }
1649
1650    ExtendedCS1.prototype.make = function() {
1651      return "making a " + this.drink + " with " + this.flavor;
1652    };
1653
1654    ExtendedCS1.className = function() {
1655      return 'ExtendedCS1';
1656    };
1657
1658    return ExtendedCS1;
1659
1660  })(BaseCS1);
1661
1662  ```
1663  class B extends ExtendedCS1
1664    constructor: (@shots) ->
1665      super('caramel')
1666    make: () ->
1667      super() + " and #{@shots} shots of espresso"
1668
1669  eq B.className(), 'ExtendedCS1'
1670  b = new B('three')
1671  eq b.make(), "making a cafe ole with caramel and three shots of espresso"
1672
1673test 'Bound method called normally before binding is ok', ->
1674  class Base
1675    constructor: ->
1676      @setProp()
1677      eq @derivedBound(), 3
1678
1679  class Derived extends Base
1680    setProp: ->
1681      @prop = 3
1682
1683    derivedBound: =>
1684      @prop
1685
1686  d = new Derived
1687
1688test 'Bound method called as callback after super() is ok', ->
1689  class Base
1690
1691  class Derived extends Base
1692    constructor: (@prop = 3) ->
1693      super()
1694      f = @derivedBound
1695      eq f(), 3
1696
1697    derivedBound: =>
1698      @prop
1699
1700  d = new Derived
1701  {derivedBound} = d
1702  eq derivedBound(), 3
1703
1704test 'Bound method of base class called as callback is ok', ->
1705  class Base
1706    constructor: (@prop = 3) ->
1707      f = @baseBound
1708      eq f(), 3
1709
1710    baseBound: =>
1711      @prop
1712
1713  b = new Base
1714  {baseBound} = b
1715  eq baseBound(), 3
1716
1717test 'Bound method of prop-named class called as callback is ok', ->
1718  Hive = {}
1719  class Hive.Bee
1720    constructor: (@prop = 3) ->
1721      f = @baseBound
1722      eq f(), 3
1723
1724    baseBound: =>
1725      @prop
1726
1727  b = new Hive.Bee
1728  {baseBound} = b
1729  eq baseBound(), 3
1730
1731test 'Bound method of class with expression base class called as callback is ok', ->
1732  calledB = no
1733  B = ->
1734    throw new Error if calledB
1735    calledB = yes
1736    class
1737  class A extends B()
1738    constructor: (@prop = 3) ->
1739      super()
1740      f = @derivedBound
1741      eq f(), 3
1742
1743    derivedBound: =>
1744      @prop
1745
1746  b = new A
1747  {derivedBound} = b
1748  eq derivedBound(), 3
1749
1750test 'Bound method of class with expression class name called as callback is ok', ->
1751  calledF = no
1752  obj = {}
1753  B = class
1754  f = ->
1755    throw new Error if calledF
1756    calledF = yes
1757    obj
1758  class f().A extends B
1759    constructor: (@prop = 3) ->
1760      super()
1761      g = @derivedBound
1762      eq g(), 3
1763
1764    derivedBound: =>
1765      @prop
1766
1767  a = new obj.A
1768  {derivedBound} = a
1769  eq derivedBound(), 3
1770
1771test 'Bound method of anonymous child class called as callback is ok', ->
1772  f = ->
1773    B = class
1774    class extends B
1775      constructor: (@prop = 3) ->
1776        super()
1777        g = @derivedBound
1778        eq g(), 3
1779
1780      derivedBound: =>
1781        @prop
1782
1783  a = new (f())
1784  {derivedBound} = a
1785  eq derivedBound(), 3
1786
1787test 'Bound method of immediately instantiated class with expression base class called as callback is ok', ->
1788  calledF = no
1789  obj = {}
1790  B = class
1791  f = ->
1792    throw new Error if calledF
1793    calledF = yes
1794    obj
1795  a = new class f().A extends B
1796    constructor: (@prop = 3) ->
1797      super()
1798      g = @derivedBound
1799      eq g(), 3
1800
1801    derivedBound: =>
1802      @prop
1803
1804  {derivedBound} = a
1805  eq derivedBound(), 3
1806
1807test "#4591: super.x.y, super['x'].y", ->
1808  class A
1809    x:
1810      y: 1
1811      z: -> 2
1812
1813  class B extends A
1814    constructor: ->
1815      super()
1816
1817      @w = super.x.y
1818      @v = super['x'].y
1819      @u = super.x['y']
1820      @t = super.x.z()
1821      @s = super['x'].z()
1822      @r = super.x['z']()
1823
1824  b = new B
1825  eq 1, b.w
1826  eq 1, b.v
1827  eq 1, b.u
1828  eq 2, b.t
1829  eq 2, b.s
1830  eq 2, b.r
1831
1832test "#4464: backticked expressions in class body", ->
1833  class A
1834    `get x() { return 42; }`
1835
1836  class B
1837    `get x() { return 42; }`
1838    constructor: ->
1839      @y = 84
1840
1841  a = new A
1842  eq 42, a.x
1843  b = new B
1844  eq 42, b.x
1845  eq 84, b.y
1846
1847test "#4724: backticked expression in a class body with hoisted member", ->
1848  class A
1849    `get x() { return 42; }`
1850    hoisted: 84
1851
1852  a = new A
1853  eq 42, a.x
1854  eq 84, a.hoisted
1855
1856test "#4822: nested anonymous classes use non-conflicting variable names", ->
1857  Class = class
1858    @a: class
1859      @b: 1
1860
1861  eq Class.a.b, 1
1862
1863test "#4827: executable class body wrappers have correct context", ->
1864  test = ->
1865    class @A
1866    class @B extends @A
1867      @property = 1
1868
1869  o = {}
1870  test.call o
1871  ok typeof o.A is typeof o.B is 'function'
1872
1873test "#4868: Incorrect ‘Can’t call super with @params’ error", ->
1874  class A
1875    constructor: (@func = ->) ->
1876      @x = 1
1877      @func()
1878
1879  class B extends A
1880    constructor: ->
1881      super -> @x = 2
1882
1883  a = new A
1884  b = new B
1885  eq 1, a.x
1886  eq 2, b.x
1887
1888  class C
1889    constructor: (@c = class) -> @c
1890
1891  class D extends C
1892    constructor: ->
1893      super class then constructor: (@a) -> @a = 3
1894
1895  d = new (new D).c
1896  eq 3, d.a
1897
1898test "#4609: Support new.target", ->
1899  class A
1900    constructor: ->
1901      @calledAs = new.target.name
1902
1903  class B extends A
1904
1905  b = new B
1906  eq b.calledAs, 'B'
1907
1908  newTarget = null
1909  Foo = ->
1910    newTarget = !!new.target
1911
1912  Foo()
1913  eq newTarget, no
1914
1915  newTarget = null
1916
1917  new Foo()
1918  eq newTarget, yes
1919
1920test "#5323: new.target can be the argument of a function", ->
1921  fn = (arg) -> arg
1922  fn new.target
1923
1924test "#5085: Bug: @ reference to class not maintained in do block", ->
1925  thisFoo = 'initial foo'
1926  thisBar = 'initial bar'
1927  fn = (o) -> o.bar()
1928
1929  class A
1930    @foo = 'foo assigned in class'
1931    do => thisFoo = @foo
1932    fn bar: => thisBar = @foo
1933
1934  eq thisFoo, 'foo assigned in class'
1935  eq thisBar, 'foo assigned in class'
1936
1937test "#5204: Computed class property", ->
1938  foo = 'bar'
1939  class A
1940    [foo]: 'baz'
1941  a = new A()
1942  eq a.bar, 'baz'
1943  eq A::bar, 'baz'
1944
1945test "#5204: Static computed class property", ->
1946  foo = 'bar'
1947  qux = 'quux'
1948  class A
1949    @[foo]: 'baz'
1950    @[qux]: -> 3
1951  eq A.bar, 'baz'
1952  eq A.quux(), 3
1953