1namespace ts.projectSystem { 2 describe("unittests:: tsserver:: with metadata in response", () => { 3 const metadata = "Extra Info"; 4 function verifyOutput(host: TestServerHost, expectedResponse: protocol.Response) { 5 const output = host.getOutput().map(mapOutputToJson); 6 assert.deepEqual(output, [expectedResponse]); 7 host.clearOutput(); 8 } 9 10 function verifyCommandWithMetadata<T extends server.protocol.Request, U = undefined>(session: TestSession, host: TestServerHost, command: Partial<T>, expectedResponseBody: U) { 11 command.seq = session.getSeq(); 12 command.type = "request"; 13 session.onMessage(JSON.stringify(command)); 14 verifyOutput(host, expectedResponseBody ? 15 { seq: 0, type: "response", command: command.command!, request_seq: command.seq, success: true, body: expectedResponseBody, metadata } : 16 { seq: 0, type: "response", command: command.command!, request_seq: command.seq, success: false, message: "No content available." } 17 ); 18 } 19 20 const aTs: File = { path: "/a.ts", content: `class c { prop = "hello"; foo() { return this.prop; } }` }; 21 const tsconfig: File = { 22 path: "/tsconfig.json", 23 content: JSON.stringify({ 24 compilerOptions: { plugins: [{ name: "myplugin" }] } 25 }) 26 }; 27 function createHostWithPlugin(files: readonly File[]) { 28 const host = createServerHost(files); 29 host.require = (_initialPath, moduleName) => { 30 assert.equal(moduleName, "myplugin"); 31 return { 32 module: () => ({ 33 create(info: server.PluginCreateInfo) { 34 const proxy = Harness.LanguageService.makeDefaultProxy(info); 35 proxy.getCompletionsAtPosition = (filename, position, options) => { 36 const result = info.languageService.getCompletionsAtPosition(filename, position, options); 37 if (result) { 38 result.metadata = metadata; 39 } 40 return result; 41 }; 42 return proxy; 43 } 44 }), 45 error: undefined 46 }; 47 }; 48 return host; 49 } 50 51 describe("With completion requests", () => { 52 const completionRequestArgs: protocol.CompletionsRequestArgs = { 53 file: aTs.path, 54 line: 1, 55 offset: aTs.content.indexOf("this.") + 1 + "this.".length 56 }; 57 const expectedCompletionEntries: readonly protocol.CompletionEntry[] = [ 58 { name: "foo", kind: ScriptElementKind.memberFunctionElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority, 59 displayParts: [ 60 { 61 text: "(", 62 kind: "punctuation" 63 },{ 64 text: "method", 65 kind: "text" 66 },{ 67 text: ")", 68 kind: "punctuation" 69 },{ 70 text: " ", 71 kind: "space" 72 },{ 73 text: "c", 74 kind: "className" 75 },{ 76 text: ".", 77 kind: "punctuation" 78 },{ 79 text: "foo", 80 kind: "methodName" 81 },{ 82 text: "(", 83 kind: "punctuation" 84 },{ 85 text: ")", 86 kind: "punctuation" 87 },{ 88 text: ":", 89 kind: "punctuation" 90 },{ 91 text: " ", 92 kind: "space" 93 },{ 94 text: "string", 95 kind: "keyword" 96 } 97 ]}, 98 { name: "prop", kind: ScriptElementKind.memberVariableElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority, 99 displayParts: [ 100 { 101 text: "(", 102 kind: "punctuation" 103 },{ 104 text: "property", 105 kind: "text" 106 },{ 107 text: ")", 108 kind: "punctuation" 109 },{ 110 text: " ", 111 kind: "space" 112 },{ 113 text: "c", 114 kind: "className" 115 },{ 116 text: ".", 117 kind: "punctuation" 118 },{ 119 text: "prop", 120 kind: "propertyName" 121 },{ 122 text: ":", 123 kind: "punctuation" 124 },{ 125 text: " ", 126 kind: "space" 127 },{ 128 text: "string", 129 kind: "keyword" 130 } 131 ] } 132 ]; 133 134 it("can pass through metadata when the command returns array", () => { 135 const host = createHostWithPlugin([aTs, tsconfig]); 136 const session = createSession(host); 137 openFilesForSession([aTs], session); 138 verifyCommandWithMetadata<protocol.CompletionsRequest, readonly protocol.CompletionEntry[]>(session, host, { 139 command: protocol.CommandTypes.Completions, 140 arguments: completionRequestArgs 141 }, expectedCompletionEntries); 142 }); 143 144 it("can pass through metadata when the command returns object", () => { 145 const host = createHostWithPlugin([aTs, tsconfig]); 146 const session = createSession(host); 147 openFilesForSession([aTs], session); 148 verifyCommandWithMetadata<protocol.CompletionsRequest, protocol.CompletionInfo>(session, host, { 149 command: protocol.CommandTypes.CompletionInfo, 150 arguments: completionRequestArgs 151 }, { 152 isGlobalCompletion: false, 153 isMemberCompletion: true, 154 isNewIdentifierLocation: false, 155 optionalReplacementSpan: { 156 start: { line: 1, offset: aTs.content.indexOf("prop;") + 1 }, 157 end: { line: 1, offset: aTs.content.indexOf("prop;") + 1 + "prop".length } 158 }, 159 entries: expectedCompletionEntries 160 }); 161 }); 162 163 it("returns undefined correctly", () => { 164 const aTs: File = { path: "/a.ts", content: `class c { prop = "hello"; foo() { const x = 0; } }` }; 165 const host = createHostWithPlugin([aTs, tsconfig]); 166 const session = createSession(host); 167 openFilesForSession([aTs], session); 168 verifyCommandWithMetadata<protocol.CompletionsRequest>(session, host, { 169 command: protocol.CommandTypes.Completions, 170 arguments: { file: aTs.path, line: 1, offset: aTs.content.indexOf("x") + 1 } 171 }, /*expectedResponseBody*/ undefined); 172 }); 173 }); 174 }); 175} 176