From 0d5d5cdf28f6a5216759bcf85433d0779176eb10 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 14:13:48 -0700 Subject: [PATCH 1/2] For any Extract* baseline that is valid JS, produce a JS baseline --- src/harness/unittests/extractTestHelpers.ts | 73 +++++++++++-------- ...ractConstant_BlockScopes_NoDependencies.js | 14 ++++ .../extractConstant/extractConstant_Class.js | 10 +++ .../extractConstant_ClassInsertionPosition.js | 34 +++++++++ .../extractConstant_ExpressionStatement.js | 4 + ...tConstant_ExpressionStatementExpression.js | 4 + .../extractConstant_Function.js | 16 ++++ .../extractConstant/extractConstant_Method.js | 22 ++++++ .../extractConstant_Parameters.js | 12 +++ .../extractConstant_TopLevel.js | 6 ++ .../extractFunction/extractFunction20.js | 28 +++++++ .../extractFunction/extractFunction21.js | 26 +++++++ .../extractFunction/extractFunction22.js | 31 ++++++++ .../extractFunction/extractFunction24.js | 43 +++++++++++ .../extractFunction/extractFunction25.js | 26 +++++++ .../extractFunction/extractFunction26.js | 31 ++++++++ .../extractFunction/extractFunction27.js | 34 +++++++++ .../extractFunction/extractFunction28.js | 34 +++++++++ .../extractFunction/extractFunction33.js | 19 +++++ 19 files changed, 437 insertions(+), 30 deletions(-) create mode 100644 tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Class.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Function.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Method.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Parameters.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_TopLevel.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction20.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction21.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction22.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction24.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction25.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction26.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction27.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction28.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction33.js diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index e619f9343d891..d42b083958027 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -98,35 +98,48 @@ namespace ts { } export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage) { - it(caption, () => { - Harness.Baseline.runBaseline(`${baselineFolder}/${caption}.ts`, () => { - const t = extractTest(text); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${caption} does not specify selection range`); - } - const f = { - path: "/a.ts", - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); - const sourceFile = program.getSourceFile(f.path); - const context: RefactorContext = { - cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, - newLineCharacter, - program, - file: sourceFile, - startPosition: selectionRange.start, - endPosition: selectionRange.end, - rulesProvider: getRuleProvider() - }; - const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); - const infos = refactor.extractSymbol.getAvailableActions(context); - const actions = find(infos, info => info.description === description.message).actions; + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + + [Extension.Ts, Extension.Js].forEach(extension => + it(`${caption} [${extension}]`, () => runBaseline(extension))); + + function runBaseline(extension: Extension) { + const f = { + path: "/a" + extension, + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + + // Don't bother generating JS baselines for inputs that aren't valid JS. + const diags = program.getSyntacticDiagnostics(); + if (diags && diags.length) { + assert.equal(Extension.Js, extension); + return; + } + + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + const actions = find(infos, info => info.description === description.message).actions; + + Harness.Baseline.runBaseline(`${baselineFolder}/${caption}${extension}`, () => { const data: string[] = []; data.push(`// ==ORIGINAL==`); data.push(sourceFile.text); @@ -140,7 +153,7 @@ namespace ts { } return data.join(newLineCharacter); }); - }); + } } export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) { diff --git a/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js new file mode 100644 index 0000000000000..25609a3a8010a --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js @@ -0,0 +1,14 @@ +// ==ORIGINAL== +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = 1; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Class.js b/tests/baselines/reference/extractConstant/extractConstant_Class.js new file mode 100644 index 0000000000000..6b581141cd66f --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Class.js @@ -0,0 +1,10 @@ +// ==ORIGINAL== +class C { + x = 1; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js new file mode 100644 index 0000000000000..4e7bcc0521a89 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M3== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js new file mode 100644 index 0000000000000..6bf35cd17e125 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js new file mode 100644 index 0000000000000..6bf35cd17e125 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Function.js b/tests/baselines/reference/extractConstant/extractConstant_Function.js new file mode 100644 index 0000000000000..67c8255c4b45d --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Function.js @@ -0,0 +1,16 @@ +// ==ORIGINAL== +function F() { + let x = 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +function F() { + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Method.js b/tests/baselines/reference/extractConstant/extractConstant_Method.js new file mode 100644 index 0000000000000..bd7c7f8635976 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Method.js @@ -0,0 +1,22 @@ +// ==ORIGINAL== +class C { + M() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M== +class C { + M() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + M() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Parameters.js b/tests/baselines/reference/extractConstant/extractConstant_Parameters.js new file mode 100644 index 0000000000000..e6c9c474fbcb8 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Parameters.js @@ -0,0 +1,12 @@ +// ==ORIGINAL== +function F() { + let w = 1; + let x = w + 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + let w = 1; + const newLocal = w + 1; + + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js new file mode 100644 index 0000000000000..fb0447583ff8f --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js @@ -0,0 +1,6 @@ +// ==ORIGINAL== +let x = 1; +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +let x = /*RENAME*/newLocal; \ No newline at end of file diff --git a/tests/baselines/reference/extractFunction/extractFunction20.js b/tests/baselines/reference/extractFunction/extractFunction20.js new file mode 100644 index 0000000000000..455fee113fbbb --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction20.js @@ -0,0 +1,28 @@ +// ==ORIGINAL== +const _ = class { + a() { + let a1 = { x: 1 }; + return a1.x + 10; + } +} +// ==SCOPE::Extract to method in anonymous class expression== +const _ = class { + a() { + return this./*RENAME*/newMethod(); + } + + newMethod() { + let a1 = { x: 1 }; + return a1.x + 10; + } +} +// ==SCOPE::Extract to function in global scope== +const _ = class { + a() { + return /*RENAME*/newFunction(); + } +} +function newFunction() { + let a1 = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction21.js b/tests/baselines/reference/extractFunction/extractFunction21.js new file mode 100644 index 0000000000000..af7928a039fad --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction21.js @@ -0,0 +1,26 @@ +// ==ORIGINAL== +function foo() { + let x = 10; + x++; + return; +} +// ==SCOPE::Extract to inner function in function 'foo'== +function foo() { + let x = 10; + return /*RENAME*/newFunction(); + + function newFunction() { + x++; + return; + } +} +// ==SCOPE::Extract to function in global scope== +function foo() { + let x = 10; + x = /*RENAME*/newFunction(x); + return; +} +function newFunction(x) { + x++; + return x; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction22.js b/tests/baselines/reference/extractFunction/extractFunction22.js new file mode 100644 index 0000000000000..09fbc7a5b82d0 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction22.js @@ -0,0 +1,31 @@ +// ==ORIGINAL== +function test() { + try { + } + finally { + return 1; + } +} +// ==SCOPE::Extract to inner function in function 'test'== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } + + function newFunction() { + return 1; + } +} +// ==SCOPE::Extract to function in global scope== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction24.js b/tests/baselines/reference/extractFunction/extractFunction24.js new file mode 100644 index 0000000000000..7b80180c5d3cb --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction24.js @@ -0,0 +1,43 @@ +// ==ORIGINAL== +function Outer() { + function M1() { } + function M2() { + return 1; + } + function M3() { } +} +// ==SCOPE::Extract to inner function in function 'M2'== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + function M3() { } +} +// ==SCOPE::Extract to inner function in function 'Outer'== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + } + function newFunction() { + return 1; + } + + function M3() { } +} +// ==SCOPE::Extract to function in global scope== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + } + function M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction25.js b/tests/baselines/reference/extractFunction/extractFunction25.js new file mode 100644 index 0000000000000..3233c4043077d --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction25.js @@ -0,0 +1,26 @@ +// ==ORIGINAL== +function M1() { } +function M2() { + return 1; +} +function M3() { } +// ==SCOPE::Extract to inner function in function 'M2'== +function M1() { } +function M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } +} +function M3() { } +// ==SCOPE::Extract to function in global scope== +function M1() { } +function M2() { + return /*RENAME*/newFunction(); +} +function newFunction() { + return 1; +} + +function M3() { } \ No newline at end of file diff --git a/tests/baselines/reference/extractFunction/extractFunction26.js b/tests/baselines/reference/extractFunction/extractFunction26.js new file mode 100644 index 0000000000000..ccc4bacb47707 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction26.js @@ -0,0 +1,31 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + M3() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + newMethod() { + return 1; + } + + M3() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction27.js b/tests/baselines/reference/extractFunction/extractFunction27.js new file mode 100644 index 0000000000000..1f894d17badf1 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction27.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + constructor() { } + M3() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + constructor() { } + newMethod() { + return 1; + } + + M3() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + constructor() { } + M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction28.js b/tests/baselines/reference/extractFunction/extractFunction28.js new file mode 100644 index 0000000000000..d205edbd4bfeb --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction28.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + M3() { } + constructor() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + newMethod() { + return 1; + } + + M3() { } + constructor() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + M3() { } + constructor() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction33.js b/tests/baselines/reference/extractFunction/extractFunction33.js new file mode 100644 index 0000000000000..fd716646e6610 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction33.js @@ -0,0 +1,19 @@ +// ==ORIGINAL== +function F() { + function G() { } +} +// ==SCOPE::Extract to inner function in function 'F'== +function F() { + /*RENAME*/newFunction(); + + function newFunction() { + function G() { } + } +} +// ==SCOPE::Extract to function in global scope== +function F() { + /*RENAME*/newFunction(); +} +function newFunction() { + function G() { } +} From a73a553f589d46a39259ca64c991f847a6981d70 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 14:31:35 -0700 Subject: [PATCH 2/2] Assert that Extract* baselines are syntactically valid --- src/harness/unittests/extractTestHelpers.ts | 33 +++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index d42b083958027..ea1cccd32ea2e 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -108,23 +108,16 @@ namespace ts { it(`${caption} [${extension}]`, () => runBaseline(extension))); function runBaseline(extension: Extension) { - const f = { - path: "/a" + extension, - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const path = "/a" + extension; + const program = makeProgram({ path, content: t.source }); - // Don't bother generating JS baselines for inputs that aren't valid JS. - const diags = program.getSyntacticDiagnostics(); - if (diags && diags.length) { + if (hasSyntacticDiagnostics(program)) { + // Don't bother generating JS baselines for inputs that aren't valid JS. assert.equal(Extension.Js, extension); return; } - const sourceFile = program.getSourceFile(f.path); + const sourceFile = program.getSourceFile(path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, newLineCharacter, @@ -150,10 +143,26 @@ namespace ts { const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); data.push(newTextWithRename); + + const diagProgram = makeProgram({ path, content: newText }); + assert.isFalse(hasSyntacticDiagnostics(diagProgram)); } return data.join(newLineCharacter); }); } + + function makeProgram(f: {path: string, content: string }) { + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + return program; + } + + function hasSyntacticDiagnostics(program: Program) { + const diags = program.getSyntacticDiagnostics(); + return length(diags) > 0; + } } export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) {