• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    describe("unittests:: services:: extract:: extractFunctions", () => {
3        testExtractFunction("extractFunction1",
4            `namespace A {
5    let x = 1;
6    function foo() {
7    }
8    namespace B {
9        function a() {
10            let a = 1;
11        [#|
12            let y = 5;
13            let z = x;
14            a = y;
15            foo();|]
16        }
17    }
18}`);
19        testExtractFunction("extractFunction2",
20            `namespace A {
21    let x = 1;
22    function foo() {
23    }
24    namespace B {
25        function a() {
26        [#|
27            let y = 5;
28            let z = x;
29            return foo();|]
30        }
31    }
32}`);
33        testExtractFunction("extractFunction3",
34            `namespace A {
35    function foo() {
36    }
37    namespace B {
38        function* a(z: number) {
39        [#|
40            let y = 5;
41            yield z;
42            return foo();|]
43        }
44    }
45}`);
46        testExtractFunction("extractFunction4",
47            `namespace A {
48    function foo() {
49    }
50    namespace B {
51        async function a(z: number, z1: any) {
52        [#|
53            let y = 5;
54            if (z) {
55                await z1;
56            }
57            return foo();|]
58        }
59    }
60}`);
61        testExtractFunction("extractFunction5",
62            `namespace A {
63    let x = 1;
64    export function foo() {
65    }
66    namespace B {
67        function a() {
68            let a = 1;
69        [#|
70            let y = 5;
71            let z = x;
72            a = y;
73            foo();|]
74        }
75    }
76}`);
77        testExtractFunction("extractFunction6",
78            `namespace A {
79    let x = 1;
80    export function foo() {
81    }
82    namespace B {
83        function a() {
84            let a = 1;
85        [#|
86            let y = 5;
87            let z = x;
88            a = y;
89            return foo();|]
90        }
91    }
92}`);
93        testExtractFunction("extractFunction7",
94            `namespace A {
95    let x = 1;
96    export namespace C {
97        export function foo() {
98        }
99    }
100    namespace B {
101        function a() {
102            let a = 1;
103        [#|
104            let y = 5;
105            let z = x;
106            a = y;
107            return C.foo();|]
108        }
109    }
110}`);
111        testExtractFunction("extractFunction9",
112            `namespace A {
113    export interface I { x: number };
114    namespace B {
115        function a() {
116            [#|let a1: I = { x: 1 };
117            return a1.x + 10;|]
118        }
119    }
120}`);
121        testExtractFunction("extractFunction10",
122            `namespace A {
123    export interface I { x: number };
124    class C {
125        a() {
126            let z = 1;
127            [#|let a1: I = { x: 1 };
128            return a1.x + 10;|]
129        }
130    }
131}`);
132        testExtractFunction("extractFunction11",
133            `namespace A {
134    let y = 1;
135    class C {
136        a() {
137            let z = 1;
138            [#|let a1 = { x: 1 };
139            y = 10;
140            z = 42;
141            return a1.x + 10;|]
142        }
143    }
144}`);
145        testExtractFunction("extractFunction12",
146            `namespace A {
147    let y = 1;
148    class C {
149        b() {}
150        a() {
151            let z = 1;
152            [#|let a1 = { x: 1 };
153            y = 10;
154            z = 42;
155            this.b();
156            return a1.x + 10;|]
157        }
158    }
159}`);
160        // The "b" type parameters aren't used and shouldn't be passed to the extracted function.
161        // Type parameters should be in syntactic order (i.e. in order or character offset from BOF).
162        // In all cases, we could use type inference, rather than passing explicit type arguments.
163        // Note the inclusion of arrow functions to ensure that some type parameters are not from
164        //   targetable scopes.
165        testExtractFunction("extractFunction13",
166            `<U1a, U1b>(u1a: U1a, u1b: U1b) => {
167    function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
168        <U2a, U2b>(u2a: U2a, u2b: U2b) => {
169            function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
170                <U3a, U3b>(u3a: U3a, u3b: U3b) => {
171                        [#|t1a.toString();
172                        t2a.toString();
173                        u1a.toString();
174                        u2a.toString();
175                        u3a.toString();|]
176                }
177            }
178        }
179    }
180}`);
181        // This test is descriptive, rather than normative.  The current implementation
182        // doesn't handle type parameter shadowing.
183        testExtractFunction("extractFunction14",
184            `function F<T>(t1: T) {
185    function G<T>(t2: T) {
186        [#|t1.toString();
187        t2.toString();|]
188    }
189}`);
190        // Confirm that the constraint is preserved.
191        testExtractFunction("extractFunction15",
192            `function F<T>(t1: T) {
193    function G<U extends T[]>(t2: U) {
194        [#|t2.toString();|]
195    }
196}`, /*includeLib*/ true);
197        // Confirm that the contextual type of an extracted expression counts as a use.
198        testExtractFunction("extractFunction16",
199            `function F<T>() {
200    const array: T[] = [#|[]|];
201}`, /*includeLib*/ true);
202        // Class type parameter
203        testExtractFunction("extractFunction17",
204            `class C<T1, T2> {
205    M(t1: T1, t2: T2) {
206        [#|t1.toString()|];
207    }
208}`);
209        // Function type parameter
210        testExtractFunction("extractFunction18",
211            `class C {
212    M<T1, T2>(t1: T1, t2: T2) {
213        [#|t1.toString()|];
214    }
215}`);
216        // Coupled constraints
217        testExtractFunction("extractFunction19",
218            `function F<T, U extends T[], V extends U[]>(v: V) {
219    [#|v.toString()|];
220}`, /*includeLib*/ true);
221
222        testExtractFunction("extractFunction20",
223            `const _ = class {
224    a() {
225        [#|let a1 = { x: 1 };
226        return a1.x + 10;|]
227    }
228}`);
229        // Write + void return
230        testExtractFunction("extractFunction21",
231            `function foo() {
232    let x = 10;
233    [#|x++;
234    return;|]
235}`);
236        // Return in finally block
237        testExtractFunction("extractFunction22",
238            `function test() {
239    try {
240    }
241    finally {
242        [#|return 1;|]
243    }
244}`);
245        // Extraction position - namespace
246        testExtractFunction("extractFunction23",
247            `namespace NS {
248    function M1() { }
249    function M2() {
250        [#|return 1;|]
251    }
252    function M3() { }
253}`);
254        // Extraction position - function
255        testExtractFunction("extractFunction24",
256            `function Outer() {
257    function M1() { }
258    function M2() {
259        [#|return 1;|]
260    }
261    function M3() { }
262}`);
263        // Extraction position - file
264        testExtractFunction("extractFunction25",
265            `function M1() { }
266function M2() {
267    [#|return 1;|]
268}
269function M3() { }`);
270        // Extraction position - class without ctor
271        testExtractFunction("extractFunction26",
272            `class C {
273    M1() { }
274    M2() {
275        [#|return 1;|]
276    }
277    M3() { }
278}`);
279        // Extraction position - class with ctor in middle
280        testExtractFunction("extractFunction27",
281            `class C {
282    M1() { }
283    M2() {
284        [#|return 1;|]
285    }
286    constructor() { }
287    M3() { }
288}`);
289        // Extraction position - class with ctor at end
290        testExtractFunction("extractFunction28",
291            `class C {
292    M1() { }
293    M2() {
294        [#|return 1;|]
295    }
296    M3() { }
297    constructor() { }
298}`);
299        // Shorthand property names
300        testExtractFunction("extractFunction29",
301            `interface UnaryExpression {
302    kind: "Unary";
303    operator: string;
304    operand: any;
305}
306
307function parseUnaryExpression(operator: string): UnaryExpression {
308    [#|return {
309        kind: "Unary",
310        operator,
311        operand: parsePrimaryExpression(),
312    };|]
313}
314
315function parsePrimaryExpression(): any {
316    throw "Not implemented";
317}`);
318        // Type parameter as declared type
319        testExtractFunction("extractFunction30",
320            `function F<T>() {
321    [#|let t: T;|]
322}`);
323        // Return in nested function
324        testExtractFunction("extractFunction31",
325            `namespace N {
326
327    export const value = 1;
328
329    () => {
330        var f: () => number;
331        [#|f = function (): number {
332            return value;
333        }|]
334    }
335}`);
336        // Return in nested class
337        testExtractFunction("extractFunction32",
338            `namespace N {
339
340    export const value = 1;
341
342    () => {
343        [#|var c = class {
344            M() {
345                return value;
346            }
347        }|]
348    }
349}`);
350        // Selection excludes leading trivia of declaration
351        testExtractFunction("extractFunction33",
352            `function F() {
353    [#|function G() { }|]
354}`);
355        // Arrow function
356        testExtractFunction("extractFunction34",
357            `const F = () => {
358    [#|function G() { }|]
359};`);
360
361        testExtractFunction("extractFunction_RepeatedSubstitution",
362            `namespace X {
363    export const j = 10;
364    export const y = [#|j * j|];
365}`);
366
367        testExtractFunction("extractFunction_VariableDeclaration_Var", `
368[#|var x = 1;
369"hello"|]
370x;
371`);
372
373        testExtractFunction("extractFunction_VariableDeclaration_Let_Type", `
374[#|let x: number = 1;
375"hello";|]
376x;
377`);
378
379        testExtractFunction("extractFunction_VariableDeclaration_Let_NoType", `
380[#|let x = 1;
381"hello";|]
382x;
383`);
384
385        testExtractFunction("extractFunction_VariableDeclaration_Const_Type", `
386[#|const x: number = 1;
387"hello";|]
388x;
389`);
390
391        testExtractFunction("extractFunction_VariableDeclaration_Const_NoType", `
392[#|const x = 1;
393"hello";|]
394x;
395`);
396
397        testExtractFunction("extractFunction_VariableDeclaration_Multiple1", `
398[#|const x = 1, y: string = "a";|]
399x; y;
400`);
401
402        testExtractFunction("extractFunction_VariableDeclaration_Multiple2", `
403[#|const x = 1, y = "a";
404const z = 3;|]
405x; y; z;
406`);
407
408        testExtractFunction("extractFunction_VariableDeclaration_Multiple3", `
409[#|const x = 1, y: string = "a";
410let z = 3;|]
411x; y; z;
412`);
413
414        testExtractFunction("extractFunction_VariableDeclaration_ConsumedTwice", `
415[#|const x: number = 1;
416"hello";|]
417x; x;
418`);
419
420        testExtractFunction("extractFunction_VariableDeclaration_DeclaredTwice", `
421[#|var x = 1;
422var x = 2;|]
423x;
424`);
425
426        testExtractFunction("extractFunction_VariableDeclaration_Writes_Var", `
427function f() {
428    let a = 1;
429    [#|var x = 1;
430    a++;|]
431    a; x;
432}`);
433
434        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_NoType", `
435function f() {
436    let a = 1;
437    [#|let x = 1;
438    a++;|]
439    a; x;
440}`);
441
442        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_Type", `
443function f() {
444    let a = 1;
445    [#|let x: number = 1;
446    a++;|]
447    a; x;
448}`);
449
450        // We propagate numericLiteralFlags, but it's not consumed by the emitter,
451        // so everything comes out decimal.  It would be nice to improve this.
452        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
453function f() {
454    let a = 1;
455    [#|let x: 0o10 | 10 | 0b10 = 10;
456    a++;|]
457    a; x;
458}`);
459
460        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType2", `
461function f() {
462    let a = 1;
463    [#|let x: "a" | 'b' = 'a';
464    a++;|]
465    a; x;
466}`);
467
468        // We propagate numericLiteralFlags, but it's not consumed by the emitter,
469        // so everything comes out decimal.  It would be nice to improve this.
470        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
471function f() {
472    let a = 1;
473    [#|let x: 0o10 | 10 | 0b10 = 10;
474    a++;|]
475    a; x;
476}`);
477
478        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_TypeWithComments", `
479function f() {
480    let a = 1;
481    [#|let x: /*A*/ "a" /*B*/ | /*C*/ 'b' /*D*/ = 'a';
482    a++;|]
483    a; x;
484}`);
485
486        testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_NoType", `
487function f() {
488    let a = 1;
489    [#|const x = 1;
490    a++;|]
491    a; x;
492}`);
493
494        testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_Type", `
495function f() {
496    let a = 1;
497    [#|const x: number = 1;
498    a++;|]
499    a; x;
500}`);
501
502        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed1", `
503function f() {
504    let a = 1;
505    [#|const x = 1;
506    let y = 2;
507    a++;|]
508    a; x; y;
509}`);
510
511        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed2", `
512function f() {
513    let a = 1;
514    [#|var x = 1;
515    let y = 2;
516    a++;|]
517    a; x; y;
518}`);
519
520        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed3", `
521function f() {
522    let a = 1;
523    [#|let x: number = 1;
524    let y = 2;
525    a++;|]
526    a; x; y;
527}`);
528
529        testExtractFunction("extractFunction_VariableDeclaration_Writes_UnionUndefined", `
530function f() {
531    let a = 1;
532    [#|let x: number | undefined = 1;
533    let y: undefined | number = 2;
534    let z: (undefined | number) = 3;
535    a++;|]
536    a; x; y; z;
537}`);
538
539        testExtractFunction("extractFunction_VariableDeclaration_ShorthandProperty", `
540function f() {
541    [#|let x;|]
542    return { x };
543}`);
544
545        testExtractFunction("extractFunction_PreserveTrivia", `
546// a
547var q = /*b*/ //c
548    /*d*/ [#|1 /*e*/ //f
549    /*g*/ + /*h*/ //i
550    /*j*/ 2|] /*k*/ //l
551    /*m*/; /*n*/ //o`);
552
553        testExtractFunction("extractFunction_NamelessClass", `
554export default class {
555    M() {
556        [#|1 + 1|];
557    }
558}`);
559
560        testExtractFunction("extractFunction_NoDeclarations", `
561function F() {
562[#|arguments.length|]; // arguments has no declaration
563}`);
564    });
565
566    function testExtractFunction(caption: string, text: string, includeLib?: boolean) {
567        testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function, includeLib);
568    }
569}
570