Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81153 views
1
/**
2
* Copyright 2013 Facebook, Inc.
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*
16
* @emails [email protected]
17
*/
18
19
/*jshint evil:true*/
20
21
jest.autoMockOff();
22
23
describe('es6-classes', function() {
24
var transformFn;
25
var classVisitors;
26
var arrowFunctionVisitors;
27
var visitors;
28
29
beforeEach(function() {
30
require('mock-modules').dumpCache();
31
32
transformFn = require('../../src/jstransform').transform;
33
34
classVisitors = require('../es6-class-visitors').visitorList;
35
arrowFunctionVisitors = require('../es6-arrow-function-visitors').visitorList;
36
37
visitors = classVisitors.concat(arrowFunctionVisitors);
38
});
39
40
function transform(code, opts) {
41
return transformFn(visitors, code, opts).code;
42
}
43
44
describe('ClassDeclarations', function() {
45
describe('preserves line numbers', function() {
46
it('does not add "use strict" unless necessary', function() {
47
var code = [
48
'function strictStuff() {',
49
' "use strict";',
50
' class A {',
51
' foo() {}',
52
' }',
53
'}',
54
'class B {',
55
' bar() {',
56
' class C {}',
57
' }',
58
'}'
59
].join('\n');
60
61
var expected = [
62
'function strictStuff() {',
63
' "use strict";',
64
' function A(){}',
65
' A.prototype.foo=function() {};',
66
' ',
67
'}',
68
'function B(){"use strict";}',
69
' B.prototype.bar=function() {"use strict";',
70
' function C(){}',
71
' };',
72
''
73
].join('\n');
74
75
expect(transform(code)).toBe(expected);
76
});
77
78
it('preserves lines with no inheritance', function() {
79
var code = [
80
'"use strict";',
81
'class Foo {',
82
' foo() {',
83
' ',
84
' ',
85
' }',
86
'',
87
' constructor(p1,',
88
' p2) {',
89
'',
90
' this.p1 = p1;',
91
' this.p2 = p2;',
92
' }',
93
'',
94
' bar(){}',
95
' static baz() {',
96
'}',
97
'}'
98
].join('\n');
99
100
var expected = [
101
'"use strict";',
102
'',
103
' Foo.prototype.foo=function() {',
104
' ',
105
' ',
106
' };',
107
'',
108
' function Foo(p1,',
109
' p2) {',
110
'',
111
' this.p1 = p1;',
112
' this.p2 = p2;',
113
' }',
114
'',
115
' Foo.prototype.bar=function(){};',
116
' Foo.baz=function() {',
117
'};',
118
''
119
].join('\n');
120
121
expect(transform(code)).toBe(expected);
122
});
123
124
it('preserves lines with inheritance from identifier', function() {
125
var code = [
126
'class Foo extends Bar {',
127
' foo() {',
128
' ',
129
' ',
130
' super(p1,',
131
' p2);',
132
' }',
133
'',
134
' constructor(p1,',
135
' p2) {',
136
'',
137
' this.p1 = p1;',
138
' this.p2 = p2;',
139
' super.blah(p1,',
140
' p2);',
141
' }',
142
'',
143
' bar(){}',
144
' static baz() {',
145
'}',
146
'}'
147
].join('\n');
148
149
var expected = [
150
'for(var Bar____Key in Bar){' +
151
'if(Bar.hasOwnProperty(Bar____Key)){' +
152
'Foo[Bar____Key]=Bar[Bar____Key];' +
153
'}' +
154
'}' +
155
'var ____SuperProtoOfBar=' +
156
'Bar===null' +
157
'?null:' +
158
'Bar.prototype;' +
159
'Foo.prototype=Object.create(____SuperProtoOfBar);' +
160
'Foo.prototype.constructor=Foo;' +
161
'Foo.__superConstructor__=Bar;',
162
163
' Foo.prototype.foo=function() {"use strict";',
164
' ',
165
' ',
166
' ____SuperProtoOfBar.foo.call(this,p1,',
167
' p2);',
168
' };',
169
'',
170
' function Foo(p1,',
171
' p2) {"use strict";',
172
'',
173
' this.p1 = p1;',
174
' this.p2 = p2;',
175
' ____SuperProtoOfBar.blah.call(this,p1,',
176
' p2);',
177
' }',
178
'',
179
' Foo.prototype.bar=function(){"use strict";};',
180
' Foo.baz=function() {"use strict";',
181
'};',
182
''
183
].join('\n');
184
185
expect(transform(code)).toBe(expected);
186
});
187
188
it('preserves lines with inheritance from expression', function() {
189
var code = [
190
'class Foo extends mixin(Bar, Baz) {',
191
' foo() {',
192
' ',
193
' ',
194
' }',
195
'',
196
' constructor(p1,',
197
' p2) {',
198
'',
199
' this.p1 = p1;',
200
' this.p2 = p2;',
201
' }',
202
'',
203
' bar(){}',
204
' static baz() {',
205
'}',
206
'}'
207
].join('\n');
208
209
var expected = [
210
'var ____Class0=mixin(Bar, Baz);' +
211
'for(var ____Class0____Key in ____Class0){' +
212
'if(____Class0.hasOwnProperty(____Class0____Key)){' +
213
'Foo[____Class0____Key]=____Class0[____Class0____Key];' +
214
'}' +
215
'}' +
216
'var ____SuperProtoOf____Class0=' +
217
'____Class0===null' +
218
'?null' +
219
':____Class0.prototype;' +
220
'Foo.prototype=Object.create(____SuperProtoOf____Class0);' +
221
'Foo.prototype.constructor=Foo;' +
222
'Foo.__superConstructor__=____Class0;',
223
224
' Foo.prototype.foo=function() {"use strict";',
225
' ',
226
' ',
227
' };',
228
'',
229
' function Foo(p1,',
230
' p2) {"use strict";',
231
'',
232
' this.p1 = p1;',
233
' this.p2 = p2;',
234
' }',
235
'',
236
' Foo.prototype.bar=function(){"use strict";};',
237
' Foo.baz=function() {"use strict";',
238
'};',
239
''
240
].join('\n');
241
242
expect(transform(code)).toBe(expected);
243
});
244
});
245
246
describe('functional tests', function() {
247
it('handles an empty body', function() {
248
var code = transform(
249
'class Foo {}'
250
);
251
252
eval(code);
253
254
var fooInst = new Foo();
255
expect(fooInst instanceof Foo).toBe(true);
256
});
257
258
it('handles constructors without params', function() {
259
var code = transform([
260
'class Foo {',
261
' constructor() {',
262
' this.test = "testValue";',
263
' }',
264
'}'
265
].join('\n'));
266
267
eval(code);
268
269
var fooInst = new Foo();
270
expect(fooInst.test).toBe('testValue');
271
});
272
273
it('handles constructors with params', function() {
274
var code = transform([
275
'class Foo {',
276
' constructor(p1, p2) {',
277
' this.p1 = p1;',
278
' this.p2 = p2;',
279
' }',
280
'}'
281
].join('\n'));
282
283
eval(code);
284
285
var fooInst = new Foo('a', 'b');
286
expect(fooInst.p1).toBe('a');
287
expect(fooInst.p2).toBe('b');
288
});
289
290
it('handles prototype methods without params', function() {
291
var code = transform([
292
'class Foo {',
293
' bar() {',
294
' return "stuff";',
295
' }',
296
'}'
297
].join('\n'));
298
299
eval(code);
300
301
var fooInst = new Foo();
302
expect(fooInst.bar()).toBe('stuff');
303
});
304
305
it('handles prototype methods with params', function() {
306
var code = transform([
307
'class Foo {',
308
' bar(p1, p2) {',
309
' this.p1 = p1;',
310
' this.p2 = p2;',
311
' }',
312
'}'
313
].join('\n'));
314
315
eval(code);
316
317
var fooInst = new Foo();
318
fooInst.bar('a', 'b');
319
expect(fooInst.p1).toBe('a');
320
expect(fooInst.p2).toBe('b');
321
});
322
323
it('handles static methods without params', function() {
324
var code = transform([
325
'class Foo {',
326
' static bar() {',
327
' return "stuff";',
328
' }',
329
'}'
330
].join('\n'));
331
332
eval(code);
333
334
expect(Foo.bar()).toBe('stuff');
335
var fooInst = new Foo();
336
expect(fooInst.bar).toBe(undefined);
337
});
338
339
it('handles static methods with params', function() {
340
var code = transform([
341
'class Foo {',
342
' static bar(p1, p2) {',
343
' return [p1, p2];',
344
' }',
345
'}'
346
].join('\n'));
347
348
eval(code);
349
350
expect(Foo.bar('a', 'b')).toEqual(['a', 'b']);
351
var fooInst = new Foo();
352
expect(fooInst.bar).toBe(undefined);
353
});
354
355
it('handles extension from an identifier', function() {
356
var code = transform([
357
'function Parent() {}',
358
'Parent.prototype.protoProp = "protoProp";',
359
'Parent.staticProp = "staticProp";',
360
361
'class Child extends Parent {}'
362
].join('\n'));
363
364
var exports = new Function(
365
code + 'return {Child: Child, Parent: Parent};'
366
)();
367
var Child = exports.Child;
368
var Parent = exports.Parent;
369
370
expect(Child.protoProp).toBe(undefined);
371
expect(Child.staticProp).toBe('staticProp');
372
var childInst = new Child();
373
expect(childInst instanceof Child).toBe(true);
374
expect(childInst instanceof Parent).toBe(true);
375
expect(childInst.protoProp).toBe('protoProp');
376
});
377
378
// ES6 draft section 14.5
379
it('handles extension from a left hand expression', function() {
380
var code = transform([
381
'function Parent1() {}',
382
'Parent1.prototype.protoProp = "protoProp";',
383
'Parent1.staticProp = "staticProp";',
384
385
'function Parent2() {}',
386
387
'class Child extends (true ? Parent1 : Parent2) {}'
388
].join('\n'));
389
390
var exports = new Function(
391
code + 'return {Parent1: Parent1, Child: Child};'
392
)();
393
var Child = exports.Child;
394
var Parent1 = exports.Parent1;
395
396
expect(Child.protoProp).toBe(undefined);
397
expect(Child.staticProp).toBe('staticProp');
398
var childInst = new Child();
399
expect(childInst instanceof Child).toBe(true);
400
expect(childInst instanceof Parent1).toBe(true);
401
expect(childInst.protoProp).toBe('protoProp');
402
expect(childInst.staticProp).toBe(undefined);
403
});
404
405
it('runs parent constructor when child constructor absent', function() {
406
var code = transform([
407
'class Parent {',
408
' constructor(p1, p2) {',
409
' this.p1 = p1;',
410
' this.p2 = p2;',
411
' }',
412
'}',
413
414
'class Child extends Parent {}'
415
].join('\n'));
416
417
var Child = new Function(code + 'return Child;')();
418
419
var childInst = new Child('a', 'b');
420
expect(childInst.p1).toBe('a');
421
expect(childInst.p2).toBe('b');
422
});
423
424
it('sets constructor property to point at constructor func', function() {
425
var code = transform([
426
'class Parent {}',
427
'class Child extends Parent {}'
428
].join('\n'));
429
430
var Child = new Function(code + 'return Child;')();
431
432
var childInst = new Child();
433
expect(childInst.constructor).toBe(Child);
434
});
435
436
it('handles super CallExpressions within constructors', function() {
437
var code = transform([
438
'class Parent {',
439
' constructor(p1, p2) {',
440
' this.p1 = p1;',
441
' this.p2 = p2;',
442
' }',
443
'}',
444
445
'class Child extends Parent {',
446
' constructor() {',
447
' super("a", "b");',
448
' this.childRan = true;',
449
' }',
450
'}'
451
].join('\n'));
452
453
var Child = new Function(code + 'return Child;')();
454
455
var childInst = new Child();
456
expect(childInst.p1).toBe('a');
457
expect(childInst.p2).toBe('b');
458
expect(childInst.childRan).toBe(true);
459
});
460
461
it('handles super CallExpressions within proto methods', function() {
462
var code = transform([
463
'class Parent {',
464
' bar(p1, p2) {',
465
' this.p1 = p1;',
466
' this.p2 = p2;',
467
' }',
468
' "baz qux"(p3) {',
469
' this.p3 = p3;',
470
' }',
471
'}',
472
473
'class Child extends Parent {',
474
' bar() {',
475
' super("a", "b");',
476
' this.barRan = true;',
477
' }',
478
' "baz qux"() {',
479
' super("c");',
480
' this["baz qux run"] = true;',
481
' }',
482
'}'
483
].join('\n'));
484
485
var Child = new Function(code + 'return Child;')();
486
487
var childInst = new Child();
488
expect(childInst.p1).toBe(undefined);
489
expect(childInst.p2).toBe(undefined);
490
expect(childInst.barRan).toBe(undefined);
491
492
childInst.bar();
493
expect(childInst.p1).toBe('a');
494
expect(childInst.p2).toBe('b');
495
expect(childInst.barRan).toBe(true);
496
497
expect(childInst.p3).toBe(undefined);
498
expect(childInst['baz qux run']).toBe(undefined);
499
500
childInst['baz qux']();
501
expect(childInst.p3).toBe('c');
502
expect(childInst['baz qux run']).toBe(true);
503
});
504
505
it('handles computed super MemberExpressions',
506
function() {
507
var code = transform([
508
'class Parent {',
509
' constructor() {',
510
' this.counter = 0;',
511
' }',
512
' incrementCounter(amount) {',
513
' this.counter += amount;',
514
' }',
515
'}',
516
517
'class Child extends Parent {',
518
' childIncrement() {',
519
' super["increment" + "Counter"](2);',
520
' }',
521
'}'
522
].join('\n'));
523
524
var Child = new Function(code + 'return Child;')();
525
526
var childInst = new Child();
527
expect(childInst.counter).toBe(0);
528
childInst.childIncrement();
529
expect(childInst.counter).toBe(2);
530
});
531
532
it('handles simple super MemberExpression access', function() {
533
var code = transform([
534
'class Parent {',
535
' getFoo(p) {',
536
' return "foo" + p;',
537
' }',
538
'}',
539
540
'class Child extends Parent {',
541
' getChildFoo() {',
542
' var x = super.getFoo;',
543
' return x("bar");',
544
' }',
545
'}'
546
].join('\n'));
547
548
var Child = new Function(code + 'return Child;')();
549
550
var childInst = new Child();
551
expect(childInst.getChildFoo()).toBe('foobar');
552
});
553
554
it('handles CallExpression on a super MemberExpression', function() {
555
var code = transform([
556
'class Parent {',
557
' getFoo(p) {',
558
' this.fooValue = "foo";',
559
' return this.fooValue + p;',
560
' }',
561
'}',
562
563
'class Child extends Parent {',
564
' getChildFoo() {',
565
' return super.getFoo.call(this, "bar");',
566
' }',
567
'}'
568
].join('\n'));
569
570
var Child = new Function(code + 'return Child;')();
571
572
var childInst = new Child();
573
expect(childInst.getChildFoo()).toBe('foobar');
574
expect(childInst.fooValue).toBe('foo');
575
});
576
577
it('handles super MemberExpressions within constructors', function() {
578
var code = transform([
579
'class Parent {',
580
' setParams(p1, p2) {',
581
' this.p1 = p1;',
582
' this.p2 = p2;',
583
' }',
584
'}',
585
586
'class Child extends Parent {',
587
' constructor() {',
588
' super.setParams("a", "b");',
589
' }',
590
'}'
591
].join('\n'));
592
593
var Child = new Function(code + 'return Child;')();
594
595
var childInst = new Child();
596
expect(childInst.p1).toBe('a');
597
expect(childInst.p2).toBe('b');
598
});
599
600
it('handles super MemberExpressions within proto methods', function() {
601
var code = transform([
602
'class Parent {',
603
' setParams(p1, p2) {',
604
' this.p1 = p1;',
605
' this.p2 = p2;',
606
' }',
607
'}',
608
609
'class Child extends Parent {',
610
' bar() {',
611
' super.setParams("a", "b");',
612
' this.barRan = true;',
613
' }',
614
'}'
615
].join('\n'));
616
617
var Child = new Function(code + 'return Child;')();
618
619
var childInst = new Child();
620
expect(childInst.p1).toBe(undefined);
621
expect(childInst.p2).toBe(undefined);
622
expect(childInst.barRan).toBe(undefined);
623
childInst.bar();
624
expect(childInst.p1).toBe('a');
625
expect(childInst.p2).toBe('b');
626
expect(childInst.barRan).toBe(true);
627
});
628
629
it('consistently munges private property identifiers', function() {
630
var code = transform([
631
'class Foo {',
632
' constructor(p1) {',
633
' this._p1 = p1;',
634
' }',
635
' getP1() {',
636
' return this._p1;',
637
' }',
638
'}'
639
].join('\n'));
640
641
eval(code);
642
643
var fooInst = new Foo('a');
644
expect(fooInst._p1).toBe(undefined);
645
expect(fooInst.getP1()).toBe('a');
646
});
647
648
it('stores munged private properties on the instance', function() {
649
// Protects against subtle transform bugs like:
650
// `this._p1 = 42` -> `this$Foo_p1 = 42`
651
var code = transform([
652
'class Foo {',
653
' constructor(p1) {',
654
' this._p1 = p1;',
655
' }',
656
' getP1() {',
657
' return this._p1;',
658
' }',
659
'}'
660
].join('\n'));
661
662
eval(code);
663
664
var fooInst1 = new Foo('a');
665
var fooInst2 = new Foo('b');
666
expect(fooInst1.getP1()).toBe('a');
667
expect(fooInst2.getP1()).toBe('b');
668
});
669
670
it('consistently munges nested private property identifiers', function() {
671
var code = transform([
672
'class Foo {',
673
' constructor(p1) {',
674
' this._data = {_p1: null};',
675
' this._data._p1 = p1;',
676
' }',
677
' getData() {',
678
' return this._data;',
679
' }',
680
' getP1() {',
681
' return this._data._p1;',
682
' }',
683
'}'
684
].join('\n'));
685
686
eval(code);
687
688
var fooInst = new Foo('a');
689
expect(fooInst.getData()._p1).toBe(undefined);
690
expect(fooInst.getP1()).toBe('a');
691
});
692
693
it('consistently munges private method identifiers', function() {
694
var code = transform([
695
'class Foo {',
696
' getBar() {',
697
' return this._getBar();',
698
' }',
699
' _getBar() {',
700
' return 42;',
701
' }',
702
'}'
703
].join('\n'));
704
705
eval(code);
706
707
var fooInst = new Foo();
708
expect(fooInst._getBar).toBe(undefined);
709
expect(fooInst.getBar()).toBe(42);
710
});
711
712
it('consistently munges private method params', function() {
713
var code = transform([
714
'class Foo {',
715
' bar(_counter, _function) {',
716
' this.counter = _counter;',
717
' _function();',
718
' }',
719
'}'
720
].join('\n'));
721
722
eval(code);
723
724
var fooInst = new Foo();
725
var callbackCalled = false;
726
fooInst.bar(42, function() { callbackCalled = true; });
727
expect(fooInst.counter).toBe(42);
728
expect(callbackCalled).toBe(true);
729
});
730
731
it('consistently munges private idents in super call params', function() {
732
var code = transform([
733
'class Parent {',
734
' constructor(foo) {',
735
' this.foo = foo;',
736
' }',
737
' setBar(bar) {',
738
' this.bar = bar;',
739
' }',
740
'}',
741
'class Child extends Parent {',
742
' constructor(_foo, _bar) {',
743
' super(_foo);',
744
' super.setBar(_bar);',
745
' }',
746
'}'
747
].join('\n'));
748
749
var Child = new Function(code + 'return Child;')();
750
751
var childInst = new Child('foo', 'bar');
752
expect(childInst.foo).toBe('foo');
753
expect(childInst.bar).toBe('bar');
754
});
755
756
it('consistently munges private idents in nested funcs', function() {
757
var code = transform([
758
'class Foo {',
759
' bar(_p1, p2) {',
760
' return function(_a) {',
761
' return [_p1, p2, _a];',
762
' };',
763
' }',
764
'}'
765
].join('\n'));
766
767
eval(code);
768
769
var fooInst = new Foo();
770
expect(fooInst.bar('a', 'b')('c')).toEqual(['a', 'b', 'c']);
771
});
772
773
it('consistently munges private idents in nested arrow funcs', function() {
774
var code = transform([
775
'class Foo {',
776
' bar(_p1, p2) {',
777
' return (_a, b) => {',
778
' return [_p1, p2, _a, b];',
779
' };',
780
' }',
781
'}'
782
].join('\n'));
783
784
eval(code);
785
786
var fooInst = new Foo();
787
expect(fooInst.bar('a', 'b')('c', 'd')).toEqual(['a', 'b', 'c', 'd']);
788
});
789
790
it('does not munge dunder-scored properties', function() {
791
var code = transform([
792
'class Foo {',
793
' constructor(p1) {',
794
' this.__p1 = p1;',
795
' }',
796
'}'
797
].join('\n'));
798
799
eval(code);
800
801
var fooInst = new Foo('a');
802
expect(fooInst.__p1).toBe('a');
803
});
804
805
it('does not munge dunder-scored methods', function() {
806
var code = transform([
807
'class Foo {',
808
' __getBar() {',
809
' return 42;',
810
' }',
811
'}'
812
].join('\n'));
813
814
eval(code);
815
816
var fooInst = new Foo();
817
expect(fooInst.__getBar()).toBe(42);
818
});
819
820
it('properly handles private vars declared in outer scope', function() {
821
var code = transform([
822
'var _bar = "outer";',
823
'class Foo {',
824
' getOuterBar() {',
825
' return _bar;',
826
' }',
827
'}'
828
].join('\n'));
829
830
var Foo = new Function(code + 'return Foo;')();
831
832
var fooInst = new Foo();
833
expect(fooInst.getOuterBar()).toBe('outer');
834
});
835
836
it('does not munge outer-declared private vars when used to calculate ' +
837
'a computed member expression', function() {
838
var code = transform([
839
'var _privateObjKey = "pvt";',
840
'var outerDataStore = {pvt: 42};',
841
'class Foo {',
842
' getStuff() {',
843
' return outerDataStore[_privateObjKey];',
844
' }',
845
'}'
846
].join('\n'));
847
848
var Foo = new Function(code + 'return Foo;')();
849
850
var fooInst = new Foo();
851
expect(fooInst.getStuff()).toBe(42);
852
});
853
854
it('properly handles private vars declared in inner scope', function() {
855
var code = transform([
856
'var _bar = {_private: 42};',
857
'class Foo {',
858
' getBarPrivate(p1) {',
859
' var _bar = {_private: p1};',
860
' return _bar._private;',
861
' }',
862
'}'
863
].join('\n'));
864
865
eval(code);
866
867
var fooInst = new Foo();
868
expect(fooInst.getBarPrivate('a')).toBe('a');
869
});
870
871
it('munges properties of private vars declared out of scope', function() {
872
var code = transform([
873
'var _bar = {_private: 42}',
874
'class Foo {',
875
' getOuterPrivate() {',
876
' return _bar._private;',
877
' }',
878
'}'
879
].join('\n'));
880
881
var exports = new Function(code + 'return {_bar: _bar, Foo: Foo};')();
882
var _bar = exports._bar;
883
var Foo = exports.Foo;
884
885
var fooInst = new Foo();
886
expect(_bar._private).toBe(42);
887
expect(fooInst.getOuterPrivate()).toBe(undefined);
888
});
889
890
it('does not munge when @preventMunge is specified', function() {
891
var code = transform([
892
'/**',
893
' * @preventMunge',
894
' */',
895
'class Foo {',
896
' constructor(p1) {',
897
' this._p1 = p1;',
898
' }',
899
' _privateMethod() {',
900
' }',
901
'}'
902
].join('\n'));
903
904
eval(code);
905
906
var fooInst = new Foo('a');
907
expect(fooInst._p1).toBe('a');
908
expect(fooInst._privateMethod).not.toBe(undefined);
909
});
910
911
it('minifies private properties when minify opt is set', function() {
912
var code = transform([
913
'class Foo {',
914
' constructor(p1) {',
915
' this._p1 = p1;',
916
' }',
917
'}'
918
].join('\n'), {minify: true});
919
920
eval(code);
921
922
var fooInst = new Foo('a');
923
expect(fooInst.$Foo0).toBe('a');
924
});
925
926
it('minifies private methods when minify opt is set', function() {
927
var code = transform([
928
'class Foo {',
929
' _bar() {',
930
' return 42;',
931
' }',
932
'}'
933
].join('\n'), {minify: true});
934
935
eval(code);
936
937
var fooInst = new Foo();
938
expect(fooInst.$Foo0()).toBe(42);
939
});
940
941
it('munges child class different from parent in same file', function() {
942
var code = transform([
943
'class Parent {',
944
' setParentFoo(foo) {',
945
' this._foo = foo;',
946
' }',
947
' getParentFoo() {',
948
' return this._foo;',
949
' }',
950
'}',
951
952
'class Child extends Parent {',
953
' setChildFoo(foo) {',
954
' this._foo = foo;',
955
' }',
956
' getChildFoo() {',
957
' return this._foo;',
958
' }',
959
'}'
960
].join('\n'));
961
962
var Child = new Function(code + 'return Child;')();
963
964
var childInst = new Child();
965
childInst.setParentFoo('parent');
966
childInst.setChildFoo('child');
967
expect(childInst.getParentFoo()).toBe('parent');
968
expect(childInst.getChildFoo()).toBe('child');
969
});
970
971
it('munges child class different from parent in other file', function() {
972
var code1 = transform([
973
'class Parent {',
974
' setParentFoo(foo) {',
975
' this._foo = foo;',
976
' }',
977
' getParentFoo() {',
978
' return this._foo;',
979
' }',
980
'}'
981
].join('\n'));
982
983
var code2 = transform([
984
'class Child extends Parent {',
985
' setChildFoo(foo) {',
986
' this._foo = foo;',
987
' }',
988
' getChildFoo() {',
989
' return this._foo;',
990
' }',
991
'}'
992
].join('\n'));
993
994
var exports = new Function(
995
code1 + code2 + 'return {Parent: Parent, Child: Child};'
996
)();
997
var Parent = exports.Parent;
998
var Child = exports.Child;
999
1000
var childInst = new Child();
1001
childInst.setParentFoo('parent');
1002
childInst.setChildFoo('child');
1003
expect(childInst.getParentFoo()).toBe('parent');
1004
expect(childInst.getChildFoo()).toBe('child');
1005
});
1006
1007
it('makes class methods implicitly "use strict"', function() {
1008
var code = transform([
1009
'class Foo {',
1010
' constructor() {',
1011
' this.constructorIsStrict = ' +
1012
'(function() {return this === undefined;})();',
1013
' }',
1014
' protoFn() {',
1015
' return (function() {return this === undefined;})();',
1016
' }',
1017
' static staticFn() {',
1018
' return (function() {return this === undefined;})();',
1019
' }',
1020
'}'
1021
].join('\n'));
1022
1023
eval(code);
1024
1025
var fooInst = new Foo();
1026
expect(fooInst.constructorIsStrict).toBe(true);
1027
expect(fooInst.protoFn()).toBe(true);
1028
expect(Foo.staticFn()).toBe(true);
1029
});
1030
1031
it('preserves generators', function() {
1032
var code = transform([
1033
'class Foo {',
1034
' static *title() {',
1035
' yield 21;',
1036
' }',
1037
'',
1038
' *gen() {',
1039
' yield 42;',
1040
' }',
1041
'}'
1042
].join('\n'));
1043
1044
expect(code).toMatch(/Foo.title\s*=\s*function\*\(/);
1045
expect(code).toMatch(/Foo.prototype.gen\s*=\s*function\*\(/);
1046
});
1047
1048
it('properly handles getter methods in ES5 compat mode', function() {
1049
var code = transform([
1050
'class Foo {',
1051
' get title() {',
1052
' return 42;',
1053
' }',
1054
' get "foo bar"() {',
1055
' return 21;',
1056
' }',
1057
'}'
1058
].join('\n'), {es5: true});
1059
1060
eval(code);
1061
1062
var fooInst = new Foo();
1063
var descriptor =
1064
Object.getOwnPropertyDescriptor(Foo.prototype, 'title');
1065
1066
expect(fooInst.title).toBe(42);
1067
expect(descriptor.enumerable).toBe(true);
1068
expect(descriptor.configurable).toBe(true);
1069
expect(fooInst['foo bar']).toBe(21);
1070
});
1071
1072
it('properly handles setter methods in ES5 compat mode', function() {
1073
var code = transform([
1074
'class Foo {',
1075
' set title(value) {',
1076
' this.__title = 42;',
1077
' }',
1078
' set "foo bar"(value) {',
1079
' this.__fooBar = 21;',
1080
' }',
1081
'}'
1082
].join('\n'), {es5: true});
1083
1084
eval(code);
1085
1086
var fooInst = new Foo();
1087
fooInst.title = 42;
1088
fooInst['foo bar'] = 21;
1089
var descriptor =
1090
Object.getOwnPropertyDescriptor(Foo.prototype, 'title');
1091
1092
expect(fooInst.__title).toBe(42);
1093
expect(descriptor.enumerable).toBe(true);
1094
expect(descriptor.configurable).toBe(true);
1095
expect(fooInst.__fooBar).toBe(21);
1096
});
1097
1098
it('properly handles getters and setters in ES5 compat mode', function() {
1099
var code = transform([
1100
'class Foo {',
1101
' get title() {',
1102
' return this.__title;',
1103
' }',
1104
'',
1105
' set title(value) {',
1106
' this.__title = value;',
1107
' }',
1108
'}'
1109
].join('\n'), {es5: true});
1110
1111
eval(code);
1112
1113
var fooInst = new Foo();
1114
var descriptor =
1115
Object.getOwnPropertyDescriptor(Foo.prototype, 'title');
1116
fooInst.title = 42;
1117
1118
expect(fooInst.__title).toBe(42);
1119
expect(fooInst.title).toBe(42);
1120
expect(descriptor.enumerable).toBe(true);
1121
expect(descriptor.configurable).toBe(true);
1122
});
1123
1124
it('properly handles static and non-static getters and setters ' +
1125
'with the same name in ES5 compat mode', function() {
1126
var code = transform([
1127
'class Foo {',
1128
' static get title() {',
1129
' return this._title;',
1130
' }',
1131
'',
1132
' static set title(value) {',
1133
' this._title = value;',
1134
' }',
1135
'',
1136
' get title() {',
1137
' return this.__title;',
1138
' }',
1139
'',
1140
' set title(value) {',
1141
' this.__title = value;',
1142
' }',
1143
'}'
1144
].join('\n'), {es5: true});
1145
1146
eval(code);
1147
1148
var fooInst = new Foo();
1149
Foo.title = 21;
1150
fooInst.title = 42;
1151
1152
expect(Foo.title).toBe(21);
1153
expect(fooInst.title).toBe(42);
1154
});
1155
1156
it('properly handles private getters and setters in ES5 compat mode',
1157
function() {
1158
var code = transform([
1159
'class Foo {',
1160
' get title() {',
1161
' return this._private;',
1162
' }',
1163
'',
1164
' set title(value) {',
1165
' this._private = value;',
1166
' }',
1167
'',
1168
' get _private() {',
1169
' return this.__superPrivate;',
1170
' }',
1171
'',
1172
' set _private(value) {',
1173
' this.__superPrivate = value;',
1174
' }',
1175
'}'
1176
].join('\n'), {es5: true});
1177
1178
eval(code);
1179
1180
var fooInst = new Foo();
1181
fooInst.title = 42;
1182
1183
expect(fooInst.__superPrivate).toBe(42);
1184
});
1185
1186
it('throws upon encountering getter methods', function() {
1187
expect(function() {
1188
transform([
1189
'class Foo {',
1190
' get title() {',
1191
' return 42;',
1192
' }',
1193
'}'
1194
].join('\n'));
1195
}).toThrow(
1196
'This transform does not support getter methods for ES6 classes. ' +
1197
'(line: 2, col: 2)'
1198
);
1199
});
1200
1201
it('throws upon encountering setter methods', function() {
1202
expect(function() {
1203
transform([
1204
'class Foo {',
1205
' set title(value) {',
1206
' this._title = value;',
1207
' }',
1208
'}'
1209
].join('\n'));
1210
}).toThrow(
1211
'This transform does not support setter methods for ES6 classes. ' +
1212
'(line: 2, col: 2)'
1213
);
1214
});
1215
});
1216
});
1217
1218
describe('ClassExpressions', function() {
1219
describe('preserves line numbers', function() {
1220
it('preserves lines with no inheritance', function() {
1221
var code = [
1222
'var Foo = class {',
1223
' foo() {',
1224
' ',
1225
' ',
1226
' }',
1227
'',
1228
' constructor(p1,',
1229
' p2) {',
1230
'',
1231
' this.p1 = p1;',
1232
' this.p2 = p2;',
1233
' }',
1234
'',
1235
' bar(){}',
1236
' static baz() {',
1237
'}',
1238
'}'
1239
].join('\n');
1240
1241
var expected = [
1242
'var Foo = (function(){',
1243
' ____Class0.prototype.foo=function() {"use strict";',
1244
' ',
1245
' ',
1246
' };',
1247
'',
1248
' function ____Class0(p1,',
1249
' p2) {"use strict";',
1250
'',
1251
' this.p1 = p1;',
1252
' this.p2 = p2;',
1253
' }',
1254
'',
1255
' ____Class0.prototype.bar=function(){"use strict";};',
1256
' ____Class0.baz=function() {"use strict";',
1257
'};',
1258
'return ____Class0;})()'
1259
].join('\n');
1260
1261
expect(transform(code)).toBe(expected);
1262
});
1263
1264
it('preserves lines with inheritance from identifier', function() {
1265
var code = [
1266
'var Foo = class extends Bar {',
1267
' foo() {',
1268
' ',
1269
' ',
1270
' super(p1,',
1271
' p2);',
1272
' }',
1273
'',
1274
' constructor(p1,',
1275
' p2) {',
1276
'',
1277
' this.p1 = p1;',
1278
' this.p2 = p2;',
1279
' super.blah(p1,',
1280
' p2);',
1281
' }',
1282
'',
1283
' bar(){}',
1284
' static baz() {',
1285
'}',
1286
'}'
1287
].join('\n');
1288
1289
var expected = [
1290
'var Foo = (function(){' +
1291
'for(var Bar____Key in Bar){' +
1292
'if(Bar.hasOwnProperty(Bar____Key)){' +
1293
'____Class0[Bar____Key]=Bar[Bar____Key];' +
1294
'}' +
1295
'}' +
1296
'var ____SuperProtoOfBar=' +
1297
'Bar===null' +
1298
'?null' +
1299
':Bar.prototype;' +
1300
'____Class0.prototype=Object.create(____SuperProtoOfBar);' +
1301
'____Class0.prototype.constructor=____Class0;' +
1302
'____Class0.__superConstructor__=Bar;',
1303
1304
' ____Class0.prototype.foo=function() {"use strict";',
1305
' ',
1306
' ',
1307
' ____SuperProtoOfBar.foo.call(this,p1,',
1308
' p2);',
1309
' };',
1310
'',
1311
' function ____Class0(p1,',
1312
' p2) {"use strict";',
1313
'',
1314
' this.p1 = p1;',
1315
' this.p2 = p2;',
1316
' ____SuperProtoOfBar.blah.call(this,p1,',
1317
' p2);',
1318
' }',
1319
'',
1320
' ____Class0.prototype.bar=function(){"use strict";};',
1321
' ____Class0.baz=function() {"use strict";',
1322
'};',
1323
'return ____Class0;})()'
1324
].join('\n');
1325
1326
expect(transform(code)).toBe(expected);
1327
});
1328
1329
it('preserves lines with inheritance from expression', function() {
1330
var code = [
1331
'var Foo = class extends mixin(Bar, Baz) {',
1332
' foo() {',
1333
' ',
1334
' ',
1335
' }',
1336
'',
1337
' constructor(p1,',
1338
' p2) {',
1339
'',
1340
' this.p1 = p1;',
1341
' this.p2 = p2;',
1342
' }',
1343
'',
1344
' bar(){}',
1345
' static baz() {',
1346
'}',
1347
'}'
1348
].join('\n');
1349
1350
var expected = [
1351
'var Foo = (function(){' +
1352
'var ____Class1=mixin(Bar, Baz);' +
1353
'for(var ____Class1____Key in ____Class1){' +
1354
'if(____Class1.hasOwnProperty(____Class1____Key)){' +
1355
'____Class0[____Class1____Key]=____Class1[____Class1____Key];' +
1356
'}' +
1357
'}' +
1358
'var ____SuperProtoOf____Class1=' +
1359
'____Class1===null' +
1360
'?null' +
1361
':____Class1.prototype;' +
1362
'____Class0.prototype=Object.create(____SuperProtoOf____Class1);' +
1363
'____Class0.prototype.constructor=____Class0;' +
1364
'____Class0.__superConstructor__=____Class1;',
1365
1366
' ____Class0.prototype.foo=function() {"use strict";',
1367
' ',
1368
' ',
1369
' };',
1370
'',
1371
' function ____Class0(p1,',
1372
' p2) {"use strict";',
1373
'',
1374
' this.p1 = p1;',
1375
' this.p2 = p2;',
1376
' }',
1377
'',
1378
' ____Class0.prototype.bar=function(){"use strict";};',
1379
' ____Class0.baz=function() {"use strict";',
1380
'};',
1381
'return ____Class0;})()'
1382
].join('\n');
1383
1384
expect(transform(code)).toBe(expected);
1385
});
1386
});
1387
1388
describe('functional tests', function() {
1389
it('scopes each anonymous class separately', function() {
1390
var code = transform([
1391
'var Foo = class {',
1392
' constructor() {',
1393
' this._name = "foo";',
1394
' var properties = [];',
1395
' for (var key in this) {',
1396
' properties.push(key);',
1397
' }',
1398
' this.properties = properties',
1399
' }',
1400
'};',
1401
1402
'var Bar = class {',
1403
' constructor() {',
1404
' this._name = "bar";',
1405
' var properties = [];',
1406
' for (var key in this) {',
1407
' properties.push(key);',
1408
' }',
1409
' this.properties = properties',
1410
' }',
1411
'}'
1412
].join('\n'));
1413
1414
eval(code);
1415
1416
var fooInst = new Foo();
1417
var barInst = new Bar();
1418
expect(fooInst.properties).not.toEqual(barInst.properties);
1419
expect(fooInst[fooInst.properties[0]]).toBe('foo');
1420
expect(barInst[barInst.properties[0]]).toBe('bar');
1421
});
1422
});
1423
});
1424
1425
describe('handles reserved words', function() {
1426
it('handles reserved words', function() {
1427
expect(transform([
1428
'class Foo {',
1429
' delete(x, y) {',
1430
' bar();',
1431
' }',
1432
'}'
1433
].join('\n'))).toBe([
1434
'function Foo(){"use strict";}',
1435
' Foo.prototype["delete"]=function(x, y) {"use strict";',
1436
' bar();',
1437
' };',
1438
''
1439
].join('\n'))
1440
});
1441
});
1442
1443
describe('preserve newlines', function() {
1444
it('preserve newlines', function() {
1445
expect(transform([
1446
'class Foo {',
1447
' A',
1448
' (',
1449
' x, y) {',
1450
' bar();',
1451
' }',
1452
'}'
1453
].join('\n'))).toBe([
1454
'function Foo(){"use strict";}',
1455
' Foo.prototype.A=function(',
1456
'',
1457
'x, y) {"use strict";',
1458
' bar();',
1459
' };',
1460
''
1461
].join('\n'))
1462
});
1463
});
1464
});
1465
1466