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