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