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