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