mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Add more tests that verify semantic diagnostics cache
This commit is contained in:
parent
7c92d09e23
commit
f97a2b3f90
@ -1319,92 +1319,6 @@ export default test;`;
|
||||
}
|
||||
});
|
||||
|
||||
it("updates errors when deep import file changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `import {B} from './b';
|
||||
declare var console: any;
|
||||
let b = new B();
|
||||
console.log(b.c.d);`
|
||||
};
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
c = new C();
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d = 1;
|
||||
}`
|
||||
};
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
content: `{}`
|
||||
};
|
||||
const files = [aFile, bFile, cFile, config, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`);
|
||||
host.writeFile(cFile.path, cFile.content.replace("d", "d2"));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C")
|
||||
]);
|
||||
// File a need not be rewritten
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs);
|
||||
});
|
||||
|
||||
it("updates errors when deep import through declaration file changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `import {B} from './b';
|
||||
declare var console: any;
|
||||
let b = new B();
|
||||
console.log(b.c.d);`
|
||||
};
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.d.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
c: C;
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.d.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d: number;
|
||||
}`
|
||||
};
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
content: `{}`
|
||||
};
|
||||
const files = [aFile, bFile, cFile, config, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`);
|
||||
host.writeFile(cFile.path, cFile.content.replace("d", "d2"));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C")
|
||||
]);
|
||||
// File a need not be rewritten
|
||||
assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs);
|
||||
});
|
||||
|
||||
it("updates errors when strictNullChecks changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
@ -1477,98 +1391,6 @@ foo().hello`
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
});
|
||||
|
||||
describe("updates errors when file transitively exported file changes", () => {
|
||||
const projectLocation = "/user/username/projects/myproject";
|
||||
const config: File = {
|
||||
path: `${projectLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
files: ["app.ts"],
|
||||
compilerOptions: { baseUrl: "." }
|
||||
})
|
||||
};
|
||||
const app: File = {
|
||||
path: `${projectLocation}/app.ts`,
|
||||
content: `import { Data } from "lib2/public";
|
||||
export class App {
|
||||
public constructor() {
|
||||
new Data().test();
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib2Public: File = {
|
||||
path: `${projectLocation}/lib2/public.ts`,
|
||||
content: `export * from "./data";`
|
||||
};
|
||||
const lib2Data: File = {
|
||||
path: `${projectLocation}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public";
|
||||
export class Data {
|
||||
public test() {
|
||||
const result: ITest = {
|
||||
title: "title"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib1Public: File = {
|
||||
path: `${projectLocation}/lib1/public.ts`,
|
||||
content: `export * from "./tools/public";`
|
||||
};
|
||||
const lib1ToolsPublic: File = {
|
||||
path: `${projectLocation}/lib1/tools/public.ts`,
|
||||
content: `export * from "./tools.interface";`
|
||||
};
|
||||
const lib1ToolsInterface: File = {
|
||||
path: `${projectLocation}/lib1/tools/tools.interface.ts`,
|
||||
content: `export interface ITest {
|
||||
title: string;
|
||||
}`
|
||||
};
|
||||
|
||||
function verifyTransitiveExports(filesWithoutConfig: ReadonlyArray<File>) {
|
||||
const files = [config, ...filesWithoutConfig];
|
||||
const host = createWatchedSystem(files, { currentDirectory: projectLocation });
|
||||
const watch = createWatchOfConfigFile(config.path, host);
|
||||
checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path));
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
host.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2"));
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path));
|
||||
checkOutputErrorsIncremental(host, [
|
||||
"lib2/data.ts(5,13): error TS2322: Type '{ title: string; }' is not assignable to type 'ITest'.\n Object literal may only specify known properties, but 'title' does not exist in type 'ITest'. Did you mean to write 'title2'?\n"
|
||||
]);
|
||||
|
||||
}
|
||||
it("when there are no circular import and exports", () => {
|
||||
verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib1Public, lib1ToolsPublic, lib1ToolsInterface]);
|
||||
});
|
||||
|
||||
it("when there are circular import and exports", () => {
|
||||
const lib2Data: File = {
|
||||
path: `${projectLocation}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
|
||||
export class Data {
|
||||
public dat?: Data2; public test() {
|
||||
const result: ITest = {
|
||||
title: "title"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib2Data2: File = {
|
||||
path: `${projectLocation}/lib2/data2.ts`,
|
||||
content: `import { Data } from "./data";
|
||||
export class Data2 {
|
||||
public dat?: Data;
|
||||
}`
|
||||
};
|
||||
verifyTransitiveExports([libFile, app, lib2Public, lib2Data, lib2Data2, lib1Public, lib1ToolsPublic, lib1ToolsInterface]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updates errors in lib file", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const field = "fullscreen";
|
||||
@ -1711,6 +1533,11 @@ interface Document {
|
||||
});
|
||||
|
||||
describe("Emit times and Error updates in builder after program changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
content: `{}`
|
||||
};
|
||||
function getOutputFileStampAndError(host: WatchedSystem, watch: Watch, file: File) {
|
||||
const builderProgram = watch.getBuilderProgram();
|
||||
const state = builderProgram.getState();
|
||||
@ -1757,8 +1584,108 @@ interface Document {
|
||||
}
|
||||
}
|
||||
|
||||
interface VerifyEmitAndErrorUpdates {
|
||||
change: (host: WatchedSystem) => void;
|
||||
getInitialErrors: (watch: Watch) => ReadonlyArray<Diagnostic> | ReadonlyArray<string>;
|
||||
getIncrementalErrors: (watch: Watch) => ReadonlyArray<Diagnostic> | ReadonlyArray<string>;
|
||||
filesWithNewEmit: ReadonlyArray<File>;
|
||||
filesWithOnlyErrorRefresh: ReadonlyArray<File>;
|
||||
filesNotTouched: ReadonlyArray<File>;
|
||||
configFile?: File;
|
||||
}
|
||||
|
||||
function verifyEmitAndErrorUpdates({ filesWithNewEmit, filesWithOnlyErrorRefresh, filesNotTouched, configFile = config, change, getInitialErrors, getIncrementalErrors }: VerifyEmitAndErrorUpdates) {
|
||||
const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched];
|
||||
const files = [...nonLibFiles, configFile, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]);
|
||||
checkOutputErrorsInitial(host, getInitialErrors(watch));
|
||||
const beforeChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles);
|
||||
change(host);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, getIncrementalErrors(watch));
|
||||
const afterChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles);
|
||||
filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange));
|
||||
filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange));
|
||||
filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ false, beforeChange, afterChange));
|
||||
}
|
||||
|
||||
describe("deep import changes", () => {
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `import {B} from './b';
|
||||
declare var console: any;
|
||||
let b = new B();
|
||||
console.log(b.c.d);`
|
||||
};
|
||||
|
||||
function verifyDeepImportChange(bFile: File, cFile: File) {
|
||||
const filesWithNewEmit: File[] = [];
|
||||
const filesWithOnlyErrorRefresh = [aFile];
|
||||
addImportedModule(bFile);
|
||||
addImportedModule(cFile);
|
||||
verifyEmitAndErrorUpdates({
|
||||
filesWithNewEmit,
|
||||
filesWithOnlyErrorRefresh,
|
||||
filesNotTouched: emptyArray,
|
||||
change: host => host.writeFile(cFile.path, cFile.content.replace("d", "d2")),
|
||||
getInitialErrors: () => emptyArray,
|
||||
getIncrementalErrors: watch => [
|
||||
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.lastIndexOf("d"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "d", "C")
|
||||
]
|
||||
});
|
||||
|
||||
function addImportedModule(file: File) {
|
||||
if (file.path.endsWith(".d.ts")) {
|
||||
filesWithOnlyErrorRefresh.push(file);
|
||||
}
|
||||
else {
|
||||
filesWithNewEmit.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("updates errors when deep import file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
c = new C();
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d = 1;
|
||||
}`
|
||||
};
|
||||
verifyDeepImportChange(bFile, cFile);
|
||||
});
|
||||
|
||||
it("updates errors when deep import through declaration file changes", () => {
|
||||
const bFile: File = {
|
||||
path: `${currentDirectory}/b.d.ts`,
|
||||
content: `import {C} from './c';
|
||||
export class B
|
||||
{
|
||||
c: C;
|
||||
}`
|
||||
};
|
||||
const cFile: File = {
|
||||
path: `${currentDirectory}/c.d.ts`,
|
||||
content: `export class C
|
||||
{
|
||||
d: number;
|
||||
}`
|
||||
};
|
||||
verifyDeepImportChange(bFile, cFile);
|
||||
});
|
||||
});
|
||||
|
||||
it("updates errors in file not exporting a deep multilevel import that changes", () => {
|
||||
const currentDirectory = "/user/username/projects/myproject";
|
||||
const aFile: File = {
|
||||
path: `${currentDirectory}/a.ts`,
|
||||
content: `export interface Point {
|
||||
@ -1798,34 +1725,116 @@ getPoint().c.x;`
|
||||
path: `${currentDirectory}/e.ts`,
|
||||
content: `import "./d";`
|
||||
};
|
||||
verifyEmitAndErrorUpdates({
|
||||
filesWithNewEmit: [aFile, bFile],
|
||||
filesWithOnlyErrorRefresh: [cFile, dFile],
|
||||
filesNotTouched: [eFile],
|
||||
change: host => host.writeFile(aFile.path, aFile.content.replace("x2", "x")),
|
||||
getInitialErrors: watch => [
|
||||
getDiagnosticOfFileFromProgram(watch(), cFile.path, cFile.content.indexOf("x: 1"), 4, chainDiagnosticMessages(
|
||||
chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, "x", "Coords"),
|
||||
Diagnostics.Type_0_is_not_assignable_to_type_1,
|
||||
"{ x: number; y: number; }",
|
||||
"Coords"
|
||||
)),
|
||||
getDiagnosticOfFileFromProgram(watch(), dFile.path, dFile.content.lastIndexOf("x"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "x", "Coords")
|
||||
],
|
||||
getIncrementalErrors: () => emptyArray
|
||||
});
|
||||
});
|
||||
|
||||
describe("updates errors when file transitively exported file changes", () => {
|
||||
const config: File = {
|
||||
path: `${currentDirectory}/tsconfig.json`,
|
||||
content: `{}`
|
||||
content: JSON.stringify({
|
||||
files: ["app.ts"],
|
||||
compilerOptions: { baseUrl: "." }
|
||||
})
|
||||
};
|
||||
const directoryFiles = [aFile, bFile, cFile, dFile, eFile];
|
||||
const files = [...directoryFiles, config, libFile];
|
||||
const host = createWatchedSystem(files, { currentDirectory });
|
||||
const watch = createWatchOfConfigFile("tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [aFile.path, bFile.path, cFile.path, dFile.path, eFile.path, libFile.path]);
|
||||
checkOutputErrorsInitial(host, [
|
||||
getDiagnosticOfFileFromProgram(watch(), cFile.path, cFile.content.indexOf("x: 1"), 4, chainDiagnosticMessages(
|
||||
chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, "x", "Coords"),
|
||||
Diagnostics.Type_0_is_not_assignable_to_type_1,
|
||||
"{ x: number; y: number; }",
|
||||
"Coords"
|
||||
)),
|
||||
getDiagnosticOfFileFromProgram(watch(), dFile.path, dFile.content.lastIndexOf("x"), 1, Diagnostics.Property_0_does_not_exist_on_type_1, "x", "Coords")
|
||||
]);
|
||||
const beforeChange = getOutputFileStampsAndErrors(host, watch, directoryFiles);
|
||||
host.writeFile(aFile.path, aFile.content.replace("x2", "x"));
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
const afterChange = getOutputFileStampsAndErrors(host, watch, directoryFiles);
|
||||
verifyOutputFileStampsAndErrors(aFile, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange);
|
||||
verifyOutputFileStampsAndErrors(bFile, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange);
|
||||
verifyOutputFileStampsAndErrors(cFile, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange);
|
||||
verifyOutputFileStampsAndErrors(dFile, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange);
|
||||
verifyOutputFileStampsAndErrors(eFile, /*emitExpected*/ false, /*errorRefershExpected*/ false, beforeChange, afterChange);
|
||||
const app: File = {
|
||||
path: `${currentDirectory}/app.ts`,
|
||||
content: `import { Data } from "lib2/public";
|
||||
export class App {
|
||||
public constructor() {
|
||||
new Data().test();
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib2Public: File = {
|
||||
path: `${currentDirectory}/lib2/public.ts`,
|
||||
content: `export * from "./data";`
|
||||
};
|
||||
const lib2Data: File = {
|
||||
path: `${currentDirectory}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public";
|
||||
export class Data {
|
||||
public test() {
|
||||
const result: ITest = {
|
||||
title: "title"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib1Public: File = {
|
||||
path: `${currentDirectory}/lib1/public.ts`,
|
||||
content: `export * from "./tools/public";`
|
||||
};
|
||||
const lib1ToolsPublic: File = {
|
||||
path: `${currentDirectory}/lib1/tools/public.ts`,
|
||||
content: `export * from "./tools.interface";`
|
||||
};
|
||||
const lib1ToolsInterface: File = {
|
||||
path: `${currentDirectory}/lib1/tools/tools.interface.ts`,
|
||||
content: `export interface ITest {
|
||||
title: string;
|
||||
}`
|
||||
};
|
||||
|
||||
function verifyTransitiveExports(lib2Data: File, lib2Data2?: File) {
|
||||
const filesWithNewEmit = [lib1ToolsInterface, lib1ToolsPublic];
|
||||
const filesWithOnlyErrorRefresh = [app, lib2Public, lib1Public, lib2Data];
|
||||
if (lib2Data2) {
|
||||
filesWithOnlyErrorRefresh.push(lib2Data2);
|
||||
}
|
||||
verifyEmitAndErrorUpdates({
|
||||
filesWithNewEmit,
|
||||
filesWithOnlyErrorRefresh,
|
||||
filesNotTouched: emptyArray,
|
||||
configFile: config,
|
||||
change: host => host.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2")),
|
||||
getInitialErrors: () => emptyArray,
|
||||
getIncrementalErrors: () => [
|
||||
"lib2/data.ts(5,13): error TS2322: Type '{ title: string; }' is not assignable to type 'ITest'.\n Object literal may only specify known properties, but 'title' does not exist in type 'ITest'. Did you mean to write 'title2'?\n"
|
||||
]
|
||||
});
|
||||
}
|
||||
it("when there are no circular import and exports", () => {
|
||||
verifyTransitiveExports(lib2Data);
|
||||
});
|
||||
|
||||
it("when there are circular import and exports", () => {
|
||||
const lib2Data: File = {
|
||||
path: `${currentDirectory}/lib2/data.ts`,
|
||||
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
|
||||
export class Data {
|
||||
public dat?: Data2; public test() {
|
||||
const result: ITest = {
|
||||
title: "title"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}`
|
||||
};
|
||||
const lib2Data2: File = {
|
||||
path: `${currentDirectory}/lib2/data2.ts`,
|
||||
content: `import { Data } from "./data";
|
||||
export class Data2 {
|
||||
public dat?: Data;
|
||||
}`
|
||||
};
|
||||
verifyTransitiveExports(lib2Data, lib2Data2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user