diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 2ed4a9031338a..f7d220b789461 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1533,6 +1533,14 @@ namespace ts.server { return undefined; } + /*@internal*/ + findDefaultConfiguredProject(info: ScriptInfo) { + if (!info.isScriptOpen()) return undefined; + const configFileName = this.getConfigFileNameForFile(info); + return configFileName && + this.findConfiguredProjectByProjectName(configFileName); + } + /** * This function tries to search for a tsconfig.json for the given file. * This is different from the method the compiler uses because diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index f602d18ae7049..86d76711d4ae7 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -491,21 +491,40 @@ namespace ts.server { case 1: return this.containingProjects[0]; default: - // if this file belongs to multiple projects, the first configured project should be - // the default project; if no configured projects, the first external project should - // be the default project; otherwise the first inferred project should be the default. + // If this file belongs to multiple projects, below is the order in which default project is used + // - for open script info, its default configured project during opening is default if info is part of it + // - first configured project of which script info is not a source of project reference redirect + // - first configured project + // - first external project + // - first inferred project let firstExternalProject; let firstConfiguredProject; - for (const project of this.containingProjects) { + let firstNonSourceOfProjectReferenceRedirect; + let defaultConfiguredProject: ConfiguredProject | false | undefined; + for (let index = 0; index < this.containingProjects.length; index++) { + const project = this.containingProjects[index]; if (project.projectKind === ProjectKind.Configured) { - if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project; + if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) { + // If we havent found default configuredProject and + // its not the last one, find it and use that one if there + if (defaultConfiguredProject === undefined && + index !== this.containingProjects.length - 1) { + defaultConfiguredProject = project.projectService.findDefaultConfiguredProject(this) || false; + } + if (defaultConfiguredProject === project) return project; + if (!firstNonSourceOfProjectReferenceRedirect) firstNonSourceOfProjectReferenceRedirect = project; + } if (!firstConfiguredProject) firstConfiguredProject = project; } else if (project.projectKind === ProjectKind.External && !firstExternalProject) { firstExternalProject = project; } } - return firstConfiguredProject || firstExternalProject || this.containingProjects[0]; + return defaultConfiguredProject || + firstNonSourceOfProjectReferenceRedirect || + firstConfiguredProject || + firstExternalProject || + this.containingProjects[0]; } } diff --git a/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts b/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts index 908c609e703b6..0de8e8e194846 100644 --- a/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts +++ b/src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts @@ -1,8 +1,7 @@ namespace ts.tscWatch { describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => { - const currentDirectory = "/user/username/projects/myproject"; const config: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: `{}` }; function getOutputFileStampAndError(host: WatchedSystem, watch: Watch, file: File) { @@ -83,7 +82,7 @@ namespace ts.tscWatch { const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched]; const files = [...nonLibFiles, configFile, libFile]; const compilerOptions = (JSON.parse(configFile.content).compilerOptions || {}) as CompilerOptions; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]); checkOutputErrorsInitial(host, getInitialErrors(watch)); @@ -167,7 +166,7 @@ namespace ts.tscWatch { describe("deep import changes", () => { const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `import {B} from './b'; declare var console: any; let b = new B(); @@ -203,7 +202,7 @@ console.log(b.c.d);` describe("updates errors when deep import file changes", () => { const bFile: File = { - path: `${currentDirectory}/b.ts`, + path: `${projectRoot}/b.ts`, content: `import {C} from './c'; export class B { @@ -211,7 +210,7 @@ export class B }` }; const cFile: File = { - path: `${currentDirectory}/c.ts`, + path: `${projectRoot}/c.ts`, content: `export class C { d = 1; @@ -222,7 +221,7 @@ export class B describe("updates errors when deep import through declaration file changes", () => { const bFile: File = { - path: `${currentDirectory}/b.d.ts`, + path: `${projectRoot}/b.d.ts`, content: `import {C} from './c'; export class B { @@ -230,7 +229,7 @@ export class B }` }; const cFile: File = { - path: `${currentDirectory}/c.d.ts`, + path: `${projectRoot}/c.d.ts`, content: `export class C { d: number; @@ -242,7 +241,7 @@ export class B describe("updates errors in file not exporting a deep multilevel import that changes", () => { const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `export interface Point { name: string; c: Coords; @@ -253,13 +252,13 @@ export interface Coords { }` }; const bFile: File = { - path: `${currentDirectory}/b.ts`, + path: `${projectRoot}/b.ts`, content: `import { Point } from "./a"; export interface PointWrapper extends Point { }` }; const cFile: File = { - path: `${currentDirectory}/c.ts`, + path: `${projectRoot}/c.ts`, content: `import { PointWrapper } from "./b"; export function getPoint(): PointWrapper { return { @@ -272,12 +271,12 @@ export function getPoint(): PointWrapper { };` }; const dFile: File = { - path: `${currentDirectory}/d.ts`, + path: `${projectRoot}/d.ts`, content: `import { getPoint } from "./c"; getPoint().c.x;` }; const eFile: File = { - path: `${currentDirectory}/e.ts`, + path: `${projectRoot}/e.ts`, content: `import "./d";` }; verifyEmitAndErrorUpdates({ @@ -301,14 +300,14 @@ getPoint().c.x;` describe("updates errors when file transitively exported file changes", () => { const config: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ files: ["app.ts"], compilerOptions: { baseUrl: "." } }) }; const app: File = { - path: `${currentDirectory}/app.ts`, + path: `${projectRoot}/app.ts`, content: `import { Data } from "lib2/public"; export class App { public constructor() { @@ -317,11 +316,11 @@ export class App { }` }; const lib2Public: File = { - path: `${currentDirectory}/lib2/public.ts`, + path: `${projectRoot}/lib2/public.ts`, content: `export * from "./data";` }; const lib2Data: File = { - path: `${currentDirectory}/lib2/data.ts`, + path: `${projectRoot}/lib2/data.ts`, content: `import { ITest } from "lib1/public"; export class Data { public test() { @@ -333,15 +332,15 @@ export class Data { }` }; const lib1Public: File = { - path: `${currentDirectory}/lib1/public.ts`, + path: `${projectRoot}/lib1/public.ts`, content: `export * from "./tools/public";` }; const lib1ToolsPublic: File = { - path: `${currentDirectory}/lib1/tools/public.ts`, + path: `${projectRoot}/lib1/tools/public.ts`, content: `export * from "./tools.interface";` }; const lib1ToolsInterface: File = { - path: `${currentDirectory}/lib1/tools/tools.interface.ts`, + path: `${projectRoot}/lib1/tools/tools.interface.ts`, content: `export interface ITest { title: string; }` @@ -372,7 +371,7 @@ export class Data { describe("when there are circular import and exports", () => { const lib2Data: File = { - path: `${currentDirectory}/lib2/data.ts`, + path: `${projectRoot}/lib2/data.ts`, content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2"; export class Data { public dat?: Data2; public test() { @@ -384,7 +383,7 @@ export class Data { }` }; const lib2Data2: File = { - path: `${currentDirectory}/lib2/data2.ts`, + path: `${projectRoot}/lib2/data2.ts`, content: `import { Data } from "./data"; export class Data2 { public dat?: Data; diff --git a/src/testRunner/unittests/tscWatch/helpers.ts b/src/testRunner/unittests/tscWatch/helpers.ts index a58785dc7dbc9..1ab49cec139a4 100644 --- a/src/testRunner/unittests/tscWatch/helpers.ts +++ b/src/testRunner/unittests/tscWatch/helpers.ts @@ -1,3 +1,7 @@ +namespace ts { + export const projects = `/user/username/projects`; + export const projectRoot = `${projects}/myproject`; +} namespace ts.tscWatch { export import WatchedSystem = TestFSWithWatch.TestServerHost; export type File = TestFSWithWatch.File; diff --git a/src/testRunner/unittests/tscWatch/programUpdates.ts b/src/testRunner/unittests/tscWatch/programUpdates.ts index ed2a15c9a1410..0b9cc883b85d1 100644 --- a/src/testRunner/unittests/tscWatch/programUpdates.ts +++ b/src/testRunner/unittests/tscWatch/programUpdates.ts @@ -918,20 +918,19 @@ namespace ts.tscWatch { describe("should not trigger should not trigger recompilation because of program emit", () => { function verifyWithOptions(options: CompilerOptions, outputFiles: readonly string[]) { - const proj = "/user/username/projects/myproject"; const file1: File = { - path: `${proj}/file1.ts`, + path: `${projectRoot}/file1.ts`, content: "export const c = 30;" }; const file2: File = { - path: `${proj}/src/file2.ts`, + path: `${projectRoot}/src/file2.ts`, content: `import {c} from "file1"; export const d = 30;` }; const tsconfig: File = { - path: `${proj}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: generateTSConfig(options, emptyArray, "\n") }; - const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: proj }); + const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile(tsconfig.path, host, /*optionsToExtend*/ undefined, /*maxNumberOfFilesToIterateForInvalidation*/1); checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]); @@ -1020,9 +1019,8 @@ namespace ts.tscWatch { }); it("updates errors correctly when declaration emit is disabled in compiler options", () => { - const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `import test from './b'; test(4, 5);` }; @@ -1031,11 +1029,11 @@ test(4, 5);` } export default test;`; const bFile: File = { - path: `${currentDirectory}/b.ts`, + path: `${projectRoot}/b.ts`, content: bFileContent }; const tsconfigFile: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { module: "commonjs", @@ -1045,7 +1043,7 @@ export default test;`; }) }; const files = [aFile, bFile, libFile, tsconfigFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); checkOutputErrorsInitial(host, emptyArray); @@ -1072,22 +1070,21 @@ export default test;`; }); it("updates errors when strictNullChecks changes", () => { - const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `declare function foo(): null | { hello: any }; foo().hello` }; const config: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: {} }) }; const files = [aFile, config, libFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); checkProgramActualFiles(watch(), [aFile.path, libFile.path]); checkOutputErrorsInitial(host, emptyArray); - const modifiedTimeOfAJs = host.getModifiedTime(`${currentDirectory}/a.js`); + const modifiedTimeOfAJs = host.getModifiedTime(`${projectRoot}/a.js`); host.writeFile(config.path, JSON.stringify({ compilerOptions: { strictNullChecks: true } })); host.runQueuedTimeoutCallbacks(); const expectedStrictNullErrors = [ @@ -1095,39 +1092,38 @@ foo().hello` ]; checkOutputErrorsIncremental(host, expectedStrictNullErrors); // File a need not be rewritten - assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs); host.writeFile(config.path, JSON.stringify({ compilerOptions: { strict: true, alwaysStrict: false } })); // Avoid changing 'alwaysStrict' or must re-bind host.runQueuedTimeoutCallbacks(); checkOutputErrorsIncremental(host, expectedStrictNullErrors); // File a need not be rewritten - assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs); host.writeFile(config.path, JSON.stringify({ compilerOptions: {} })); host.runQueuedTimeoutCallbacks(); checkOutputErrorsIncremental(host, emptyArray); // File a need not be rewritten - assert.equal(host.getModifiedTime(`${currentDirectory}/a.js`), modifiedTimeOfAJs); + assert.equal(host.getModifiedTime(`${projectRoot}/a.js`), modifiedTimeOfAJs); }); it("updates errors when ambient modules of program changes", () => { - const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `declare module 'a' { type foo = number; }` }; const config: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const files = [aFile, config, libFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); checkProgramActualFiles(watch(), [aFile.path, libFile.path]); checkOutputErrorsInitial(host, emptyArray); // Create bts with same file contents - const bTsPath = `${currentDirectory}/b.ts`; + const bTsPath = `${projectRoot}/b.ts`; host.writeFile(bTsPath, aFile.content); host.runQueuedTimeoutCallbacks(); checkProgramActualFiles(watch(), [aFile.path, "b.ts", libFile.path]); @@ -1144,7 +1140,6 @@ foo().hello` }); describe("updates errors in lib file", () => { - const currentDirectory = "/user/username/projects/myproject"; const field = "fullscreen"; const fieldWithoutReadonly = `interface Document { ${field}: boolean; @@ -1166,7 +1161,7 @@ interface Document { const files = [aFile, libFileWithDocument]; function verifyLibErrors(options: CompilerOptions) { - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfFilesAndCompilerOptions([aFile.path], host, options); checkProgramActualFiles(watch(), [aFile.path, libFile.path]); checkOutputErrorsInitial(host, getErrors()); @@ -1202,7 +1197,7 @@ interface Document { describe("when non module file changes", () => { const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `${fieldWithoutReadonly} var y: number;` }; @@ -1211,7 +1206,7 @@ var y: number;` describe("when module file with global definitions changes", () => { const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `export {} declare global { ${fieldWithoutReadonly} @@ -1223,16 +1218,15 @@ var y: number; }); it("when skipLibCheck and skipDefaultLibCheck changes", () => { - const currentDirectory = "/user/username/projects/myproject"; const field = "fullscreen"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `interface Document { ${field}: boolean; }` }; const bFile: File = { - path: `${currentDirectory}/b.d.ts`, + path: `${projectRoot}/b.d.ts`, content: `interface Document { ${field}: boolean; }` @@ -1245,13 +1239,13 @@ interface Document { }` }; const configFile: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const files = [aFile, bFile, configFile, libFileWithDocument]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); verifyProgramFiles(); checkOutputErrorsInitial(host, [ @@ -1284,18 +1278,17 @@ interface Document { }); it("reports errors correctly with isolatedModules", () => { - const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `export const a: string = "";` }; const bFile: File = { - path: `${currentDirectory}/b.ts`, + path: `${projectRoot}/b.ts`, content: `import { a } from "./a"; const b: string = a;` }; const configFile: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { isolatedModules: true @@ -1305,20 +1298,20 @@ const b: string = a;` const files = [aFile, bFile, libFile, configFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); verifyProgramFiles(); checkOutputErrorsInitial(host, emptyArray); - assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict"; + assert.equal(host.readFile(`${projectRoot}/a.js`), `"use strict"; exports.__esModule = true; exports.a = ""; `, "Contents of a.js"); - assert.equal(host.readFile(`${currentDirectory}/b.js`), `"use strict"; + assert.equal(host.readFile(`${projectRoot}/b.js`), `"use strict"; exports.__esModule = true; var a_1 = require("./a"); var b = a_1.a; `, "Contents of b.js"); - const modifiedTime = host.getModifiedTime(`${currentDirectory}/b.js`); + const modifiedTime = host.getModifiedTime(`${projectRoot}/b.js`); host.writeFile(aFile.path, `export const a: number = 1`); host.runQueuedTimeoutCallbacks(); @@ -1326,11 +1319,11 @@ var b = a_1.a; checkOutputErrorsIncremental(host, [ getDiagnosticOfFileFromProgram(watch(), bFile.path, bFile.content.indexOf("b"), 1, Diagnostics.Type_0_is_not_assignable_to_type_1, "number", "string") ]); - assert.equal(host.readFile(`${currentDirectory}/a.js`), `"use strict"; + assert.equal(host.readFile(`${projectRoot}/a.js`), `"use strict"; exports.__esModule = true; exports.a = 1; `, "Contents of a.js"); - assert.equal(host.getModifiedTime(`${currentDirectory}/b.js`), modifiedTime, "Timestamp of b.js"); + assert.equal(host.getModifiedTime(`${projectRoot}/b.js`), modifiedTime, "Timestamp of b.js"); function verifyProgramFiles() { checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]); @@ -1338,9 +1331,8 @@ exports.a = 1; }); it("reports errors correctly with file not in rootDir", () => { - const currentDirectory = "/user/username/projects/myproject"; const aFile: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `import { x } from "../b";` }; const bFile: File = { @@ -1348,7 +1340,7 @@ exports.a = 1; content: `export const x = 10;` }; const configFile: File = { - path: `${currentDirectory}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { rootDir: ".", @@ -1359,10 +1351,10 @@ exports.a = 1; const files = [aFile, bFile, libFile, configFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfConfigFile("tsconfig.json", host); checkOutputErrorsInitial(host, [ - getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory) + getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, projectRoot) ]); const aContent = ` @@ -1370,7 +1362,7 @@ ${aFile.content}`; host.writeFile(aFile.path, aContent); host.runQueuedTimeoutCallbacks(); checkOutputErrorsIncremental(host, [ - getDiagnosticOfFileFromProgram(watch(), aFile.path, aContent.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory) + getDiagnosticOfFileFromProgram(watch(), aFile.path, aContent.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, projectRoot) ]); }); }); diff --git a/src/testRunner/unittests/tscWatch/resolutionCache.ts b/src/testRunner/unittests/tscWatch/resolutionCache.ts index 802b9c5bc523e..02009d1100669 100644 --- a/src/testRunner/unittests/tscWatch/resolutionCache.ts +++ b/src/testRunner/unittests/tscWatch/resolutionCache.ts @@ -339,42 +339,40 @@ declare module "fs" { }); it("works when renaming node_modules folder that already contains @types folder", () => { - const currentDirectory = "/user/username/projects/myproject"; const file: File = { - path: `${currentDirectory}/a.ts`, + path: `${projectRoot}/a.ts`, content: `import * as q from "qqq";` }; const module: File = { - path: `${currentDirectory}/node_modules2/@types/qqq/index.d.ts`, + path: `${projectRoot}/node_modules2/@types/qqq/index.d.ts`, content: "export {}" }; const files = [file, module, libFile]; - const host = createWatchedSystem(files, { currentDirectory }); + const host = createWatchedSystem(files, { currentDirectory: projectRoot }); const watch = createWatchOfFilesAndCompilerOptions([file.path], host); checkProgramActualFiles(watch(), [file.path, libFile.path]); checkOutputErrorsInitial(host, [getDiagnosticModuleNotFoundOfFile(watch(), file, "qqq")]); checkWatchedDirectories(host, emptyArray, /*recursive*/ false); - checkWatchedDirectories(host, [`${currentDirectory}/node_modules`, `${currentDirectory}/node_modules/@types`], /*recursive*/ true); + checkWatchedDirectories(host, [`${projectRoot}/node_modules`, `${projectRoot}/node_modules/@types`], /*recursive*/ true); - host.renameFolder(`${currentDirectory}/node_modules2`, `${currentDirectory}/node_modules`); + host.renameFolder(`${projectRoot}/node_modules2`, `${projectRoot}/node_modules`); host.runQueuedTimeoutCallbacks(); - checkProgramActualFiles(watch(), [file.path, libFile.path, `${currentDirectory}/node_modules/@types/qqq/index.d.ts`]); + checkProgramActualFiles(watch(), [file.path, libFile.path, `${projectRoot}/node_modules/@types/qqq/index.d.ts`]); checkOutputErrorsIncremental(host, emptyArray); }); describe("ignores files/folder changes in node_modules that start with '.'", () => { - const projectPath = "/user/username/projects/project"; const npmCacheFile: File = { - path: `${projectPath}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`, + path: `${projectRoot}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`, content: JSON.stringify({ something: 10 }) }; const file1: File = { - path: `${projectPath}/test.ts`, + path: `${projectRoot}/test.ts`, content: `import { x } from "somemodule";` }; const file2: File = { - path: `${projectPath}/node_modules/somemodule/index.d.ts`, + path: `${projectRoot}/node_modules/somemodule/index.d.ts`, content: `export const x = 10;` }; const files = [libFile, file1, file2]; @@ -390,7 +388,7 @@ declare module "fs" { }); it("when watching node_modules as part of wild card directories in config project", () => { const config: File = { - path: `${projectPath}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const host = createWatchedSystem(files.concat(config)); @@ -405,7 +403,6 @@ declare module "fs" { }); describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch with modules linked to sibling folder", () => { - const projectRoot = "/user/username/projects/project"; const mainPackageRoot = `${projectRoot}/main`; const linkedPackageRoot = `${projectRoot}/linked-package`; const mainFile: File = { diff --git a/src/testRunner/unittests/tscWatch/watchApi.ts b/src/testRunner/unittests/tscWatch/watchApi.ts index facd668b825b4..8f94ff005ecd5 100644 --- a/src/testRunner/unittests/tscWatch/watchApi.ts +++ b/src/testRunner/unittests/tscWatch/watchApi.ts @@ -1,6 +1,5 @@ namespace ts.tscWatch { describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolution", () => { - const projectRoot = "/user/username/projects/project"; const configFileJson: any = { compilerOptions: { module: "commonjs", resolveJsonModule: true }, files: ["index.ts"] @@ -39,7 +38,6 @@ namespace ts.tscWatch { }); describe("unittests:: tsc-watch:: watchAPI:: tsc-watch expose error count to watch status reporter", () => { - const projectRoot = "/user/username/projects/project"; const configFileJson: any = { compilerOptions: { module: "commonjs" }, files: ["index.ts"] diff --git a/src/testRunner/unittests/tsserver/compileOnSave.ts b/src/testRunner/unittests/tsserver/compileOnSave.ts index f641b6e85cb36..47d4571541c05 100644 --- a/src/testRunner/unittests/tsserver/compileOnSave.ts +++ b/src/testRunner/unittests/tsserver/compileOnSave.ts @@ -802,7 +802,6 @@ namespace ts.projectSystem { }); describe("unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => { - const projectRoot = "/user/username/projects/myproject"; const core: File = { path: `${projectRoot}/core/core.ts`, content: "let z = 10;" diff --git a/src/testRunner/unittests/tsserver/configuredProjects.ts b/src/testRunner/unittests/tsserver/configuredProjects.ts index 7d066cd9473ca..5d2a8730492c8 100644 --- a/src/testRunner/unittests/tsserver/configuredProjects.ts +++ b/src/testRunner/unittests/tsserver/configuredProjects.ts @@ -82,7 +82,6 @@ namespace ts.projectSystem { }); it("add and then remove a config file in a folder with loose files", () => { - const projectRoot = "/user/username/projects/project"; const configFile: File = { path: `${projectRoot}/tsconfig.json`, content: `{ @@ -440,7 +439,6 @@ namespace ts.projectSystem { }); it("open file become a part of configured project if it is referenced from root file", () => { - const projectRoot = "/user/username/projects/project"; const file1 = { path: `${projectRoot}/a/b/f1.ts`, content: "export let x = 5" @@ -846,6 +844,67 @@ namespace ts.projectSystem { const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options); assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]); }); + + it("when multiple projects are open, detects correct default project", () => { + const barConfig: File = { + path: `${projectRoot}/bar/tsconfig.json`, + content: JSON.stringify({ + include: ["index.ts"], + compilerOptions: { + lib: ["dom", "es2017"] + } + }) + }; + const barIndex: File = { + path: `${projectRoot}/bar/index.ts`, + content: ` +export function bar() { + console.log("hello world"); +}` + }; + const fooConfig: File = { + path: `${projectRoot}/foo/tsconfig.json`, + content: JSON.stringify({ + include: ["index.ts"], + compilerOptions: { + lib: ["es2017"] + } + }) + }; + const fooIndex: File = { + path: `${projectRoot}/foo/index.ts`, + content: ` +import { bar } from "bar"; +bar();` + }; + const barSymLink: SymLink = { + path: `${projectRoot}/foo/node_modules/bar`, + symLink: `${projectRoot}/bar` + }; + + const lib2017: File = { + path: `${getDirectoryPath(libFile.path)}/lib.es2017.d.ts`, + content: libFile.content + }; + const libDom: File = { + path: `${getDirectoryPath(libFile.path)}/lib.dom.d.ts`, + content: ` +declare var console: { + log(...args: any[]): void; +};` + }; + const host = createServerHost([barConfig, barIndex, fooConfig, fooIndex, barSymLink, lib2017, libDom]); + const session = createSession(host, { canUseEvents: true, }); + openFilesForSession([fooIndex, barIndex], session); + verifyGetErrRequest({ + session, + host, + expected: [ + { file: barIndex, syntax: [], semantic: [], suggestion: [] }, + { file: fooIndex, syntax: [], semantic: [], suggestion: [] }, + ] + }); + }); }); describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories listed in config file input array", () => { @@ -903,13 +962,12 @@ namespace ts.projectSystem { }); it("should tolerate invalid include files that start in subDirectory", () => { - const projectFolder = "/user/username/projects/myproject"; const f = { - path: `${projectFolder}/src/server/index.ts`, + path: `${projectRoot}/src/server/index.ts`, content: "let x = 1" }; const config = { - path: `${projectFolder}/src/server/tsconfig.json`, + path: `${projectRoot}/src/server/tsconfig.json`, content: JSON.stringify({ compiler: { module: "commonjs", diff --git a/src/testRunner/unittests/tsserver/documentRegistry.ts b/src/testRunner/unittests/tsserver/documentRegistry.ts index 10723300cc060..51b1bee660426 100644 --- a/src/testRunner/unittests/tsserver/documentRegistry.ts +++ b/src/testRunner/unittests/tsserver/documentRegistry.ts @@ -1,17 +1,16 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: document registry in project service", () => { - const projectRootPath = "/user/username/projects/project"; const importModuleContent = `import {a} from "./module1"`; const file: File = { - path: `${projectRootPath}/index.ts`, + path: `${projectRoot}/index.ts`, content: importModuleContent }; const moduleFile: File = { - path: `${projectRootPath}/module1.d.ts`, + path: `${projectRoot}/module1.d.ts`, content: "export const a: number;" }; const configFile: File = { - path: `${projectRootPath}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ files: ["index.ts"] }) }; diff --git a/src/testRunner/unittests/tsserver/events/largeFileReferenced.ts b/src/testRunner/unittests/tsserver/events/largeFileReferenced.ts index ff69a7e6568a4..cb9df9e6372d4 100644 --- a/src/testRunner/unittests/tsserver/events/largeFileReferenced.ts +++ b/src/testRunner/unittests/tsserver/events/largeFileReferenced.ts @@ -1,6 +1,5 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: events:: LargeFileReferencedEvent with large file", () => { - const projectRoot = "/user/username/projects/project"; function getLargeFile(useLargeTsFile: boolean) { return `src/large.${useLargeTsFile ? "ts" : "js"}`; diff --git a/src/testRunner/unittests/tsserver/events/projectLoading.ts b/src/testRunner/unittests/tsserver/events/projectLoading.ts index 5f4bf1a52c9ca..2bb0d95761a5e 100644 --- a/src/testRunner/unittests/tsserver/events/projectLoading.ts +++ b/src/testRunner/unittests/tsserver/events/projectLoading.ts @@ -1,16 +1,15 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: events:: ProjectLoadingStart and ProjectLoadingFinish events", () => { - const projectRoot = "/user/username/projects"; const aTs: File = { - path: `${projectRoot}/a/a.ts`, + path: `${projects}/a/a.ts`, content: "export class A { }" }; const configA: File = { - path: `${projectRoot}/a/tsconfig.json`, + path: `${projects}/a/tsconfig.json`, content: "{}" }; - const bTsPath = `${projectRoot}/b/b.ts`; - const configBPath = `${projectRoot}/b/tsconfig.json`; + const bTsPath = `${projects}/b/b.ts`; + const configBPath = `${projects}/b/tsconfig.json`; const files = [libFile, aTs, configA]; function verifyProjectLoadingStartAndFinish(createSession: (host: TestServerHost) => { @@ -84,14 +83,14 @@ namespace ts.projectSystem { function verify(disableSourceOfProjectReferenceRedirect?: true) { const aDTs: File = { - path: `${projectRoot}/a/a.d.ts`, + path: `${projects}/a/a.d.ts`, content: `export declare class A { } //# sourceMappingURL=a.d.ts.map ` }; const aDTsMap: File = { - path: `${projectRoot}/a/a.d.ts.map`, + path: `${projects}/a/a.d.ts.map`, content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}` }; const bTs: File = { @@ -134,7 +133,7 @@ namespace ts.projectSystem { }); describe("with external projects and config files ", () => { - const projectFileName = `${projectRoot}/a/project.csproj`; + const projectFileName = `${projects}/a/project.csproj`; function createSession(lazyConfiguredProjectsFromExternalProject: boolean) { const { session, service, verifyEvent: verifyEventWorker, getNumberOfEvents } = createSessionToVerifyEvent(files); diff --git a/src/testRunner/unittests/tsserver/externalProjects.ts b/src/testRunner/unittests/tsserver/externalProjects.ts index 21d1290fc2e96..5e335f1a4faaf 100644 --- a/src/testRunner/unittests/tsserver/externalProjects.ts +++ b/src/testRunner/unittests/tsserver/externalProjects.ts @@ -824,10 +824,9 @@ namespace ts.projectSystem { }); it("handles creation of external project with jsconfig before jsconfig creation watcher is invoked", () => { - const projectLocation = `/user/username/projects/WebApplication36/WebApplication36`; - const projectFileName = `${projectLocation}/WebApplication36.csproj`; + const projectFileName = `${projectRoot}/WebApplication36.csproj`; const tsconfig: File = { - path: `${projectLocation}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const files = [libFile, tsconfig]; @@ -845,7 +844,7 @@ namespace ts.projectSystem { checkProjectActualFiles(configProject, [tsconfig.path]); // write js file, open external project and open it for edit - const jsFilePath = `${projectLocation}/javascript.js`; + const jsFilePath = `${projectRoot}/javascript.js`; host.writeFile(jsFilePath, ""); service.openExternalProjects([{ projectFileName, @@ -860,7 +859,7 @@ namespace ts.projectSystem { // write jsconfig file const jsConfig: File = { - path: `${projectLocation}/jsconfig.json`, + path: `${projectRoot}/jsconfig.json`, content: "{}" }; // Dont invoke file creation watchers as the repro suggests diff --git a/src/testRunner/unittests/tsserver/inferredProjects.ts b/src/testRunner/unittests/tsserver/inferredProjects.ts index 44ff8292acdd5..6c647c4e64139 100644 --- a/src/testRunner/unittests/tsserver/inferredProjects.ts +++ b/src/testRunner/unittests/tsserver/inferredProjects.ts @@ -1,7 +1,6 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: Inferred projects", () => { it("create inferred project", () => { - const projectRoot = "/user/username/projects/project"; const appFile: File = { path: `${projectRoot}/app.ts`, content: ` @@ -31,7 +30,6 @@ namespace ts.projectSystem { }); it("should use only one inferred project if 'useOneInferredProject' is set", () => { - const projectRoot = "/user/username/projects/project"; const file1 = { path: `${projectRoot}/a/b/main.ts`, content: "let x =1;" @@ -347,7 +345,6 @@ namespace ts.projectSystem { }); it("should still retain configured project created while opening the file", () => { - const projectRoot = "/user/username/projects/project"; const appFile: File = { path: `${projectRoot}/app.ts`, content: `const app = 20;` diff --git a/src/testRunner/unittests/tsserver/projectErrors.ts b/src/testRunner/unittests/tsserver/projectErrors.ts index bb60e48e3ad60..f5c28eb7a74c0 100644 --- a/src/testRunner/unittests/tsserver/projectErrors.ts +++ b/src/testRunner/unittests/tsserver/projectErrors.ts @@ -292,42 +292,21 @@ namespace ts.projectSystem { // Since this is not js project so no typings are queued host.checkTimeoutQueueLength(0); - const newTimeoutId = host.getNextTimeoutId(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [untitledFile] - } - }); - host.checkTimeoutQueueLength(1); - - // Run the last one = get error request - host.runQueuedTimeoutCallbacks(newTimeoutId); - - assert.isFalse(hasError()); - host.checkTimeoutQueueLength(0); - checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - assert.isFalse(hasError()); const errorOffset = fileContent.indexOf(refPathNotFound1) + 1; - checkErrorMessage(session, "semanticDiag", { - file: untitledFile, - diagnostics: [ - createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), - createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") - ] + verifyGetErrRequest({ + session, + host, + expected: [{ + file: untitledFile, + syntax: [], + semantic: [ + createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), + createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") + ], + suggestion: [] + }], + onErrEvent: () => assert.isFalse(hasError()) }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - assert.isFalse(hasError()); - checkErrorMessage(session, "suggestionDiag", { file: untitledFile, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } it("has projectRoot", () => { @@ -371,27 +350,16 @@ namespace ts.projectSystem { verifyErrorsInApp(); function verifyErrorsInApp() { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [app.path] - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file: app, + syntax: [], + semantic: [], + suggestion: [] + }], }); - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: app.path, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); @@ -421,7 +389,6 @@ namespace ts.projectSystem { }); it("Reports errors correctly when file referenced by inferred project root, is opened right after closing the root file", () => { - const projectRoot = "/user/username/projects/myproject"; const app: File = { path: `${projectRoot}/src/client/app.js`, content: "" @@ -451,32 +418,12 @@ namespace ts.projectSystem { checkErrors([serverUtilities.path, app.path]); function checkErrors(openFiles: [string, string]) { - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - delay: 0, - files: openFiles - } + verifyGetErrRequest({ + session, + host, + expected: openFiles.map(file => ({ file, syntax: [], semantic: [], suggestion: [] })), + existingTimeouts: 2 }); - - for (const openFile of openFiles) { - session.clearMessages(); - host.checkTimeoutQueueLength(3); - host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); - - checkErrorMessage(session, "syntaxDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: openFile, diagnostics: [] }); - } - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); @@ -531,36 +478,19 @@ declare module '@custom/plugin' { function checkErrors() { host.checkTimeoutQueueLength(0); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [aFile.path], - } - }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: aFile.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: aFile.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: aFile.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true), - createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true) - ], + verifyGetErrRequest({ + session, + host, + expected: [{ + file: aFile, + syntax: [], + semantic: [], + suggestion: [ + createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true), + createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true) + ] + }] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); }); diff --git a/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts b/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts index 9602ef6360eec..7399078f1ef9d 100644 --- a/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts +++ b/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts @@ -1,8 +1,7 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: with project references and compile on save", () => { - const projectLocation = "/user/username/projects/myproject"; - const dependecyLocation = `${projectLocation}/dependency`; - const usageLocation = `${projectLocation}/usage`; + const dependecyLocation = `${projectRoot}/dependency`; + const usageLocation = `${projectRoot}/usage`; const dependencyTs: File = { path: `${dependecyLocation}/fns.ts`, content: `export function fn1() { } @@ -294,7 +293,7 @@ exports.fn2 = fn2; ${appendJs}` }, { - path: `${projectLocation}/decls/fns.d.ts`, + path: `${projectRoot}/decls/fns.d.ts`, content: `export declare function fn1(): void; export declare function fn2(): void; ${appendDts}` diff --git a/src/testRunner/unittests/tsserver/projectReferenceErrors.ts b/src/testRunner/unittests/tsserver/projectReferenceErrors.ts index a3ec48486157b..e6e71263576ff 100644 --- a/src/testRunner/unittests/tsserver/projectReferenceErrors.ts +++ b/src/testRunner/unittests/tsserver/projectReferenceErrors.ts @@ -1,48 +1,92 @@ namespace ts.projectSystem { - describe("unittests:: tsserver:: with project references and error reporting", () => { - const projectLocation = "/user/username/projects/myproject"; - const dependecyLocation = `${projectLocation}/dependency`; - const usageLocation = `${projectLocation}/usage`; + export interface GetErrDiagnostics { + file: string | File; + syntax?: protocol.Diagnostic[]; + semantic?: protocol.Diagnostic[]; + suggestion?: protocol.Diagnostic[]; + } + export interface VerifyGetErrRequestBase { + session: TestSession; + host: TestServerHost; + onErrEvent?: () => void; + existingTimeouts?: number; + } + export interface VerifyGetErrRequest extends VerifyGetErrRequestBase { + expected: readonly GetErrDiagnostics[]; + } + export function verifyGetErrRequest(request: VerifyGetErrRequest) { + const { session, expected } = request; + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + arguments: { + delay: 0, + files: expected.map(f => filePath(f.file)) + } + }); + checkAllErrors({ ...request, expectedSequenceId }); + } + + export interface CheckAllErrors extends VerifyGetErrRequest { + expectedSequenceId: number; + } + function checkAllErrors({ expected, expectedSequenceId, ...rest }: CheckAllErrors) { + for (let i = 0; i < expected.length; i++) { + checkErrorsInFile({ + ...rest, + expected: expected[i], + expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined, + }); + } + } - interface CheckErrorsInFile { - session: TestSession; - host: TestServerHost; - expected: GetErrDiagnostics; - expectedSequenceId?: number; + function filePath(file: string | File) { + return isString(file) ? file : file.path; + } + interface CheckErrorsInFile extends VerifyGetErrRequestBase { + expected: GetErrDiagnostics; + expectedSequenceId?: number; + } + function checkErrorsInFile({ + session, host, onErrEvent, existingTimeouts, expectedSequenceId, + expected: { file, syntax, semantic, suggestion }, + }: CheckErrorsInFile) { + onErrEvent = onErrEvent || noop; + if (existingTimeouts !== undefined) { + host.checkTimeoutQueueLength(existingTimeouts + 1); + host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); } - function checkErrorsInFile({ session, host, expected: { file, syntax, semantic, suggestion }, expectedSequenceId }: CheckErrorsInFile) { + else { host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: syntax }); + } + if (syntax) { + onErrEvent(); + checkErrorMessage(session, "syntaxDiag", { file: filePath(file), diagnostics: syntax }); + } + if (semantic) { session.clearMessages(); host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: semantic }); + onErrEvent(); + checkErrorMessage(session, "semanticDiag", { file: filePath(file), diagnostics: semantic }); + } + if (suggestion) { session.clearMessages(); host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: file.path, diagnostics: suggestion }); - if (expectedSequenceId !== undefined) { - checkCompleteEvent(session, 2, expectedSequenceId); - } - session.clearMessages(); + onErrEvent(); + checkErrorMessage(session, "suggestionDiag", { file: filePath(file), diagnostics: suggestion }); } - - interface CheckAllErrors { - session: TestSession; - host: TestServerHost; - expected: readonly GetErrDiagnostics[]; - expectedSequenceId: number; - } - function checkAllErrors({ session, host, expected, expectedSequenceId }: CheckAllErrors) { - for (let i = 0; i < expected.length; i++) { - checkErrorsInFile({ - session, - host, - expected: expected[i], - expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined - }); - } + if (expectedSequenceId !== undefined) { + checkCompleteEvent(session, syntax || semantic || suggestion ? 2 : 1, expectedSequenceId); } + session.clearMessages(); + } + + describe("unittests:: tsserver:: with project references and error reporting", () => { + const dependecyLocation = `${projectRoot}/dependency`; + const usageLocation = `${projectRoot}/usage`; function verifyErrorsUsingGeterr({ allFiles, openFiles, expectedGetErr }: VerifyScenario) { it("verifies the errors in open file", () => { @@ -50,18 +94,7 @@ namespace ts.projectSystem { const session = createSession(host, { canUseEvents: true, }); openFilesForSession(openFiles(), session); - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - const expected = expectedGetErr(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - delay: 0, - files: expected.map(f => f.file.path) - } - }); - - checkAllErrors({ session, host, expected, expectedSequenceId }); + verifyGetErrRequest({ session, host, expected: expectedGetErr() }); }); } @@ -96,27 +129,27 @@ namespace ts.projectSystem { const actualSyntax = session.executeCommandSeq({ command: protocol.CommandTypes.SyntacticDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${filePath(file)}, project: ${project}`); const actualSemantic = session.executeCommandSeq({ command: protocol.CommandTypes.SemanticDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${filePath(file)}, project: ${project}`); const actualSuggestion = session.executeCommandSeq({ command: protocol.CommandTypes.SuggestionDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${filePath(file)}, project: ${project}`); } }); } @@ -140,12 +173,6 @@ namespace ts.projectSystem { }); } - interface GetErrDiagnostics { - file: File; - syntax: protocol.Diagnostic[]; - semantic: protocol.Diagnostic[]; - suggestion: protocol.Diagnostic[]; - } interface GetErrForProjectDiagnostics { project: string; errors: readonly GetErrDiagnostics[]; diff --git a/src/testRunner/unittests/tsserver/projectReferences.ts b/src/testRunner/unittests/tsserver/projectReferences.ts index e4716ac5f2ed7..bbc368b6d8a33 100644 --- a/src/testRunner/unittests/tsserver/projectReferences.ts +++ b/src/testRunner/unittests/tsserver/projectReferences.ts @@ -92,10 +92,9 @@ namespace ts.projectSystem { }); describe("with main and depedency project", () => { - const projectLocation = "/user/username/projects/myproject"; - const dependecyLocation = `${projectLocation}/dependency`; - const dependecyDeclsLocation = `${projectLocation}/decls`; - const mainLocation = `${projectLocation}/main`; + const dependecyLocation = `${projectRoot}/dependency`; + const dependecyDeclsLocation = `${projectRoot}/decls`; + const mainLocation = `${projectRoot}/main`; const dependencyTs: File = { path: `${dependecyLocation}/FnS.ts`, content: `export function fn1() { } @@ -137,11 +136,11 @@ fn5(); }; const randomFile: File = { - path: `${projectLocation}/random/random.ts`, + path: `${projectRoot}/random/random.ts`, content: "let a = 10;" }; const randomConfig: File = { - path: `${projectLocation}/random/tsconfig.json`, + path: `${projectRoot}/random/tsconfig.json`, content: "{}" }; const dtsLocation = `${dependecyDeclsLocation}/FnS.d.ts`; @@ -1302,9 +1301,8 @@ function foo() { }); it("reusing d.ts files from composite and non composite projects", () => { - const projectLocation = "/user/username/projects/myproject"; const configA: File = { - path: `${projectLocation}/compositea/tsconfig.json`, + path: `${projectRoot}/compositea/tsconfig.json`, content: JSON.stringify({ compilerOptions: { composite: true, @@ -1316,27 +1314,27 @@ function foo() { }) }; const aTs: File = { - path: `${projectLocation}/compositea/a.ts`, + path: `${projectRoot}/compositea/a.ts`, content: `import { b } from "@ref/compositeb/b";` }; const a2Ts: File = { - path: `${projectLocation}/compositea/a2.ts`, + path: `${projectRoot}/compositea/a2.ts`, content: `export const x = 10;` }; const configB: File = { - path: `${projectLocation}/compositeb/tsconfig.json`, + path: `${projectRoot}/compositeb/tsconfig.json`, content: configA.content }; const bTs: File = { - path: `${projectLocation}/compositeb/b.ts`, + path: `${projectRoot}/compositeb/b.ts`, content: "export function b() {}" }; const bDts: File = { - path: `${projectLocation}/dist/compositeb/b.d.ts`, + path: `${projectRoot}/dist/compositeb/b.d.ts`, content: "export declare function b(): void;" }; const configC: File = { - path: `${projectLocation}/compositec/tsconfig.json`, + path: `${projectRoot}/compositec/tsconfig.json`, content: JSON.stringify({ compilerOptions: { composite: true, @@ -1349,7 +1347,7 @@ function foo() { }) }; const cTs: File = { - path: `${projectLocation}/compositec/c.ts`, + path: `${projectRoot}/compositec/c.ts`, content: aTs.content }; const files = [libFile, aTs, a2Ts, configA, bDts, bTs, configB, cTs, configC]; diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 7d92949b52609..56bbe989bfc7d 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -1209,17 +1209,16 @@ var x = 10;` }); it("requests are done on file on pendingReload but has svc for previous version", () => { - const projectLocation = "/user/username/projects/project"; const file1: File = { - path: `${projectLocation}/src/file1.ts`, + path: `${projectRoot}/src/file1.ts`, content: `import { y } from "./file2"; let x = 10;` }; const file2: File = { - path: `${projectLocation}/src/file2.ts`, + path: `${projectRoot}/src/file2.ts`, content: "export let y = 10;" }; const config: File = { - path: `${projectLocation}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const files = [file1, file2, libFile, config]; @@ -1309,20 +1308,19 @@ var x = 10;` }); it("Orphan source files are handled correctly on watch trigger", () => { - const projectLocation = "/user/username/projects/project"; const file1: File = { - path: `${projectLocation}/src/file1.ts`, + path: `${projectRoot}/src/file1.ts`, content: `export let x = 10;` }; const file2: File = { - path: `${projectLocation}/src/file2.ts`, + path: `${projectRoot}/src/file2.ts`, content: "export let y = 10;" }; const configContent1 = JSON.stringify({ files: ["src/file1.ts", "src/file2.ts"] }); const config: File = { - path: `${projectLocation}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: configContent1 }; const files = [file1, file2, libFile, config]; @@ -1445,36 +1443,17 @@ var x = 10;` // Actually trigger the file move host.reloadFS(files); host.checkTimeoutQueueLength(2); - const fileBErrorTimeoutId = host.getNextTimeoutId(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - files: [fileB.path, fileSubA.path], - delay: 0 - } + verifyGetErrRequest({ + session, + host, + expected: [ + { file: fileB, syntax: [], semantic: [], suggestion: [] }, + { file: fileSubA }, + ], + existingTimeouts: 2, + onErrEvent: () => assert.isFalse(hasErrorMsg()) }); - const getErrSeqId = session.getSeq(); - host.checkTimeoutQueueLength(3); - - session.clearMessages(); - host.runQueuedTimeoutCallbacks(fileBErrorTimeoutId); - checkErrorMessage(session, "syntaxDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - const fileSubAErrorTimeoutId = host.getNextTimeoutId(); - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "suggestionDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - host.checkTimeoutQueueLength(3); - host.runQueuedTimeoutCallbacks(fileSubAErrorTimeoutId); - checkCompleteEvent(session, 1, getErrSeqId); - assert.isFalse(hasErrorMsg()); function openFile(file: File) { openFilesForSession([{ file, projectRootPath }], session); diff --git a/src/testRunner/unittests/tsserver/resolutionCache.ts b/src/testRunner/unittests/tsserver/resolutionCache.ts index 85af2bb36b041..7ebb2ee4d3746 100644 --- a/src/testRunner/unittests/tsserver/resolutionCache.ts +++ b/src/testRunner/unittests/tsserver/resolutionCache.ts @@ -149,34 +149,20 @@ namespace ts.projectSystem { } }); checkNumberOfProjects(service, { inferredProjects: 1 }); - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file1.path] - } - }); - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: file1.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); const startOffset = file1.content.indexOf('"') + 1; - checkErrorMessage(session, "semanticDiag", { - file: file1.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + '"pad"'.length }, Diagnostics.Cannot_find_module_0, ["pad"]) - ], + verifyGetErrRequest({ + session, + host, + expected: [{ + file: file1, + syntax: [], + semantic: [ + createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + '"pad"'.length }, Diagnostics.Cannot_find_module_0, ["pad"]) + ], + suggestion: [] + }] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: file1.path, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); const padIndex: File = { path: `${folderPath}/node_modules/@types/pad/index.d.ts`, @@ -213,40 +199,22 @@ namespace ts.projectSystem { checkNumberOfProjects(service, { inferredProjects: 1 }); session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); host.checkTimeoutQueueLengthAndRun(2); checkProjectUpdatedInBackgroundEvent(session, [file.path]); - session.clearMessages(); - - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file.path], - } - }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: file.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecessary*/ true), - ], + verifyGetErrRequest({ + session, + host, + expected: [{ + file, + syntax: [], + semantic: [], + suggestion: [ + createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecessary*/ true), + ] + }] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); }); it("disable suggestion diagnostics", () => { @@ -273,31 +241,19 @@ namespace ts.projectSystem { checkNumberOfProjects(service, { inferredProjects: 1 }); session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); host.checkTimeoutQueueLengthAndRun(2); checkProjectUpdatedInBackgroundEvent(session, [file.path]); - session.clearMessages(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file.path], - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file, + syntax: [], + semantic: [] + }] }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: [] }); - // No suggestion event, we're done. - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); }); it("suppressed diagnostic events", () => { @@ -494,9 +450,8 @@ namespace ts.projectSystem { }); describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module resolution caching", () => { - const projectLocation = "/user/username/projects/myproject"; const configFile: File = { - path: `${projectLocation}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { traceResolution: true } }) }; @@ -603,7 +558,7 @@ namespace ts.projectSystem { } function verifyWatchesWithConfigFile(host: TestServerHost, files: File[], openFile: File, extraExpectedDirectories?: readonly string[]) { - const expectedRecursiveDirectories = arrayToSet([projectLocation, `${projectLocation}/${nodeModulesAtTypes}`, ...(extraExpectedDirectories || emptyArray)]); + const expectedRecursiveDirectories = arrayToSet([projectRoot, `${projectRoot}/${nodeModulesAtTypes}`, ...(extraExpectedDirectories || emptyArray)]); checkWatchedFiles(host, mapDefined(files, f => { if (f === openFile) { return undefined; @@ -622,11 +577,11 @@ namespace ts.projectSystem { describe("from files in same folder", () => { function getFiles(fileContent: string) { const file1: File = { - path: `${projectLocation}/src/file1.ts`, + path: `${projectRoot}/src/file1.ts`, content: fileContent }; const file2: File = { - path: `${projectLocation}/src/file2.ts`, + path: `${projectRoot}/src/file2.ts`, content: fileContent }; return { file1, file2 }; @@ -637,7 +592,7 @@ namespace ts.projectSystem { const module2Name = "../module2"; const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`; const { file1, file2 } = getFiles(fileContent); - const { module1, module2 } = getModules(`${projectLocation}/src/module1.ts`, `${projectLocation}/module2.ts`); + const { module1, module2 } = getModules(`${projectRoot}/src/module1.ts`, `${projectRoot}/module2.ts`); const files = [module1, module2, file1, file2, configFile, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); @@ -660,12 +615,12 @@ namespace ts.projectSystem { }); it("non relative module name", () => { - const expectedNonRelativeDirectories = [`${projectLocation}/node_modules`, `${projectLocation}/src`]; + const expectedNonRelativeDirectories = [`${projectRoot}/node_modules`, `${projectRoot}/src`]; const module1Name = "module1"; const module2Name = "module2"; const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`; const { file1, file2 } = getFiles(fileContent); - const { module1, module2 } = getModules(`${projectLocation}/src/node_modules/module1/index.ts`, `${projectLocation}/node_modules/module2/index.ts`); + const { module1, module2 } = getModules(`${projectRoot}/src/node_modules/module1/index.ts`, `${projectRoot}/node_modules/module2/index.ts`); const files = [module1, module2, file1, file2, configFile, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); @@ -691,19 +646,19 @@ namespace ts.projectSystem { describe("from files in different folders", () => { function getFiles(fileContent1: string, fileContent2 = fileContent1, fileContent3 = fileContent1, fileContent4 = fileContent1) { const file1: File = { - path: `${projectLocation}/product/src/file1.ts`, + path: `${projectRoot}/product/src/file1.ts`, content: fileContent1 }; const file2: File = { - path: `${projectLocation}/product/src/feature/file2.ts`, + path: `${projectRoot}/product/src/feature/file2.ts`, content: fileContent2 }; const file3: File = { - path: `${projectLocation}/product/test/src/file3.ts`, + path: `${projectRoot}/product/test/src/file3.ts`, content: fileContent3 }; const file4: File = { - path: `${projectLocation}/product/test/file4.ts`, + path: `${projectRoot}/product/test/file4.ts`, content: fileContent4 }; return { file1, file2, file3, file4 }; @@ -721,7 +676,7 @@ namespace ts.projectSystem { const fileContent3 = `import { module1 } from "${module5Name}";import { module2 } from "${module4Name}";`; const fileContent4 = `import { module1 } from "${module6Name}";import { module2 } from "${module2Name}";`; const { file1, file2, file3, file4 } = getFiles(fileContent1, fileContent2, fileContent3, fileContent4); - const { module1, module2 } = getModules(`${projectLocation}/product/src/module1.ts`, `${projectLocation}/product/module2.ts`); + const { module1, module2 } = getModules(`${projectRoot}/product/src/module1.ts`, `${projectRoot}/product/module2.ts`); const files = [module1, module2, file1, file2, file3, file4, configFile, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); @@ -753,12 +708,12 @@ namespace ts.projectSystem { }); it("non relative module name", () => { - const expectedNonRelativeDirectories = [`${projectLocation}/node_modules`, `${projectLocation}/product`]; + const expectedNonRelativeDirectories = [`${projectRoot}/node_modules`, `${projectRoot}/product`]; const module1Name = "module1"; const module2Name = "module2"; const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`; const { file1, file2, file3, file4 } = getFiles(fileContent); - const { module1, module2 } = getModules(`${projectLocation}/product/node_modules/module1/index.ts`, `${projectLocation}/node_modules/module2/index.ts`); + const { module1, module2 } = getModules(`${projectRoot}/product/node_modules/module1/index.ts`, `${projectRoot}/node_modules/module2/index.ts`); const files = [module1, module2, file1, file2, file3, file4, configFile, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); @@ -768,8 +723,8 @@ namespace ts.projectSystem { getExpectedNonRelativeModuleResolutionTrace(host, file1, module2, module2Name, expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file2, module1, module1Name, getDirectoryPath(file1.path), expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file2, module2, module2Name, getDirectoryPath(file1.path), expectedTrace); - getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module1, module1Name, `${projectLocation}/product`, expectedTrace); - getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module2, module2Name, `${projectLocation}/product`, expectedTrace); + getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module1, module1Name, `${projectRoot}/product`, expectedTrace); + getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module2, module2Name, `${projectRoot}/product`, expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module1, module1Name, getDirectoryPath(file4.path), expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module2, module2Name, getDirectoryPath(file4.path), expectedTrace); verifyTrace(resolutionTrace, expectedTrace); @@ -797,7 +752,7 @@ namespace ts.projectSystem { const file4Name = "../test/file4"; const importModuleContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`; const { file1, file2, file3, file4 } = getFiles(`import "${file2Name}"; import "${file4Name}"; import "${file3Name}"; ${importModuleContent}`, importModuleContent, importModuleContent, importModuleContent); - const { module1, module2 } = getModules(`${projectLocation}/product/node_modules/module1/index.ts`, `${projectLocation}/node_modules/module2/index.ts`); + const { module1, module2 } = getModules(`${projectRoot}/product/node_modules/module1/index.ts`, `${projectRoot}/node_modules/module2/index.ts`); const files = [module1, module2, file1, file2, file3, file4, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); @@ -811,19 +766,19 @@ namespace ts.projectSystem { getExpectedNonRelativeModuleResolutionTrace(host, file1, module2, module2Name, expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file2, module1, module1Name, getDirectoryPath(file1.path), expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file2, module2, module2Name, getDirectoryPath(file1.path), expectedTrace); - getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module1, module1Name, `${projectLocation}/product`, expectedTrace); - getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module2, module2Name, `${projectLocation}/product`, expectedTrace); + getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module1, module1Name, `${projectRoot}/product`, expectedTrace); + getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file4, module2, module2Name, `${projectRoot}/product`, expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module1, module1Name, getDirectoryPath(file4.path), expectedTrace); getExpectedNonRelativeModuleResolutionFromCacheTrace(host, file3, module2, module2Name, getDirectoryPath(file4.path), expectedTrace); verifyTrace(resolutionTrace, expectedTrace); const currentDirectory = getDirectoryPath(file1.path); const watchedFiles = mapDefined(files, f => f === file1 || f.path.indexOf("/node_modules/") !== -1 ? undefined : f.path) - .concat(getConfigFilesToWatch(`${projectLocation}/product/src`)); + .concat(getConfigFilesToWatch(`${projectRoot}/product/src`)); const watchedRecursiveDirectories = getTypeRootsFromLocation(currentDirectory).concat([ - `${currentDirectory}/node_modules`, `${currentDirectory}/feature`, `${projectLocation}/product/${nodeModules}`, - `${projectLocation}/${nodeModules}`, `${projectLocation}/product/test/${nodeModules}`, - `${projectLocation}/product/test/src/${nodeModules}` + `${currentDirectory}/node_modules`, `${currentDirectory}/feature`, `${projectRoot}/product/${nodeModules}`, + `${projectRoot}/${nodeModules}`, `${projectRoot}/product/test/${nodeModules}`, + `${projectRoot}/product/test/src/${nodeModules}` ]); checkWatches(); @@ -852,7 +807,6 @@ namespace ts.projectSystem { }); describe("when watching directories for failed lookup locations in amd resolution", () => { - const projectRoot = "/user/username/projects/project"; const nodeFile: File = { path: `${projectRoot}/src/typings/node.d.ts`, content: ` @@ -928,17 +882,16 @@ export const x = 10;` }); describe("ignores files/folder changes in node_modules that start with '.'", () => { - const projectPath = "/user/username/projects/project"; const npmCacheFile: File = { - path: `${projectPath}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`, + path: `${projectRoot}/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`, content: JSON.stringify({ something: 10 }) }; const file1: File = { - path: `${projectPath}/test.ts`, + path: `${projectRoot}/test.ts`, content: `import { x } from "somemodule";` }; const file2: File = { - path: `${projectPath}/node_modules/somemodule/index.d.ts`, + path: `${projectRoot}/node_modules/somemodule/index.d.ts`, content: `export const x = 10;` }; it("when watching node_modules in inferred project for failed lookup/closed script infos", () => { @@ -957,7 +910,7 @@ export const x = 10;` }); it("when watching node_modules as part of wild card directories in config project", () => { const config: File = { - path: `${projectPath}/tsconfig.json`, + path: `${projectRoot}/tsconfig.json`, content: "{}" }; const files = [libFile, file1, file2, config]; @@ -976,15 +929,15 @@ export const x = 10;` describe("avoid unnecessary invalidation", () => { it("unnecessary lookup invalidation on save", () => { - const expectedNonRelativeDirectories = [`${projectLocation}/node_modules`, `${projectLocation}/src`]; + const expectedNonRelativeDirectories = [`${projectRoot}/node_modules`, `${projectRoot}/src`]; const module1Name = "module1"; const module2Name = "module2"; const fileContent = `import { module1 } from "${module1Name}";import { module2 } from "${module2Name}";`; const file1: File = { - path: `${projectLocation}/src/file1.ts`, + path: `${projectRoot}/src/file1.ts`, content: fileContent }; - const { module1, module2 } = getModules(`${projectLocation}/src/node_modules/module1/index.ts`, `${projectLocation}/node_modules/module2/index.ts`); + const { module1, module2 } = getModules(`${projectRoot}/src/node_modules/module1/index.ts`, `${projectRoot}/node_modules/module2/index.ts`); const files = [module1, module2, file1, configFile, libFile]; const host = createServerHost(files); const resolutionTrace = createHostModuleResolutionTrace(host); diff --git a/src/testRunner/unittests/tsserver/symLinks.ts b/src/testRunner/unittests/tsserver/symLinks.ts index 54b7cb3fab427..4576dc90c0792 100644 --- a/src/testRunner/unittests/tsserver/symLinks.ts +++ b/src/testRunner/unittests/tsserver/symLinks.ts @@ -149,34 +149,16 @@ new C();` const filesInProjectWithResolvedModule = [...filesInProjectWithUnresolvedModule, recongnizerTextDistTypingFile.path]; function verifyErrors(session: TestSession, semanticErrors: protocol.Diagnostic[]) { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [recognizersDateTimeSrcFile.path], - } - }); - - const host = session.testhost; - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: semanticErrors }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: recognizersDateTimeSrcFile.path, - diagnostics: [], + verifyGetErrRequest({ + session, + host: session.testhost, + expected: [{ + file: recognizersDateTimeSrcFile, + syntax: [], + semantic: semanticErrors, + suggestion: [] + }] }); - checkCompleteEvent(session, 2, expectedSequenceId); } function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: ReadonlyMap, nonRecursiveDirectories: string[]) { diff --git a/src/testRunner/unittests/tsserver/syntaxOperations.ts b/src/testRunner/unittests/tsserver/syntaxOperations.ts index d3127355191cf..ec5610b649ec2 100644 --- a/src/testRunner/unittests/tsserver/syntaxOperations.ts +++ b/src/testRunner/unittests/tsserver/syntaxOperations.ts @@ -15,7 +15,6 @@ namespace ts.projectSystem { } it("works when file is removed and added with different content", () => { - const projectRoot = "/user/username/projects/myproject"; const app: File = { path: `${projectRoot}/app.ts`, content: "console.log('Hello world');" diff --git a/src/testRunner/unittests/tsserver/typeReferenceDirectives.ts b/src/testRunner/unittests/tsserver/typeReferenceDirectives.ts index e3ad121e42768..42f977339853a 100644 --- a/src/testRunner/unittests/tsserver/typeReferenceDirectives.ts +++ b/src/testRunner/unittests/tsserver/typeReferenceDirectives.ts @@ -1,8 +1,7 @@ namespace ts.projectSystem { describe("unittests:: tsserver:: typeReferenceDirectives", () => { it("when typeReferenceDirective contains UpperCasePackage", () => { - const projectLocation = "/user/username/projects/myproject"; - const libProjectLocation = `${projectLocation}/lib`; + const libProjectLocation = `${projectRoot}/lib`; const typeLib: File = { path: `${libProjectLocation}/@types/UpperCasePackage/index.d.ts`, content: `declare class BrokenTest { @@ -20,7 +19,7 @@ declare class TestLib { test(): void; }` }; - const testProjectLocation = `${projectLocation}/test`; + const testProjectLocation = `${projectRoot}/test`; const testFile: File = { path: `${testProjectLocation}/test.ts`, content: `class TestClass1 { @@ -58,8 +57,7 @@ declare class TestLib { }); it("when typeReferenceDirective is relative path and in a sibling folder", () => { - const projectRootPath = "/user/username/projects/browser-addon"; - const projectPath = `${projectRootPath}/background`; + const projectPath = `${projectRoot}/background`; const file: File = { path: `${projectPath}/a.ts`, content: "let x = 10;" @@ -75,7 +73,7 @@ declare class TestLib { }) }; const filesystem: File = { - path: `${projectRootPath}/typedefs/filesystem.d.ts`, + path: `${projectRoot}/typedefs/filesystem.d.ts`, content: `interface LocalFileSystem { someProperty: string; }` }; const files = [file, tsconfig, filesystem, libFile]; diff --git a/src/testRunner/unittests/tsserver/typingsInstaller.ts b/src/testRunner/unittests/tsserver/typingsInstaller.ts index 1b7f156057df3..cecc6c4383a5d 100644 --- a/src/testRunner/unittests/tsserver/typingsInstaller.ts +++ b/src/testRunner/unittests/tsserver/typingsInstaller.ts @@ -994,7 +994,6 @@ namespace ts.projectSystem { }); it("should redo resolution that resolved to '.js' file after typings are installed", () => { - const projects = `/user/username/projects`; const file: TestFSWithWatch.File = { path: `${projects}/a/b/app.js`, content: `