1namespace ts.projectSystem { 2 describe("unittests:: tsserver:: webServer", () => { 3 class TestWorkerSession extends server.WorkerSession { 4 constructor(host: server.ServerHost, webHost: server.HostWithWriteMessage, options: Partial<server.StartSessionOptions>, logger: server.Logger) { 5 super( 6 host, 7 webHost, 8 { 9 globalPlugins: undefined, 10 pluginProbeLocations: undefined, 11 allowLocalPluginLoads: undefined, 12 useSingleInferredProject: true, 13 useInferredProjectPerProjectRoot: false, 14 suppressDiagnosticEvents: false, 15 noGetErrOnBackgroundUpdate: true, 16 syntaxOnly: undefined, 17 serverMode: undefined, 18 ...options 19 }, 20 logger, 21 server.nullCancellationToken, 22 () => emptyArray 23 ); 24 } 25 26 getProjectService() { 27 return this.projectService; 28 } 29 } 30 function setup(logLevel: server.LogLevel | undefined) { 31 const host = createServerHost([libFile], { windowsStyleRoot: "c:/" }); 32 const messages: any[] = []; 33 const webHost: server.WebHost = { 34 readFile: s => host.readFile(s), 35 fileExists: s => host.fileExists(s), 36 writeMessage: s => messages.push(s), 37 }; 38 const webSys = server.createWebSystem(webHost, emptyArray, () => host.getExecutingFilePath()); 39 const logger = logLevel !== undefined ? new server.MainProcessLogger(logLevel, webHost) : nullLogger; 40 const session = new TestWorkerSession(webSys, webHost, { serverMode: LanguageServiceMode.PartialSemantic }, logger); 41 return { getMessages: () => messages, clearMessages: () => messages.length = 0, session }; 42 43 } 44 45 describe("open files are added to inferred project and semantic operations succeed", () => { 46 function verify(logLevel: server.LogLevel | undefined) { 47 const { session, clearMessages, getMessages } = setup(logLevel); 48 const service = session.getProjectService(); 49 const file: File = { 50 path: "^memfs:/sample-folder/large.ts", 51 content: "export const numberConst = 10; export const arrayConst: Array<string> = [];" 52 }; 53 session.executeCommand({ 54 seq: 1, 55 type: "request", 56 command: protocol.CommandTypes.Open, 57 arguments: { 58 file: file.path, 59 fileContent: file.content 60 } 61 }); 62 checkNumberOfProjects(service, { inferredProjects: 1 }); 63 const project = service.inferredProjects[0]; 64 checkProjectActualFiles(project, ["/lib.d.ts", file.path]); // Lib files are rooted 65 verifyQuickInfo(); 66 verifyGotoDefInLib(); 67 68 function verifyQuickInfo() { 69 clearMessages(); 70 const start = protocolFileLocationFromSubstring(file, "numberConst"); 71 session.onMessage({ 72 seq: 2, 73 type: "request", 74 command: protocol.CommandTypes.Quickinfo, 75 arguments: start 76 }); 77 assert.deepEqual(last(getMessages()), { 78 seq: 0, 79 type: "response", 80 command: protocol.CommandTypes.Quickinfo, 81 request_seq: 2, 82 success: true, 83 performanceData: undefined, 84 body: { 85 kind: ScriptElementKind.constElement, 86 kindModifiers: "export", 87 start: { line: start.line, offset: start.offset }, 88 end: { line: start.line, offset: start.offset + "numberConst".length }, 89 displayString: "const numberConst: 10", 90 documentation: "", 91 tags: [] 92 } 93 }); 94 verifyLogger(); 95 } 96 97 function verifyGotoDefInLib() { 98 clearMessages(); 99 const start = protocolFileLocationFromSubstring(file, "Array"); 100 session.onMessage({ 101 seq: 3, 102 type: "request", 103 command: protocol.CommandTypes.DefinitionAndBoundSpan, 104 arguments: start 105 }); 106 assert.deepEqual(last(getMessages()), { 107 seq: 0, 108 type: "response", 109 command: protocol.CommandTypes.DefinitionAndBoundSpan, 110 request_seq: 3, 111 success: true, 112 performanceData: undefined, 113 body: { 114 definitions: [{ 115 file: "/lib.d.ts", 116 ...protocolTextSpanWithContextFromSubstring({ 117 fileText: libFile.content, 118 text: "Array", 119 contextText: "interface Array<T> { length: number; [n: number]: T; }" 120 }) 121 }], 122 textSpan: { 123 start: { line: start.line, offset: start.offset }, 124 end: { line: start.line, offset: start.offset + "Array".length }, 125 } 126 } 127 }); 128 verifyLogger(); 129 } 130 131 function verifyLogger() { 132 const messages = getMessages(); 133 assert.equal(messages.length, logLevel === server.LogLevel.verbose ? 4 : 1, `Expected ${JSON.stringify(messages)}`); 134 if (logLevel === server.LogLevel.verbose) { 135 verifyLogMessages(messages[0], "info"); 136 verifyLogMessages(messages[1], "perf"); 137 verifyLogMessages(messages[2], "info"); 138 } 139 clearMessages(); 140 } 141 142 function verifyLogMessages(actual: any, expectedLevel: server.MessageLogLevel) { 143 assert.equal(actual.type, "log"); 144 assert.equal(actual.level, expectedLevel); 145 } 146 } 147 148 it("with logging enabled", () => { 149 verify(server.LogLevel.verbose); 150 }); 151 152 it("with logging disabled", () => { 153 verify(/*logLevel*/ undefined); 154 }); 155 }); 156 }); 157} 158