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