1namespace ts.projectSystem { 2 describe("unittests:: tsserver:: Semantic operations on partialSemanticServer", () => { 3 function setup() { 4 const file1: File = { 5 path: `${tscWatch.projectRoot}/a.ts`, 6 content: `import { y, cc } from "./b"; 7import { something } from "something"; 8class c { prop = "hello"; foo() { return this.prop; } }` 9 }; 10 const file2: File = { 11 path: `${tscWatch.projectRoot}/b.ts`, 12 content: `export { cc } from "./c"; 13import { something } from "something"; 14 export const y = 10;` 15 }; 16 const file3: File = { 17 path: `${tscWatch.projectRoot}/c.ts`, 18 content: `export const cc = 10;` 19 }; 20 const something: File = { 21 path: `${tscWatch.projectRoot}/node_modules/something/index.d.ts`, 22 content: "export const something = 10;" 23 }; 24 const configFile: File = { 25 path: `${tscWatch.projectRoot}/tsconfig.json`, 26 content: "{}" 27 }; 28 const host = createServerHost([file1, file2, file3, something, libFile, configFile]); 29 const session = createSession(host, { 30 serverMode: LanguageServiceMode.PartialSemantic, 31 useSingleInferredProject: true, 32 logger: createLoggerWithInMemoryLogs(host), 33 }); 34 return { host, session, file1, file2, file3, something, configFile }; 35 } 36 37 it("open files are added to inferred project even if config file is present and semantic operations succeed", () => { 38 const { session, file1, file2 } = setup(); 39 const service = session.getProjectService(); 40 openFilesForSession([file1], session); 41 const project = service.inferredProjects[0]; 42 verifyCompletions(); 43 44 openFilesForSession([file2], session); 45 checkNumberOfProjects(service, { inferredProjects: 1 }); 46 checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]); 47 verifyCompletions(); 48 49 baselineTsserverLogs("partialSemanticServer", "files are added to inferred project", session); 50 51 function verifyCompletions() { 52 session.executeCommandSeq<protocol.CompletionsRequest>({ 53 command: protocol.CommandTypes.Completions, 54 arguments: protocolFileLocationFromSubstring(file1, "prop", { index: 1 }) 55 }); 56 } 57 }); 58 59 it("throws on unsupported commands", () => { 60 const { session, file1 } = setup(); 61 const service = session.getProjectService(); 62 openFilesForSession([file1], session); 63 const request: protocol.SemanticDiagnosticsSyncRequest = { 64 type: "request", 65 seq: 1, 66 command: protocol.CommandTypes.SemanticDiagnosticsSync, 67 arguments: { file: file1.path } 68 }; 69 try { 70 session.executeCommand(request); 71 } 72 catch (e) { 73 session.logger.info(e.message); 74 } 75 76 const project = service.inferredProjects[0]; 77 try { 78 project.getLanguageService().getSemanticDiagnostics(file1.path); 79 } 80 catch (e) { 81 session.logger.info(e.message); 82 } 83 baselineTsserverLogs("partialSemanticServer", "throws unsupported commands", session); 84 }); 85 86 it("allows syntactic diagnostic commands", () => { 87 const file1: File = { 88 path: `${tscWatch.projectRoot}/a.ts`, 89 content: `if (a < (b + c) { }` 90 }; 91 const configFile: File = { 92 path: `${tscWatch.projectRoot}/tsconfig.json`, 93 content: `{}` 94 }; 95 const expectedErrorMessage = "')' expected."; 96 97 const host = createServerHost([file1, libFile, configFile]); 98 const session = createSession(host, { 99 serverMode: LanguageServiceMode.PartialSemantic, 100 useSingleInferredProject: true, 101 logger: createLoggerWithInMemoryLogs(host) 102 }); 103 104 const service = session.getProjectService(); 105 openFilesForSession([file1], session); 106 const request: protocol.SyntacticDiagnosticsSyncRequest = { 107 type: "request", 108 seq: 1, 109 command: protocol.CommandTypes.SyntacticDiagnosticsSync, 110 arguments: { file: file1.path } 111 }; 112 const response = session.executeCommandSeq(request).response as protocol.SyntacticDiagnosticsSyncResponse["body"]; 113 assert.isDefined(response); 114 assert.equal(response!.length, 1); 115 assert.equal((response![0] as protocol.Diagnostic).text, expectedErrorMessage); 116 117 const project = service.inferredProjects[0]; 118 const diagnostics = project.getLanguageService().getSyntacticDiagnostics(file1.path); 119 assert.isTrue(diagnostics.length === 1); 120 assert.equal(diagnostics[0].messageText, expectedErrorMessage); 121 122 verifyGetErrRequest({ session, host, files: [file1], skip: [{ semantic: true, suggestion: true }] }); 123 baselineTsserverLogs("partialSemanticServer", "syntactic diagnostics are returned with no error", session); 124 }); 125 126 it("should not include auto type reference directives", () => { 127 const { host, session, file1 } = setup(); 128 const atTypes: File = { 129 path: `/node_modules/@types/somemodule/index.d.ts`, 130 content: "export const something = 10;" 131 }; 132 host.ensureFileOrFolder(atTypes); 133 openFilesForSession([file1], session); 134 baselineTsserverLogs("partialSemanticServer", "should not include auto type reference directives", session); 135 }); 136 137 it("should not include referenced files from unopened files", () => { 138 const file1: File = { 139 path: `${tscWatch.projectRoot}/a.ts`, 140 content: `///<reference path="b.ts"/> 141///<reference path="${tscWatch.projectRoot}/node_modules/something/index.d.ts"/> 142function fooA() { }` 143 }; 144 const file2: File = { 145 path: `${tscWatch.projectRoot}/b.ts`, 146 content: `///<reference path="./c.ts"/> 147///<reference path="${tscWatch.projectRoot}/node_modules/something/index.d.ts"/> 148function fooB() { }` 149 }; 150 const file3: File = { 151 path: `${tscWatch.projectRoot}/c.ts`, 152 content: `function fooC() { }` 153 }; 154 const something: File = { 155 path: `${tscWatch.projectRoot}/node_modules/something/index.d.ts`, 156 content: "function something() {}" 157 }; 158 const configFile: File = { 159 path: `${tscWatch.projectRoot}/tsconfig.json`, 160 content: "{}" 161 }; 162 const host = createServerHost([file1, file2, file3, something, libFile, configFile]); 163 const session = createSession(host, { 164 serverMode: LanguageServiceMode.PartialSemantic, 165 useSingleInferredProject: true, 166 logger: createLoggerWithInMemoryLogs(host), 167 }); 168 openFilesForSession([file1], session); 169 baselineTsserverLogs("partialSemanticServer", "should not include referenced files from unopened files", session); 170 }); 171 172 it("should not crash when external module name resolution is reused", () => { 173 const { session, file1, file2, file3 } = setup(); 174 openFilesForSession([file1], session); 175 176 // Close the file that contains non relative external module name and open some file that doesnt have non relative external module import 177 closeFilesForSession([file1], session); 178 openFilesForSession([file3], session); 179 180 // Open file with non relative external module name 181 openFilesForSession([file2], session); 182 baselineTsserverLogs("partialSemanticServer", "should not crash when external module name resolution is reused", session); 183 }); 184 185 it("should not create autoImportProvider or handle package jsons", () => { 186 const angularFormsDts: File = { 187 path: "/node_modules/@angular/forms/forms.d.ts", 188 content: "export declare class PatternValidator {}", 189 }; 190 const angularFormsPackageJson: File = { 191 path: "/node_modules/@angular/forms/package.json", 192 content: `{ "name": "@angular/forms", "typings": "./forms.d.ts" }`, 193 }; 194 const tsconfig: File = { 195 path: "/tsconfig.json", 196 content: `{ "compilerOptions": { "module": "commonjs" } }`, 197 }; 198 const packageJson: File = { 199 path: "/package.json", 200 content: `{ "dependencies": { "@angular/forms": "*", "@angular/core": "*" } }` 201 }; 202 const indexTs: File = { 203 path: "/index.ts", 204 content: "" 205 }; 206 const host = createServerHost([angularFormsDts, angularFormsPackageJson, tsconfig, packageJson, indexTs, libFile]); 207 const session = createSession(host, { serverMode: LanguageServiceMode.PartialSemantic, useSingleInferredProject: true }); 208 const service = session.getProjectService(); 209 openFilesForSession([indexTs], session); 210 const project = service.inferredProjects[0]; 211 assert.isFalse(project.autoImportProviderHost); 212 assert.isUndefined(project.getPackageJsonAutoImportProvider()); 213 assert.deepEqual(project.getPackageJsonsForAutoImport(), emptyArray); 214 }); 215 216 it("should support go-to-definition on module specifiers", () => { 217 const { session, file1 } = setup(); 218 openFilesForSession([file1], session); 219 session.executeCommandSeq<protocol.DefinitionAndBoundSpanRequest>({ 220 command: protocol.CommandTypes.DefinitionAndBoundSpan, 221 arguments: protocolFileLocationFromSubstring(file1, `"./b"`) 222 }); 223 baselineTsserverLogs("partialSemanticServer", "should support go-to-definition on module specifiers", session); 224 }); 225 }); 226} 227