Improve testing of code fixes, and improve diagnostic messages (#18742)

* Improve testing of code fixes, and improve diagnostic messages

* Disambiguate `newFileContent` from `newRangeContent`
This commit is contained in:
Andy
2017-09-26 15:16:29 -07:00
committed by GitHub
parent a6555921f4
commit ecef2dc970
15 changed files with 231 additions and 75 deletions
+4
View File
@@ -3689,6 +3689,10 @@
"category": "Message",
"code": 90026
},
"Declare static property '{0}'.": {
"category": "Message",
"code": 90027
},
"Convert function to an ES2015 class": {
"category": "Message",
+46
View File
@@ -2337,6 +2337,39 @@ namespace FourSlash {
}
}
public verifyCodeFix(options: FourSlashInterface.VerifyCodeFixOptions) {
const fileName = this.activeFile.fileName;
const actions = this.getCodeFixActions(fileName, options.errorCode);
let index = options.index;
if (index === undefined) {
if (!(actions && actions.length === 1)) {
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : ""}`);
}
index = 0;
}
else {
if (!(actions && actions.length >= index + 1)) {
this.raiseError(`Should find at least ${index + 1} codefix(es), but ${actions ? actions.length : "none"} found.`);
}
}
const action = actions[index];
assert.equal(action.description, options.description);
for (const change of action.changes) {
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
}
if (options.newFileContent) {
assert(!options.newRangeContent);
this.verifyCurrentFileContent(options.newFileContent);
}
else {
this.verifyRangeIs(options.newRangeContent, /*includeWhitespace*/ true);
}
}
/**
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
* @param fileName Path to file where error should be retrieved from.
@@ -3716,6 +3749,10 @@ namespace FourSlashInterface {
this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges);
}
public codeFix(options: FourSlashInterface.VerifyCodeFixOptions) {
this.state.verifyCodeFix(options);
}
public codeFixAvailable() {
this.state.verifyCodeFixAvailable(this.negative);
}
@@ -4359,4 +4396,13 @@ namespace FourSlashInterface {
export interface CompletionsAtOptions {
isNewIdentifierLocation?: boolean;
}
export interface VerifyCodeFixOptions {
description: string;
// One of these should be defined.
newFileContent?: string;
newRangeContent?: string;
errorCode?: number;
index?: number;
}
}
@@ -156,8 +156,9 @@ namespace ts.codefix {
const propertyChangeTracker = textChanges.ChangeTracker.fromContext(context);
propertyChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, property, { suffix: context.newLineCharacter });
(actions || (actions = [])).push({
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Declare_property_0), [tokenName]),
const diag = makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0;
actions = append(actions, {
description: formatStringFromArgs(getLocaleSpecificMessage(diag), [tokenName]),
changes: propertyChangeTracker.getChanges()
});
@@ -197,11 +198,9 @@ namespace ts.codefix {
const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromContext(context);
methodDeclarationChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, methodDeclaration, { suffix: context.newLineCharacter });
const diag = makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0;
return {
description: formatStringFromArgs(getLocaleSpecificMessage(makeStatic ?
Diagnostics.Declare_method_0 :
Diagnostics.Declare_static_method_0),
[tokenName]),
description: formatStringFromArgs(getLocaleSpecificMessage(diag), [tokenName]),
changes: methodDeclarationChangeTracker.getChanges()
};
}
@@ -7,6 +7,9 @@
//// |]}
////}
verify.rangeAfterCodeFix(`
verify.codeFix({
description: "Add 'this.' to unresolved variable.",
newRangeContent: `
this.foo = 10;
`, /*includeWhitespace*/ true);
`
});
@@ -6,4 +6,7 @@
//// bar() { [|foo = 10|] };
////}
verify.rangeAfterCodeFix("this.foo = 10");
verify.codeFix({
description: "Add 'this.' to unresolved variable.",
newRangeContent: "this.foo = 10",
});
@@ -1,14 +1,19 @@
/// <reference path='fourslash.ts' />
////[|class C {
////class C {
//// method() {
//// this.foo = 10;
//// }
////}|]
////}
verify.rangeAfterCodeFix(`class C {
foo: number;
verify.codeFix({
description: "Declare property 'foo'.",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
foo: number;\r
method() {
this.foo = 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
}`
});
@@ -1,14 +1,19 @@
/// <reference path='fourslash.ts' />
////[|class C {
////class C {
//// method() {
//// this.foo = 10;
//// }
////}|]
////}
verify.rangeAfterCodeFix(`class C {
[x:string]: number;
verify.codeFix({
description: "Add index signature for property 'foo'.",
index: 1,
// TODO: GH#18445
newFileContent: `class C {
[x: string]: number;\r
method() {
this.foo = 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 1);
}`
});
@@ -1,14 +1,19 @@
/// <reference path='fourslash.ts' />
////[|class C {
////class C {
//// static method() {
//// this.foo = 10;
//// }
////}|]
////}
verify.rangeAfterCodeFix(`class C {
static foo: number;
verify.codeFix({
description: "Declare static property 'foo'.",
index: 0,
// TODO: GH#18445
newFileContent: `class C {
static foo: number;\r
static method() {
this.foo = 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
}`
});
@@ -4,19 +4,25 @@
// @allowJs: true
// @Filename: a.js
////[|class C {
////class C {
//// constructor() {
//// }
//// method() {
//// this.foo === 10;
//// }
////}|]
////}
verify.rangeAfterCodeFix(`class C {
verify.codeFix({
description: "Initialize property 'foo' in the constructor.",
index: 0,
// TODO: GH#18741 and GH#18445
newFileContent: `class C {
constructor() {
this.foo = undefined;
\r
this.foo = undefined;\r
}
method() {
this.foo === 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
}`
});
@@ -4,17 +4,20 @@
// @allowJs: true
// @Filename: a.js
////[|class C {
////class C {
//// static method() {
//// ()=>{ this.foo === 10 };
//// }
////}
////|]
verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 0);
verify.currentFileContentIs(`class C {
verify.codeFix({
description: "Initialize static property 'foo'.",
index: 0,
// TODO: GH#18743 and GH#18445
newFileContent: `class C {
static method() {
()=>{ this.foo === 10 };
}
}
C.foo = undefined;` + "\r\n"); // TODO: GH#18445
}C.foo = undefined;\r
`
});
@@ -4,15 +4,21 @@
// @allowJs: true
// @Filename: a.js
////[|class C {
////class C {
//// constructor() {
//// }
//// prop = ()=>{ this.foo === 10 };
////}|]
////}
verify.rangeAfterCodeFix(`class C {
verify.codeFix({
description: "Initialize property 'foo' in the constructor.",
index: 0,
// TODO: GH#18741 and GH#18445
newFileContent: `class C {
constructor() {
this.foo = undefined;
}
\r
this.foo = undefined;\r
}
prop = ()=>{ this.foo === 10 };
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
}`
});
@@ -4,13 +4,16 @@
// @allowJs: true
// @Filename: a.js
////[|class C {
////class C {
//// static p = ()=>{ this.foo === 10 };
////}
////|]
verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2)
verify.currentFileContentIs(`class C {
verify.codeFix({
description: "Initialize static property 'foo'.",
index: 2,
// TODO: GH#18743 and GH#18445
newFileContent: `class C {
static p = ()=>{ this.foo === 10 };
}
C.foo = undefined;` + "\r\n"); // TODO: GH#18445
}C.foo = undefined;\r
`
});
@@ -9,18 +9,55 @@
//// }
//// }
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.codeFix({
description: "Declare static method 'm1'.",
index: 0,
// TODO: GH#18445
newRangeContent: `
static m1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`,
});
verify.rangeIs(`
static prop2: string;
static prop1: number;
static m2(arg0: any, arg1: any): any {
throw new Error("Method not implemented.");
}
static m1(arg0: any, arg1: any, arg2: any): any {
throw new Error("Method not implemented.");
}
`);
verify.codeFix({
description: "Declare static method 'm2'.",
index: 0,
newRangeContent: `
static m2(arg0: any, arg1: any): any {\r
throw new Error("Method not implemented.");\r
}\r
static m1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`,
});
verify.codeFix({
description: "Declare static property 'prop1'.",
index: 0,
newRangeContent: `
static prop1: number;\r
static m2(arg0: any, arg1: any): any {\r
throw new Error("Method not implemented.");\r
}\r
static m1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`,
});
verify.codeFix({
description: "Declare static property 'prop2'.",
index: 0,
newRangeContent: `
static prop2: string;\r
static prop1: number;\r
static m2(arg0: any, arg1: any): any {\r
throw new Error("Method not implemented.");\r
}\r
static m1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`,
});
@@ -10,18 +10,42 @@
//// }
//// }
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.codeFix({
description: "Declare method 'foo1'.",
index: 0,
// TODO: GH#18445
newRangeContent: `
foo1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`,
});
verify.rangeIs(`
foo3<T0, T1, T2, T3, T4, T5, T6, T7>(): any {
throw new Error("Method not implemented.");
}
foo2<T, U, V, W, X, Y, Z>(): any {
throw new Error("Method not implemented.");
}
foo1(arg0: any, arg1: any, arg2: any): any {
throw new Error("Method not implemented.");
}
`);
verify.codeFix({
description: "Declare method 'foo2'.",
index: 0,
newRangeContent: `
foo2<T, U, V, W, X, Y, Z>(): any {\r
throw new Error("Method not implemented.");\r
}\r
foo1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`
});
verify.codeFix({
description: "Declare method 'foo3'.",
index: 0,
newRangeContent:`
foo3<T0, T1, T2, T3, T4, T5, T6, T7>(): any {\r
throw new Error("Method not implemented.");\r
}\r
foo2<T, U, V, W, X, Y, Z>(): any {\r
throw new Error("Method not implemented.");\r
}\r
foo1(arg0: any, arg1: any, arg2: any): any {\r
throw new Error("Method not implemented.");\r
}\r
`
});
+7
View File
@@ -155,6 +155,13 @@ declare namespace FourSlashInterface {
implementationListIsEmpty(): void;
isValidBraceCompletionAtPosition(openingBrace?: string): void;
isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void;
codeFix(options: {
description: string,
newFileContent?: string,
newRangeContent?: string,
errorCode?: number,
index?: number,
});
codeFixAvailable(): void;
applicableRefactorAvailableAtMarker(markerName: string): void;
codeFixDiagnosticsAvailableAtMarkers(markerNames: string[], diagnosticCode?: number): void;