Skip to content

Commit 96c7370

Browse files
committed
More refactoring for resloutionCache and project errors
1 parent f6ac949 commit 96c7370

File tree

4 files changed

+706
-734
lines changed

4 files changed

+706
-734
lines changed

src/testRunner/unittests/projectErrors.ts

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,4 +563,287 @@ namespace ts.projectSystem {
563563
assert.isTrue(diagsAfterUpdate.length === 0);
564564
});
565565
});
566+
567+
describe("tsserver:: Project Errors for Configure file diagnostics events", () => {
568+
function getUnknownCompilerOptionDiagnostic(configFile: File, prop: string): ConfigFileDiagnostic {
569+
const d = Diagnostics.Unknown_compiler_option_0;
570+
const start = configFile.content.indexOf(prop) - 1; // start at "prop"
571+
return {
572+
fileName: configFile.path,
573+
start,
574+
length: prop.length + 2,
575+
messageText: formatStringFromArgs(d.message, [prop]),
576+
category: d.category,
577+
code: d.code,
578+
reportsUnnecessary: undefined
579+
};
580+
}
581+
582+
function getFileNotFoundDiagnostic(configFile: File, relativeFileName: string): ConfigFileDiagnostic {
583+
const findString = `{"path":"./${relativeFileName}"}`;
584+
const d = Diagnostics.File_0_not_found;
585+
const start = configFile.content.indexOf(findString);
586+
return {
587+
fileName: configFile.path,
588+
start,
589+
length: findString.length,
590+
messageText: formatStringFromArgs(d.message, [`${getDirectoryPath(configFile.path)}/${relativeFileName}`]),
591+
category: d.category,
592+
code: d.code,
593+
reportsUnnecessary: undefined
594+
};
595+
}
596+
597+
it("are generated when the config file has errors", () => {
598+
const file: File = {
599+
path: "/a/b/app.ts",
600+
content: "let x = 10"
601+
};
602+
const configFile: File = {
603+
path: "/a/b/tsconfig.json",
604+
content: `{
605+
"compilerOptions": {
606+
"foo": "bar",
607+
"allowJS": true
608+
}
609+
}`
610+
};
611+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
612+
openFilesForSession([file], serverEventManager.session);
613+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
614+
getUnknownCompilerOptionDiagnostic(configFile, "foo"),
615+
getUnknownCompilerOptionDiagnostic(configFile, "allowJS")
616+
]);
617+
});
618+
619+
it("are generated when the config file doesn't have errors", () => {
620+
const file: File = {
621+
path: "/a/b/app.ts",
622+
content: "let x = 10"
623+
};
624+
const configFile: File = {
625+
path: "/a/b/tsconfig.json",
626+
content: `{
627+
"compilerOptions": {}
628+
}`
629+
};
630+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
631+
openFilesForSession([file], serverEventManager.session);
632+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
633+
});
634+
635+
it("are generated when the config file changes", () => {
636+
const file: File = {
637+
path: "/a/b/app.ts",
638+
content: "let x = 10"
639+
};
640+
const configFile = {
641+
path: "/a/b/tsconfig.json",
642+
content: `{
643+
"compilerOptions": {}
644+
}`
645+
};
646+
647+
const files = [file, libFile, configFile];
648+
const serverEventManager = new TestServerEventManager(files);
649+
openFilesForSession([file], serverEventManager.session);
650+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, emptyArray);
651+
652+
configFile.content = `{
653+
"compilerOptions": {
654+
"haha": 123
655+
}
656+
}`;
657+
serverEventManager.host.reloadFS(files);
658+
serverEventManager.host.runQueuedTimeoutCallbacks();
659+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, [
660+
getUnknownCompilerOptionDiagnostic(configFile, "haha")
661+
]);
662+
663+
configFile.content = `{
664+
"compilerOptions": {}
665+
}`;
666+
serverEventManager.host.reloadFS(files);
667+
serverEventManager.host.runQueuedTimeoutCallbacks();
668+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path, emptyArray);
669+
});
670+
671+
it("are not generated when the config file does not include file opened and config file has errors", () => {
672+
const file: File = {
673+
path: "/a/b/app.ts",
674+
content: "let x = 10"
675+
};
676+
const file2: File = {
677+
path: "/a/b/test.ts",
678+
content: "let x = 10"
679+
};
680+
const configFile: File = {
681+
path: "/a/b/tsconfig.json",
682+
content: `{
683+
"compilerOptions": {
684+
"foo": "bar",
685+
"allowJS": true
686+
},
687+
"files": ["app.ts"]
688+
}`
689+
};
690+
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
691+
openFilesForSession([file2], serverEventManager.session);
692+
serverEventManager.hasZeroEvent("configFileDiag");
693+
});
694+
695+
it("are not generated when the config file has errors but suppressDiagnosticEvents is true", () => {
696+
const file: File = {
697+
path: "/a/b/app.ts",
698+
content: "let x = 10"
699+
};
700+
const configFile: File = {
701+
path: "/a/b/tsconfig.json",
702+
content: `{
703+
"compilerOptions": {
704+
"foo": "bar",
705+
"allowJS": true
706+
}
707+
}`
708+
};
709+
const serverEventManager = new TestServerEventManager([file, libFile, configFile], /*suppressDiagnosticEvents*/ true);
710+
openFilesForSession([file], serverEventManager.session);
711+
serverEventManager.hasZeroEvent("configFileDiag");
712+
});
713+
714+
it("are not generated when the config file does not include file opened and doesnt contain any errors", () => {
715+
const file: File = {
716+
path: "/a/b/app.ts",
717+
content: "let x = 10"
718+
};
719+
const file2: File = {
720+
path: "/a/b/test.ts",
721+
content: "let x = 10"
722+
};
723+
const configFile: File = {
724+
path: "/a/b/tsconfig.json",
725+
content: `{
726+
"files": ["app.ts"]
727+
}`
728+
};
729+
730+
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
731+
openFilesForSession([file2], serverEventManager.session);
732+
serverEventManager.hasZeroEvent("configFileDiag");
733+
});
734+
735+
it("contains the project reference errors", () => {
736+
const file: File = {
737+
path: "/a/b/app.ts",
738+
content: "let x = 10"
739+
};
740+
const noSuchTsconfig = "no-such-tsconfig.json";
741+
const configFile: File = {
742+
path: "/a/b/tsconfig.json",
743+
content: `{
744+
"files": ["app.ts"],
745+
"references": [{"path":"./${noSuchTsconfig}"}]
746+
}`
747+
};
748+
749+
const serverEventManager = new TestServerEventManager([file, libFile, configFile]);
750+
openFilesForSession([file], serverEventManager.session);
751+
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path, [
752+
getFileNotFoundDiagnostic(configFile, noSuchTsconfig)
753+
]);
754+
});
755+
});
756+
757+
describe("tsserver:: Project Errors reports Options Diagnostic locations correctly with changes in configFile contents", () => {
758+
it("when options change", () => {
759+
const file = {
760+
path: "/a/b/app.ts",
761+
content: "let x = 10"
762+
};
763+
const configFileContentBeforeComment = `{`;
764+
const configFileContentComment = `
765+
// comment`;
766+
const configFileContentAfterComment = `
767+
"compilerOptions": {
768+
"allowJs": true,
769+
"declaration": true
770+
}
771+
}`;
772+
const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment;
773+
const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment;
774+
775+
const configFile = {
776+
path: "/a/b/tsconfig.json",
777+
content: configFileContentWithComment
778+
};
779+
const host = createServerHost([file, libFile, configFile]);
780+
const session = createSession(host);
781+
openFilesForSession([file], session);
782+
783+
const projectService = session.getProjectService();
784+
checkNumberOfProjects(projectService, { configuredProjects: 1 });
785+
const projectName = configuredProjectAt(projectService, 0).getProjectName();
786+
787+
const diags = session.executeCommand(<server.protocol.SemanticDiagnosticsSyncRequest>{
788+
type: "request",
789+
command: server.CommandNames.SemanticDiagnosticsSync,
790+
seq: 2,
791+
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
792+
}).response as ReadonlyArray<server.protocol.DiagnosticWithLinePosition>;
793+
assert.isTrue(diags.length === 2);
794+
795+
configFile.content = configFileContentWithoutCommentLine;
796+
host.reloadFS([file, configFile]);
797+
798+
const diagsAfterEdit = session.executeCommand(<server.protocol.SemanticDiagnosticsSyncRequest>{
799+
type: "request",
800+
command: server.CommandNames.SemanticDiagnosticsSync,
801+
seq: 2,
802+
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
803+
}).response as ReadonlyArray<server.protocol.DiagnosticWithLinePosition>;
804+
assert.isTrue(diagsAfterEdit.length === 2);
805+
806+
verifyDiagnostic(diags[0], diagsAfterEdit[0]);
807+
verifyDiagnostic(diags[1], diagsAfterEdit[1]);
808+
809+
function verifyDiagnostic(beforeEditDiag: server.protocol.DiagnosticWithLinePosition, afterEditDiag: server.protocol.DiagnosticWithLinePosition) {
810+
assert.equal(beforeEditDiag.message, afterEditDiag.message);
811+
assert.equal(beforeEditDiag.code, afterEditDiag.code);
812+
assert.equal(beforeEditDiag.category, afterEditDiag.category);
813+
assert.equal(beforeEditDiag.startLocation.line, afterEditDiag.startLocation.line + 1);
814+
assert.equal(beforeEditDiag.startLocation.offset, afterEditDiag.startLocation.offset);
815+
assert.equal(beforeEditDiag.endLocation.line, afterEditDiag.endLocation.line + 1);
816+
assert.equal(beforeEditDiag.endLocation.offset, afterEditDiag.endLocation.offset);
817+
}
818+
});
819+
});
820+
821+
describe("tsserver:: Project Errors with config file change", () => {
822+
it("Updates diagnostics when '--noUnusedLabels' changes", () => {
823+
const aTs: File = { path: "/a.ts", content: "label: while (1) {}" };
824+
const options = (allowUnusedLabels: boolean) => `{ "compilerOptions": { "allowUnusedLabels": ${allowUnusedLabels} } }`;
825+
const tsconfig: File = { path: "/tsconfig.json", content: options(/*allowUnusedLabels*/ true) };
826+
827+
const host = createServerHost([aTs, tsconfig]);
828+
const session = createSession(host);
829+
openFilesForSession([aTs], session);
830+
831+
host.modifyFile(tsconfig.path, options(/*allowUnusedLabels*/ false));
832+
host.runQueuedTimeoutCallbacks();
833+
834+
const response = executeSessionRequest<protocol.SemanticDiagnosticsSyncRequest, protocol.SemanticDiagnosticsSyncResponse>(session, protocol.CommandTypes.SemanticDiagnosticsSync, { file: aTs.path }) as protocol.Diagnostic[] | undefined;
835+
assert.deepEqual<protocol.Diagnostic[] | undefined>(response, [
836+
{
837+
start: { line: 1, offset: 1 },
838+
end: { line: 1, offset: 1 + "label".length },
839+
text: "Unused label.",
840+
category: "error",
841+
code: Diagnostics.Unused_label.code,
842+
relatedInformation: undefined,
843+
reportsUnnecessary: true,
844+
source: undefined,
845+
},
846+
]);
847+
});
848+
});
566849
}

0 commit comments

Comments
 (0)