Skip to content

Commit 8e4c559

Browse files
authored
Merge pull request #18829 from amcasey/JsExtractTests
Improve Extract* unit test coverage and correctness
2 parents 7959bd0 + a73a553 commit 8e4c559

19 files changed

+446
-30
lines changed

src/harness/unittests/extractTestHelpers.ts

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,35 +98,41 @@ namespace ts {
9898
}
9999

100100
export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage) {
101-
it(caption, () => {
102-
Harness.Baseline.runBaseline(`${baselineFolder}/${caption}.ts`, () => {
103-
const t = extractTest(text);
104-
const selectionRange = t.ranges.get("selection");
105-
if (!selectionRange) {
106-
throw new Error(`Test ${caption} does not specify selection range`);
107-
}
108-
const f = {
109-
path: "/a.ts",
110-
content: t.source
111-
};
112-
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
113-
const projectService = projectSystem.createProjectService(host);
114-
projectService.openClientFile(f.path);
115-
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
116-
const sourceFile = program.getSourceFile(f.path);
117-
const context: RefactorContext = {
118-
cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
119-
newLineCharacter,
120-
program,
121-
file: sourceFile,
122-
startPosition: selectionRange.start,
123-
endPosition: selectionRange.end,
124-
rulesProvider: getRuleProvider()
125-
};
126-
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
127-
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
128-
const infos = refactor.extractSymbol.getAvailableActions(context);
129-
const actions = find(infos, info => info.description === description.message).actions;
101+
const t = extractTest(text);
102+
const selectionRange = t.ranges.get("selection");
103+
if (!selectionRange) {
104+
throw new Error(`Test ${caption} does not specify selection range`);
105+
}
106+
107+
[Extension.Ts, Extension.Js].forEach(extension =>
108+
it(`${caption} [${extension}]`, () => runBaseline(extension)));
109+
110+
function runBaseline(extension: Extension) {
111+
const path = "/a" + extension;
112+
const program = makeProgram({ path, content: t.source });
113+
114+
if (hasSyntacticDiagnostics(program)) {
115+
// Don't bother generating JS baselines for inputs that aren't valid JS.
116+
assert.equal(Extension.Js, extension);
117+
return;
118+
}
119+
120+
const sourceFile = program.getSourceFile(path);
121+
const context: RefactorContext = {
122+
cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
123+
newLineCharacter,
124+
program,
125+
file: sourceFile,
126+
startPosition: selectionRange.start,
127+
endPosition: selectionRange.end,
128+
rulesProvider: getRuleProvider()
129+
};
130+
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
131+
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
132+
const infos = refactor.extractSymbol.getAvailableActions(context);
133+
const actions = find(infos, info => info.description === description.message).actions;
134+
135+
Harness.Baseline.runBaseline(`${baselineFolder}/${caption}${extension}`, () => {
130136
const data: string[] = [];
131137
data.push(`// ==ORIGINAL==`);
132138
data.push(sourceFile.text);
@@ -137,10 +143,26 @@ namespace ts {
137143
const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges);
138144
const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation);
139145
data.push(newTextWithRename);
146+
147+
const diagProgram = makeProgram({ path, content: newText });
148+
assert.isFalse(hasSyntacticDiagnostics(diagProgram));
140149
}
141150
return data.join(newLineCharacter);
142151
});
143-
});
152+
}
153+
154+
function makeProgram(f: {path: string, content: string }) {
155+
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
156+
const projectService = projectSystem.createProjectService(host);
157+
projectService.openClientFile(f.path);
158+
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
159+
return program;
160+
}
161+
162+
function hasSyntacticDiagnostics(program: Program) {
163+
const diags = program.getSyntacticDiagnostics();
164+
return length(diags) > 0;
165+
}
144166
}
145167

