• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    describe("unittests:: tsserver:: Semantic operations on PartialSemantic server", () => {
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, { serverMode: LanguageServiceMode.PartialSemantic, useSingleInferredProject: true });
30            return { host, session, file1, file2, file3, something, configFile };
31        }
32
33        it("open files are added to inferred project even if config file is present and semantic operations succeed", () => {
34            const { host, session, file1, file2 } = setup();
35            const service = session.getProjectService();
36            openFilesForSession([file1], session);
37            checkNumberOfProjects(service, { inferredProjects: 1 });
38            const project = service.inferredProjects[0];
39            checkProjectActualFiles(project, [libFile.path, file1.path]); // no imports are resolved
40            verifyCompletions();
41
42            openFilesForSession([file2], session);
43            checkNumberOfProjects(service, { inferredProjects: 1 });
44            checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
45            verifyCompletions();
46
47            function verifyCompletions() {
48                assert.isTrue(project.languageServiceEnabled);
49                checkWatchedFiles(host, emptyArray);
50                checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
51                checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
52                const response = session.executeCommandSeq<protocol.CompletionsRequest>({
53                    command: protocol.CommandTypes.Completions,
54                    arguments: protocolFileLocationFromSubstring(file1, "prop", { index: 1 })
55                }).response as protocol.CompletionEntry[];
56                const displayPartsProp = [
57                    {
58                        text: "(",
59                        kind: "punctuation"
60                    },{
61                        text: "property",
62                        kind: "text"
63                    },{
64                        text: ")",
65                        kind: "punctuation"
66                    },{
67                        text: " ",
68                        kind: "space"
69                    },{
70                        text: "c",
71                        kind: "className"
72                    },{
73                        text: ".",
74                        kind: "punctuation"
75                    },{
76                        text: "prop",
77                        kind: "propertyName"
78                    },{
79                        text: ":",
80                        kind: "punctuation"
81                    },{
82                        text: " ",
83                        kind: "space"
84                    },{
85                        text: "string",
86                        kind: "keyword"
87                    }
88                ];
89                const displayPartsFoo = [
90                    {
91                        text: "(",
92                        kind: "punctuation"
93                    },{
94                        text: "method",
95                        kind: "text"
96                    },{
97                        text: ")",
98                        kind: "punctuation"
99                    },{
100                        text: " ",
101                        kind: "space"
102                    },{
103                        text: "c",
104                        kind: "className"
105                    },{
106                        text: ".",
107                        kind: "punctuation"
108                    },{
109                        text: "foo",
110                        kind: "methodName"
111                    },{
112                        text: "(",
113                        kind: "punctuation"
114                    },{
115                        text: ")",
116                        kind: "punctuation"
117                    },{
118                        text: ":",
119                        kind: "punctuation"
120                    },{
121                        text: " ",
122                        kind: "space"
123                    },{
124                        text: "string",
125                        kind: "keyword"
126                    }
127                ];
128                assert.deepEqual(response, [
129                    completionEntry("foo", ScriptElementKind.memberFunctionElement, displayPartsFoo),
130                    completionEntry("prop", ScriptElementKind.memberVariableElement, displayPartsProp),
131                ]);
132            }
133
134            function completionEntry(name: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[]): protocol.CompletionEntry {
135                return {
136                    name,
137                    kind,
138                    kindModifiers: "",
139                    sortText: Completions.SortText.LocationPriority,
140                    hasAction: undefined,
141                    insertText: undefined,
142                    isPackageJsonImport: undefined,
143                    isRecommended: undefined,
144                    replacementSpan: undefined,
145                    source: undefined,
146                    jsDoc: undefined,
147                    displayParts
148                };
149            }
150        });
151
152        it("throws on unsupported commands", () => {
153            const { session, file1 } = setup();
154            const service = session.getProjectService();
155            openFilesForSession([file1], session);
156            let hasException = false;
157            const request: protocol.SemanticDiagnosticsSyncRequest = {
158                type: "request",
159                seq: 1,
160                command: protocol.CommandTypes.SemanticDiagnosticsSync,
161                arguments: { file: file1.path }
162            };
163            try {
164                session.executeCommand(request);
165            }
166            catch (e) {
167                assert.equal(e.message, `Request: semanticDiagnosticsSync not allowed in LanguageServiceMode.PartialSemantic`);
168                hasException = true;
169            }
170            assert.isTrue(hasException);
171
172            hasException = false;
173            const project = service.inferredProjects[0];
174            try {
175                project.getLanguageService().getSemanticDiagnostics(file1.path);
176            }
177            catch (e) {
178                assert.equal(e.message, `LanguageService Operation: getSemanticDiagnostics not allowed in LanguageServiceMode.PartialSemantic`);
179                hasException = true;
180            }
181            assert.isTrue(hasException);
182        });
183
184        it("should not include auto type reference directives", () => {
185            const { host, session, file1 } = setup();
186            const atTypes: File = {
187                path: `/node_modules/@types/somemodule/index.d.ts`,
188                content: "export const something = 10;"
189            };
190            host.ensureFileOrFolder(atTypes);
191            const service = session.getProjectService();
192            openFilesForSession([file1], session);
193            checkNumberOfProjects(service, { inferredProjects: 1 });
194            const project = service.inferredProjects[0];
195            checkProjectActualFiles(project, [libFile.path, file1.path]); // Should not contain atTypes
196        });
197
198        it("should not include referenced files from unopened files", () => {
199            const file1: File = {
200                path: `${tscWatch.projectRoot}/a.ts`,
201                content: `///<reference path="b.ts"/>
202///<reference path="${tscWatch.projectRoot}/node_modules/something/index.d.ts"/>
203function fooA() { }`
204            };
205            const file2: File = {
206                path: `${tscWatch.projectRoot}/b.ts`,
207                content: `///<reference path="./c.ts"/>
208///<reference path="${tscWatch.projectRoot}/node_modules/something/index.d.ts"/>
209function fooB() { }`
210            };
211            const file3: File = {
212                path: `${tscWatch.projectRoot}/c.ts`,
213                content: `function fooC() { }`
214            };
215            const something: File = {
216                path: `${tscWatch.projectRoot}/node_modules/something/index.d.ts`,
217                content: "function something() {}"
218            };
219            const configFile: File = {
220                path: `${tscWatch.projectRoot}/tsconfig.json`,
221                content: "{}"
222            };
223            const host = createServerHost([file1, file2, file3, something, libFile, configFile]);
224            const session = createSession(host, { serverMode: LanguageServiceMode.PartialSemantic, useSingleInferredProject: true });
225            const service = session.getProjectService();
226            openFilesForSession([file1], session);
227            checkNumberOfProjects(service, { inferredProjects: 1 });
228            const project = service.inferredProjects[0];
229            checkProjectActualFiles(project, [libFile.path, file1.path]); // no resolve
230        });
231
232        it("should not crash when external module name resolution is reused", () => {
233            const { session, file1, file2, file3 } = setup();
234            const service = session.getProjectService();
235            openFilesForSession([file1], session);
236            checkNumberOfProjects(service, { inferredProjects: 1 });
237            const project = service.inferredProjects[0];
238            checkProjectActualFiles(project, [libFile.path, file1.path]);
239
240            // Close the file that contains non relative external module name and open some file that doesnt have non relative external module import
241            closeFilesForSession([file1], session);
242            openFilesForSession([file3], session);
243            checkProjectActualFiles(project, [libFile.path, file3.path]);
244
245            // Open file with non relative external module name
246            openFilesForSession([file2], session);
247            checkProjectActualFiles(project, [libFile.path, file2.path, file3.path]);
248        });
249
250        it("should not create autoImportProvider or handle package jsons", () => {
251            const angularFormsDts: File = {
252                path: "/node_modules/@angular/forms/forms.d.ts",
253                content: "export declare class PatternValidator {}",
254            };
255            const angularFormsPackageJson: File = {
256                path: "/node_modules/@angular/forms/package.json",
257                content: `{ "name": "@angular/forms", "typings": "./forms.d.ts" }`,
258            };
259            const tsconfig: File = {
260                path: "/tsconfig.json",
261                content: `{ "compilerOptions": { "module": "commonjs" } }`,
262            };
263            const packageJson: File = {
264                path: "/package.json",
265                content: `{ "dependencies": { "@angular/forms": "*", "@angular/core": "*" } }`
266            };
267            const indexTs: File = {
268                path: "/index.ts",
269                content: ""
270            };
271            const host = createServerHost([angularFormsDts, angularFormsPackageJson, tsconfig, packageJson, indexTs, libFile]);
272            const session = createSession(host, { serverMode: LanguageServiceMode.PartialSemantic, useSingleInferredProject: true });
273            const service = session.getProjectService();
274            openFilesForSession([indexTs], session);
275            const project = service.inferredProjects[0];
276            assert.isFalse(project.autoImportProviderHost);
277            assert.isUndefined(project.getPackageJsonAutoImportProvider());
278            assert.deepEqual(project.getPackageJsonsForAutoImport(), emptyArray);
279        });
280    });
281}
282