Don't normalize whitespace in fourslash tests (#18447)

* Don't normalize whitespace in fourslash tests

* Only render whitespace when the diff is text-only
This commit is contained in:
Andy 2017-09-14 08:23:50 -07:00 committed by GitHub
parent cf53743bd6
commit d96dfeb708
16 changed files with 82 additions and 83 deletions

View File

@ -22,10 +22,6 @@
namespace FourSlash {
ts.disableIncrementalParsing = false;
function normalizeNewLines(s: string) {
return s.replace(/\r\n/g, "\n");
}
// Represents a parsed source file with metadata
export interface FourSlashFile {
// The contents of the file (with markers, etc stripped out)
@ -364,7 +360,7 @@ namespace FourSlash {
baseIndentSize: 0,
indentSize: 4,
tabSize: 4,
newLineCharacter: Harness.IO.newLine(),
newLineCharacter: "\n",
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
@ -1603,7 +1599,7 @@ namespace FourSlash {
}
}
public printCurrentFileState(makeWhitespaceVisible: boolean, makeCaretVisible: boolean) {
public printCurrentFileState(showWhitespace: boolean, makeCaretVisible: boolean) {
for (const file of this.testData.files) {
const active = (this.activeFile === file);
Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`);
@ -1611,8 +1607,8 @@ namespace FourSlash {
if (active) {
content = content.substr(0, this.currentCaretPosition) + (makeCaretVisible ? "|" : "") + content.substr(this.currentCaretPosition);
}
if (makeWhitespaceVisible) {
content = TestState.makeWhitespaceVisible(content);
if (showWhitespace) {
content = makeWhitespaceVisible(content);
}
Harness.IO.log(content);
}
@ -2128,10 +2124,8 @@ namespace FourSlash {
public verifyCurrentFileContent(text: string) {
const actual = this.getFileContent(this.activeFile.fileName);
if (normalizeNewLines(actual) !== normalizeNewLines(text)) {
throw new Error("verifyCurrentFileContent\n" +
"\tExpected: \"" + TestState.makeWhitespaceVisible(text) + "\"\n" +
"\t Actual: \"" + TestState.makeWhitespaceVisible(actual) + "\"");
if (actual !== text) {
throw new Error(`verifyCurrentFileContent failed:\n${showTextDiff(text, actual)}`);
}
}
@ -2305,11 +2299,11 @@ namespace FourSlash {
const actualText = this.rangeText(ranges[0]);
const result = includeWhiteSpace
? normalizeNewLines(actualText) === normalizeNewLines(expectedText)
? actualText === expectedText
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
if (!result) {
this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`);
this.raiseError(`Actual range text doesn't match expected text.\n${showTextDiff(expectedText, actualText)}`);
}
}
@ -2403,15 +2397,19 @@ namespace FourSlash {
const originalContent = scriptInfo.content;
for (const codeFix of codeFixes) {
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0])));
let text = this.rangeText(ranges[0]);
// TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests)
text = text.replace(/\r\n/g, "\n");
actualTextArray.push(text);
scriptInfo.updateContent(originalContent);
}
const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort();
const sortedExpectedArray = expectedTextArray.sort();
const sortedActualArray = actualTextArray.sort();
if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
this.raiseError(
`Actual text array doesn't match expected text array. \nActual: \n'${sortedActualArray.join("\n\n")}'\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
}
ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => {
if (expected !== actual) {
this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`);
}
});
}
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
@ -2431,7 +2429,7 @@ namespace FourSlash {
}
if (actual.newText !== expected.newText) {
this.raiseError(`${name} failed - expected insertion:\n"${this.clarifyNewlines(expected.newText)}"\nactual insertion:\n"${this.clarifyNewlines(actual.newText)}"`);
this.raiseError(`${name} failed for expected insertion.\n${showTextDiff(expected.newText, actual.newText)}`);
}
if (actual.caretOffset !== expected.caretOffset) {
@ -2440,17 +2438,6 @@ namespace FourSlash {
}
}
private clarifyNewlines(str: string) {
return str.replace(/\r?\n/g, lineEnding => {
const representation = lineEnding === "\r\n" ? "CRLF" : "LF";
return "# - " + representation + lineEnding;
});
}
private normalizeNewlines(str: string) {
return str.replace(/\r?\n/g, "\n");
}
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
const openBraceMap = ts.createMapFromTemplate<ts.CharacterCodes>({
@ -2878,8 +2865,8 @@ namespace FourSlash {
}
const actualContent = this.getFileContent(this.activeFile.fileName);
if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) {
this.raiseError(`verifyFileAfterApplyingRefactors failed: expected:\n${expectedContent}\nactual:\n${actualContent}`);
if (actualContent !== expectedContent) {
this.raiseError(`verifyFileAfterApplyingRefactors failed:\n${showTextDiff(expectedContent, actualContent)}`);
}
}
@ -3014,10 +3001,6 @@ namespace FourSlash {
}
}
private static makeWhitespaceVisible(text: string) {
return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ ");
}
public setCancelled(numberOfCalls: number): void {
this.cancellationToken.setCancelled(numberOfCalls);
}
@ -3319,12 +3302,7 @@ ${code}
let column = 1;
const flush = (lastSafeCharIndex: number) => {
if (lastSafeCharIndex === undefined) {
output = output + content.substr(lastNormalCharPosition);
}
else {
output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex - lastNormalCharPosition);
}
output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition);
};
if (content.length > 0) {
@ -3511,6 +3489,27 @@ ${code}
function toArray<T>(x: T | T[]): T[] {
return ts.isArray(x) ? x : [x];
}
function makeWhitespaceVisible(text: string) {
return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ ");
}
function showTextDiff(expected: string, actual: string): string {
// Only show whitespace if the difference is whitespace-only.
if (differOnlyByWhitespace(expected, actual)) {
expected = makeWhitespaceVisible(expected);
actual = makeWhitespaceVisible(actual);
}
return `Expected:\n${expected}\nActual:${actual}`;
}
function differOnlyByWhitespace(a: string, b: string) {
return stripWhitespace(a) === stripWhitespace(b);
}
function stripWhitespace(s: string): string {
return s.replace(/\s/g, "");
}
}
namespace FourSlashInterface {
@ -4143,15 +4142,15 @@ namespace FourSlashInterface {
}
public printCurrentFileState() {
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ true);
this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ true);
}
public printCurrentFileStateWithWhitespace() {
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ true, /*makeCaretVisible*/ true);
this.state.printCurrentFileState(/*showWhitespace*/ true, /*makeCaretVisible*/ true);
}
public printCurrentFileStateWithoutCaret() {
this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ false);
this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ false);
}
public printCurrentQuickInfo() {

View File

@ -500,7 +500,8 @@ namespace Harness {
export let IO: IO;
// harness always uses one kind of new line
const harnessNewLine = "\r\n";
// But note that `parseTestData` in `fourslash.ts` uses "\n"
export const harnessNewLine = "\r\n";
// Root for file paths that are stored in a virtual file system
export const virtualFileSystemRoot = "/";

View File

@ -130,7 +130,7 @@ namespace Harness.LanguageService {
}
public getNewLine(): string {
return "\r\n";
return harnessNewLine;
}
public getFilenames(): string[] {

View File

@ -573,7 +573,7 @@ namespace ts.server {
getEditsForRefactor(
fileName: string,
_formatOptions: FormatCodeSettings,
formatOptions: FormatCodeSettings,
positionOrRange: number | TextRange,
refactorName: string,
actionName: string): RefactorEditInfo {
@ -581,6 +581,7 @@ namespace ts.server {
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetEditsForRefactorRequestArgs;
args.refactor = refactorName;
args.action = actionName;
args.formatOptions = formatOptions;
const request = this.processRequest<protocol.GetEditsForRefactorRequest>(CommandNames.GetEditsForRefactor, args);
const response = this.processResponse<protocol.GetEditsForRefactorResponse>(request);

View File

@ -494,6 +494,7 @@ namespace ts.server.protocol {
refactor: string;
/* The 'name' property from the refactoring action */
action: string;
formatOptions: FormatCodeSettings,
};

View File

@ -1488,7 +1488,7 @@ namespace ts.server {
const result = project.getLanguageService().getEditsForRefactor(
file,
this.projectService.getFormatCodeOptions(),
convertFormatOptions(args.formatOptions),
position || textRange,
args.refactor,
args.action

View File

@ -2019,7 +2019,7 @@ namespace ts {
startPosition,
endPosition,
program: getProgram(),
newLineCharacter: host.getNewLine(),
newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(),
rulesProvider: getRuleProvider(formatOptions),
cancellationToken
};

View File

@ -184,16 +184,12 @@ namespace ts.textChanges {
return s;
}
function getNewlineKind(context: { newLineCharacter: string }) {
return context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
}
export class ChangeTracker {
private changes: Change[] = [];
private readonly newLineCharacter: string;
public static fromContext(context: RefactorContext | CodeFixContext) {
return new ChangeTracker(getNewlineKind(context), context.rulesProvider);
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider);
}
constructor(

View File

@ -17,5 +17,4 @@ verify.currentFileContentIs(`class C {
()=>{ this.foo === 10 };
}
}
C.foo = undefined;
`);
C.foo = undefined;` + "\r\n"); // TODO: GH#18445

View File

@ -13,5 +13,4 @@ verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2)
verify.currentFileContentIs(`class C {
static p = ()=>{ this.foo === 10 };
}
C.foo = undefined;
`);
C.foo = undefined;` + "\r\n"); // TODO: GH#18445

View File

@ -9,7 +9,8 @@
//// super();
//// |]}
////}
// TODO: GH#18445
verify.rangeAfterCodeFix(`
super();
super();\r
this.a = 12;
`, /*includeWhiteSpace*/ true);
`, /*includeWhiteSpace*/ true);

View File

@ -6,6 +6,7 @@
//// constructor() {[|
//// |]}
////}
// TODO: GH#18445
verify.rangeAfterCodeFix(`
super();
super();\r
`, /*includeWhitespace*/ true);

View File

@ -19,7 +19,7 @@ edit.applyRefactor({
`function foo() {
var i = 10;
var __return: any;
({ __return, i } = n/*RENAME*/ewFunction(i));
({ __return, i } = /*RENAME*/newFunction(i));
return __return;
}
function newFunction(i) {

View File

@ -11,12 +11,12 @@
////}
format.document();
verify.currentFileContentIs("class C {\r\n\
<<<<<<< HEAD\r\n\
v = 1;\r\n\
||||||| merged common ancestors\r\n\
v = 3;\r\n\
=======\r\n\
v = 2;\r\n\
>>>>>>> Branch - a\r\n\
}");
verify.currentFileContentIs(`class C {
<<<<<<< HEAD
v = 1;
||||||| merged common ancestors
v = 3;
=======
v = 2;
>>>>>>> Branch - a
}`);

View File

@ -9,10 +9,10 @@
////}
format.document();
verify.currentFileContentIs("class C {\r\n\
<<<<<<< HEAD\r\n\
v = 1;\r\n\
=======\r\n\
v = 2;\r\n\
>>>>>>> Branch - a\r\n\
}");
verify.currentFileContentIs(`class C {
<<<<<<< HEAD
v = 1;
=======
v = 2;
>>>>>>> Branch - a
}`);

View File

@ -10,7 +10,8 @@
////x;|]
goTo.file("/b.ts");
verify.rangeAfterCodeFix(`import { x } from "./a";
// TODO:GH#18445
verify.rangeAfterCodeFix(`import { x } from "./a";\r
\r
export { x } from "./a";
x;`, /*includeWhiteSpace*/ true);