146168
export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// ==ORIGINAL==
2+
for (let i = 0; i < 10; i++) {
3+
for (let j = 0; j < 10; j++) {
4+
let x = 1;
5+
}
6+
}
7+
// ==SCOPE::Extract to constant in global scope==
8+
const newLocal = 1;
9+
10+
for (let i = 0; i < 10; i++) {
11+
for (let j = 0; j < 10; j++) {
12+
let x = /*RENAME*/newLocal;
13+
}
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// ==ORIGINAL==
2+
class C {
3+
x = 1;
4+
}
5+
// ==SCOPE::Extract to constant in global scope==
6+
const newLocal = 1;
7+
8+
class C {
9+
x = /*RENAME*/newLocal;
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// ==ORIGINAL==
2+
class C {
3+
a = 1;
4+
b = 2;
5+
M1() { }
6+
M2() { }
7+
M3() {
8+
let x = 1;
9+
}
10+
}
11+
// ==SCOPE::Extract to constant in method 'M3==
12+
class C {
13+
a = 1;
14+
b = 2;
15+
M1() { }
16+
M2() { }
17+
M3() {
18+
const newLocal = 1;
19+
20+
let x = /*RENAME*/newLocal;
21+
}
22+
}
23+
// ==SCOPE::Extract to constant in global scope==
24+
const newLocal = 1;
25+
26+
class C {
27+
a = 1;
28+
b = 2;
29+
M1() { }
30+
M2() { }
31+
M3() {
32+
let x = /*RENAME*/newLocal;
33+
}
34+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// ==ORIGINAL==
2+
"hello";
3+
// ==SCOPE::Extract to constant in global scope==
4+
const /*RENAME*/newLocal = "hello";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// ==ORIGINAL==
2+
"hello";
3+
// ==SCOPE::Extract to constant in global scope==
4+
const /*RENAME*/newLocal = "hello";
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// ==ORIGINAL==
2+
function F() {
3+
let x = 1;
4+
}
5+
// ==SCOPE::Extract to constant in function 'F'==
6+
function F() {
7+
const newLocal = 1;
8+
9+
let x = /*RENAME*/newLocal;
10+
}
11+
// ==SCOPE::Extract to constant in global scope==
12+
const newLocal = 1;
13+
14+
function F() {
15+
let x = /*RENAME*/newLocal;
16+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// ==ORIGINAL==
2+
class C {
3+
M() {
4+
let x = 1;
5+
}
6+
}
7+
// ==SCOPE::Extract to constant in method 'M==
8+
class C {
9+
M() {
10+
const newLocal = 1;
11+
12+
let x = /*RENAME*/newLocal;
13+
}
14+
}
15+
// ==SCOPE::Extract to constant in global scope==
16+
const newLocal = 1;
17+
18+
class C {
19+
M() {
20+
let x = /*RENAME*/newLocal;
21+
}
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// ==ORIGINAL==
2+
function F() {
3+
let w = 1;
4+
let x = w + 1;
5+
}
6+
// ==SCOPE::Extract to constant in function 'F'==
7+
function F() {
8+
let w = 1;
9+
const newLocal = w + 1;
10+
11+
let x = /*RENAME*/newLocal;
12+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ==ORIGINAL==
2+
let x = 1;
3+
// ==SCOPE::Extract to constant in global scope==
4+
const newLocal = 1;
5+
6+
let x = /*RENAME*/newLocal;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// ==ORIGINAL==
2+
const _ = class {
3+
a() {
4+
let a1 = { x: 1 };
5+
return a1.x + 10;
6+
}
7+
}
8+
// ==SCOPE::Extract to method in anonymous class expression==
9+
const _ = class {
10+
a() {
11+
return this./*RENAME*/newMethod();
12+
}
13+
14+
newMethod() {
15+
let a1 = { x: 1 };
16+
return a1.x + 10;
17+
}
18+
}
19+
// ==SCOPE::Extract to function in global scope==
20+
const _ = class {
21+
a() {
22+
return /*RENAME*/newFunction();
23+
}
24+
}
25+
function newFunction() {
26+
let a1 = { x: 1 };
27+
return a1.x + 10;
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ==ORIGINAL==
2+
function foo() {
3+
let x = 10;
4+
x++;
5+
return;
6+
}
7+
// ==SCOPE::Extract to inner function in function 'foo'==
8+
function foo() {
9+
let x = 10;
10+
return /*RENAME*/newFunction();
11+
12+
function newFunction() {
13+
x++;
14+
return;
15+
}
16+
}
17+
// ==SCOPE::Extract to function in global scope==
18+
function foo() {
19+
let x = 10;
20+
x = /*RENAME*/newFunction(x);
21+
return;
22+
}
23+
function newFunction(x) {
24+
x++;
25+
return x;
26+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// ==ORIGINAL==
2+
function test() {
3+
try {
4+
}
5+
finally {
6+
return 1;
7+
}
8+
}
9+
// ==SCOPE::Extract to inner function in function 'test'==
10+
function test() {
11+
try {
12+
}
13+
finally {
14+
return /*RENAME*/newFunction();
15+
}
16+
17+
function newFunction() {
18+
return 1;
19+
}
20+
}
21+
// ==SCOPE::Extract to function in global scope==
22+
function test() {
23+
try {
24+
}
25+
finally {
26+
return /*RENAME*/newFunction();
27+
}
28+
}
29+
function newFunction() {
30+
return 1;
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// ==ORIGINAL==
2+
function Outer() {
3+
function M1() { }
4+
function M2() {
5+
return 1;
6+
}
7+
function M3() { }
8+
}
9+
// ==SCOPE::Extract to inner function in function 'M2'==
10+
function Outer() {
11+
function M1() { }
12+
function M2() {
13+
return /*RENAME*/newFunction();
14+
15+
function newFunction() {
16+
return 1;
17+
}
18+
}
19+
function M3() { }
20+
}
21+
// ==SCOPE::Extract to inner function in function 'Outer'==
22+
function Outer() {
23+
function M1() { }
24+
function M2() {
25+
return /*RENAME*/newFunction();
26+
}
27+
function newFunction() {
28+
return 1;
29+
}
30+
31+
function M3() { }
32+
}
33+
// ==SCOPE::Extract to function in global scope==
34+
function Outer() {
35+
function M1() { }
36+
function M2() {
37+
return /*RENAME*/newFunction();
38+
}
39+
function M3() { }
40+
}
41+
function newFunction() {
42+
return 1;
43+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ==ORIGINAL==
2+
function M1() { }
3+
function M2() {
4+
return 1;
5+
}
6+
function M3() { }
7+
// ==SCOPE::Extract to inner function in function 'M2'==
8+
function M1() { }
9+
function M2() {
10+
return /*RENAME*/newFunction();
11+
12+
function newFunction() {
13+
return 1;
14+
}
15+
}
16+
function M3() { }
17+
// ==SCOPE::Extract to function in global scope==
18+
function M1() { }
19+
function M2() {
20+
return /*RENAME*/newFunction();
21+
}
22+
function newFunction() {
23+
return 1;
24+
}
25+
26+
function M3() { }

0 commit comments

Comments
 (0)