• 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
356        testExtractFunction("extractFunction_RepeatedSubstitution",
357            `namespace X {
358    export const j = 10;
359    export const y = [#|j * j|];
360}`);
361
362        testExtractFunction("extractFunction_VariableDeclaration_Var", `
363[#|var x = 1;
364"hello"|]
365x;
366`);
367
368        testExtractFunction("extractFunction_VariableDeclaration_Let_Type", `
369[#|let x: number = 1;
370"hello";|]
371x;
372`);
373
374        testExtractFunction("extractFunction_VariableDeclaration_Let_NoType", `
375[#|let x = 1;
376"hello";|]
377x;
378`);
379
380        testExtractFunction("extractFunction_VariableDeclaration_Const_Type", `
381[#|const x: number = 1;
382"hello";|]
383x;
384`);
385
386        testExtractFunction("extractFunction_VariableDeclaration_Const_NoType", `
387[#|const x = 1;
388"hello";|]
389x;
390`);
391
392        testExtractFunction("extractFunction_VariableDeclaration_Multiple1", `
393[#|const x = 1, y: string = "a";|]
394x; y;
395`);
396
397        testExtractFunction("extractFunction_VariableDeclaration_Multiple2", `
398[#|const x = 1, y = "a";
399const z = 3;|]
400x; y; z;
401`);
402
403        testExtractFunction("extractFunction_VariableDeclaration_Multiple3", `
404[#|const x = 1, y: string = "a";
405let z = 3;|]
406x; y; z;
407`);
408
409        testExtractFunction("extractFunction_VariableDeclaration_ConsumedTwice", `
410[#|const x: number = 1;
411"hello";|]
412x; x;
413`);
414
415        testExtractFunction("extractFunction_VariableDeclaration_DeclaredTwice", `
416[#|var x = 1;
417var x = 2;|]
418x;
419`);
420
421        testExtractFunction("extractFunction_VariableDeclaration_Writes_Var", `
422function f() {
423    let a = 1;
424    [#|var x = 1;
425    a++;|]
426    a; x;
427}`);
428
429        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_NoType", `
430function f() {
431    let a = 1;
432    [#|let x = 1;
433    a++;|]
434    a; x;
435}`);
436
437        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_Type", `
438function f() {
439    let a = 1;
440    [#|let x: number = 1;
441    a++;|]
442    a; x;
443}`);
444
445        // We propagate numericLiteralFlags, but it's not consumed by the emitter,
446        // so everything comes out decimal.  It would be nice to improve this.
447        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
448function f() {
449    let a = 1;
450    [#|let x: 0o10 | 10 | 0b10 = 10;
451    a++;|]
452    a; x;
453}`);
454
455        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType2", `
456function f() {
457    let a = 1;
458    [#|let x: "a" | 'b' = 'a';
459    a++;|]
460    a; x;
461}`);
462
463        // We propagate numericLiteralFlags, but it's not consumed by the emitter,
464        // so everything comes out decimal.  It would be nice to improve this.
465        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
466function f() {
467    let a = 1;
468    [#|let x: 0o10 | 10 | 0b10 = 10;
469    a++;|]
470    a; x;
471}`);
472
473        testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_TypeWithComments", `
474function f() {
475    let a = 1;
476    [#|let x: /*A*/ "a" /*B*/ | /*C*/ 'b' /*D*/ = 'a';
477    a++;|]
478    a; x;
479}`);
480
481        testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_NoType", `
482function f() {
483    let a = 1;
484    [#|const x = 1;
485    a++;|]
486    a; x;
487}`);
488
489        testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_Type", `
490function f() {
491    let a = 1;
492    [#|const x: number = 1;
493    a++;|]
494    a; x;
495}`);
496
497        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed1", `
498function f() {
499    let a = 1;
500    [#|const x = 1;
501    let y = 2;
502    a++;|]
503    a; x; y;
504}`);
505
506        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed2", `
507function f() {
508    let a = 1;
509    [#|var x = 1;
510    let y = 2;
511    a++;|]
512    a; x; y;
513}`);
514
515        testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed3", `
516function f() {
517    let a = 1;
518    [#|let x: number = 1;
519    let y = 2;
520    a++;|]
521    a; x; y;
522}`);
523
524        testExtractFunction("extractFunction_VariableDeclaration_Writes_UnionUndefined", `
525function f() {
526    let a = 1;
527    [#|let x: number | undefined = 1;
528    let y: undefined | number = 2;
529    let z: (undefined | number) = 3;
530    a++;|]
531    a; x; y; z;
532}`);
533
534        testExtractFunction("extractFunction_VariableDeclaration_ShorthandProperty", `
535function f() {
536    [#|let x;|]
537    return { x };
538}`);
539
540        testExtractFunction("extractFunction_PreserveTrivia", `
541// a
542var q = /*b*/ //c
543    /*d*/ [#|1 /*e*/ //f
544    /*g*/ + /*h*/ //i
545    /*j*/ 2|] /*k*/ //l
546    /*m*/; /*n*/ //o`);
547
548        testExtractFunction("extractFunction_NamelessClass", `
549export default class {
550    M() {
551        [#|1 + 1|];
552    }
553}`);
554
555        testExtractFunction("extractFunction_NoDeclarations", `
556function F() {
557[#|arguments.length|]; // arguments has no declaration
558}`);
559    });
560
561    function testExtractFunction(caption: string, text: string, includeLib?: boolean) {
562        testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function, includeLib);
563    }
564}
565