• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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