PageRenderTime 76ms CodeModel.GetById 13ms app.highlight 52ms RepoModel.GetById 1ms app.codeStats 1ms

/test/classes.coffee

http://github.com/jashkenas/coffee-script
CoffeeScript | 1948 lines | 1446 code | 407 blank | 95 comment | 19 complexity | 25665dc5f64f2605be840e485297332d MD5 | raw file
   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 "#5085: Bug: @ reference to class not maintained in do block", ->
1921  thisFoo = 'initial foo'
1922  thisBar = 'initial bar'
1923  fn = (o) -> o.bar()
1924
1925  class A
1926    @foo = 'foo assigned in class'
1927    do => thisFoo = @foo
1928    fn bar: => thisBar = @foo
1929
1930  eq thisFoo, 'foo assigned in class'
1931  eq thisBar, 'foo assigned in class'
1932
1933test "#5204: Computed class property", ->
1934  foo = 'bar'
1935  class A
1936    [foo]: 'baz'
1937  a = new A()
1938  eq a.bar, 'baz'
1939  eq A::bar, 'baz'
1940
1941test "#5204: Static computed class property", ->
1942  foo = 'bar'
1943  qux = 'quux'
1944  class A
1945    @[foo]: 'baz'
1946    @[qux]: -> 3
1947  eq A.bar, 'baz'
1948  eq A.quux(), 3