diff --git a/src/testRunner/unittests/tsserver/projectErrors.ts b/src/testRunner/unittests/tsserver/projectErrors.ts index a8c846f2cf..f5c28eb7a7 100644 --- a/src/testRunner/unittests/tsserver/projectErrors.ts +++ b/src/testRunner/unittests/tsserver/projectErrors.ts @@ -292,42 +292,21 @@ namespace ts.projectSystem { // Since this is not js project so no typings are queued host.checkTimeoutQueueLength(0); - const newTimeoutId = host.getNextTimeoutId(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [untitledFile] - } - }); - host.checkTimeoutQueueLength(1); - - // Run the last one = get error request - host.runQueuedTimeoutCallbacks(newTimeoutId); - - assert.isFalse(hasError()); - host.checkTimeoutQueueLength(0); - checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - assert.isFalse(hasError()); const errorOffset = fileContent.indexOf(refPathNotFound1) + 1; - checkErrorMessage(session, "semanticDiag", { - file: untitledFile, - diagnostics: [ - createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), - createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") - ] + verifyGetErrRequest({ + session, + host, + expected: [{ + file: untitledFile, + syntax: [], + semantic: [ + createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), + createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") + ], + suggestion: [] + }], + onErrEvent: () => assert.isFalse(hasError()) }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - assert.isFalse(hasError()); - checkErrorMessage(session, "suggestionDiag", { file: untitledFile, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } it("has projectRoot", () => { @@ -371,27 +350,16 @@ namespace ts.projectSystem { verifyErrorsInApp(); function verifyErrorsInApp() { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [app.path] - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file: app, + syntax: [], + semantic: [], + suggestion: [] + }], }); - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: app.path, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); @@ -450,32 +418,12 @@ namespace ts.projectSystem { checkErrors([serverUtilities.path, app.path]); function checkErrors(openFiles: [string, string]) { - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - delay: 0, - files: openFiles - } + verifyGetErrRequest({ + session, + host, + expected: openFiles.map(file => ({ file, syntax: [], semantic: [], suggestion: [] })), + existingTimeouts: 2 }); - - for (const openFile of openFiles) { - session.clearMessages(); - host.checkTimeoutQueueLength(3); - host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); - - checkErrorMessage(session, "syntaxDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: openFile, diagnostics: [] }); - } - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); @@ -530,36 +478,19 @@ declare module '@custom/plugin' { function checkErrors() { host.checkTimeoutQueueLength(0); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [aFile.path], - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file: aFile, + syntax: [], + semantic: [], + suggestion: [ + createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true), + createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true) + ] + }] }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: aFile.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: aFile.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: aFile.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true), - createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true) - ], - }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); } }); }); diff --git a/src/testRunner/unittests/tsserver/projectReferenceErrors.ts b/src/testRunner/unittests/tsserver/projectReferenceErrors.ts index 2802b8e673..e6e7126357 100644 --- a/src/testRunner/unittests/tsserver/projectReferenceErrors.ts +++ b/src/testRunner/unittests/tsserver/projectReferenceErrors.ts @@ -1,66 +1,100 @@ namespace ts.projectSystem { + export interface GetErrDiagnostics { + file: string | File; + syntax?: protocol.Diagnostic[]; + semantic?: protocol.Diagnostic[]; + suggestion?: protocol.Diagnostic[]; + } + export interface VerifyGetErrRequestBase { + session: TestSession; + host: TestServerHost; + onErrEvent?: () => void; + existingTimeouts?: number; + } + export interface VerifyGetErrRequest extends VerifyGetErrRequestBase { + expected: readonly GetErrDiagnostics[]; + } + export function verifyGetErrRequest(request: VerifyGetErrRequest) { + const { session, expected } = request; + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + arguments: { + delay: 0, + files: expected.map(f => filePath(f.file)) + } + }); + checkAllErrors({ ...request, expectedSequenceId }); + } + + export interface CheckAllErrors extends VerifyGetErrRequest { + expectedSequenceId: number; + } + function checkAllErrors({ expected, expectedSequenceId, ...rest }: CheckAllErrors) { + for (let i = 0; i < expected.length; i++) { + checkErrorsInFile({ + ...rest, + expected: expected[i], + expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined, + }); + } + } + + function filePath(file: string | File) { + return isString(file) ? file : file.path; + } + interface CheckErrorsInFile extends VerifyGetErrRequestBase { + expected: GetErrDiagnostics; + expectedSequenceId?: number; + } + function checkErrorsInFile({ + session, host, onErrEvent, existingTimeouts, expectedSequenceId, + expected: { file, syntax, semantic, suggestion }, + }: CheckErrorsInFile) { + onErrEvent = onErrEvent || noop; + if (existingTimeouts !== undefined) { + host.checkTimeoutQueueLength(existingTimeouts + 1); + host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); + } + else { + host.checkTimeoutQueueLengthAndRun(1); + } + if (syntax) { + onErrEvent(); + checkErrorMessage(session, "syntaxDiag", { file: filePath(file), diagnostics: syntax }); + } + if (semantic) { + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + onErrEvent(); + checkErrorMessage(session, "semanticDiag", { file: filePath(file), diagnostics: semantic }); + } + if (suggestion) { + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + onErrEvent(); + checkErrorMessage(session, "suggestionDiag", { file: filePath(file), diagnostics: suggestion }); + } + if (expectedSequenceId !== undefined) { + checkCompleteEvent(session, syntax || semantic || suggestion ? 2 : 1, expectedSequenceId); + } + session.clearMessages(); + } + describe("unittests:: tsserver:: with project references and error reporting", () => { const dependecyLocation = `${projectRoot}/dependency`; const usageLocation = `${projectRoot}/usage`; - interface CheckErrorsInFile { - session: TestSession; - host: TestServerHost; - expected: GetErrDiagnostics; - expectedSequenceId?: number; - } - function checkErrorsInFile({ session, host, expected: { file, syntax, semantic, suggestion }, expectedSequenceId }: CheckErrorsInFile) { - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: syntax }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: semantic }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: file.path, diagnostics: suggestion }); - if (expectedSequenceId !== undefined) { - checkCompleteEvent(session, 2, expectedSequenceId); - } - session.clearMessages(); - } - - interface CheckAllErrors { - session: TestSession; - host: TestServerHost; - expected: readonly GetErrDiagnostics[]; - expectedSequenceId: number; - } - function checkAllErrors({ session, host, expected, expectedSequenceId }: CheckAllErrors) { - for (let i = 0; i < expected.length; i++) { - checkErrorsInFile({ - session, - host, - expected: expected[i], - expectedSequenceId: i === expected.length - 1 ? expectedSequenceId : undefined - }); - } - } - function verifyErrorsUsingGeterr({ allFiles, openFiles, expectedGetErr }: VerifyScenario) { it("verifies the errors in open file", () => { const host = createServerHost([...allFiles(), libFile]); const session = createSession(host, { canUseEvents: true, }); openFilesForSession(openFiles(), session); - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - const expected = expectedGetErr(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - delay: 0, - files: expected.map(f => f.file.path) - } - }); - - checkAllErrors({ session, host, expected, expectedSequenceId }); + verifyGetErrRequest({ session, host, expected: expectedGetErr() }); }); } @@ -95,27 +129,27 @@ namespace ts.projectSystem { const actualSyntax = session.executeCommandSeq({ command: protocol.CommandTypes.SyntacticDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSyntax, syntax, `Syntax diagnostics for file: ${filePath(file)}, project: ${project}`); const actualSemantic = session.executeCommandSeq({ command: protocol.CommandTypes.SemanticDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSemantic, semantic, `Semantic diagnostics for file: ${filePath(file)}, project: ${project}`); const actualSuggestion = session.executeCommandSeq({ command: protocol.CommandTypes.SuggestionDiagnosticsSync, arguments: { - file: file.path, + file: filePath(file), projectFileName: project } }).response as protocol.Diagnostic[]; - assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${file.path}, project: ${project}`); + assert.deepEqual(actualSuggestion, suggestion, `Suggestion diagnostics for file: ${filePath(file)}, project: ${project}`); } }); } @@ -139,12 +173,6 @@ namespace ts.projectSystem { }); } - interface GetErrDiagnostics { - file: File; - syntax: protocol.Diagnostic[]; - semantic: protocol.Diagnostic[]; - suggestion: protocol.Diagnostic[]; - } interface GetErrForProjectDiagnostics { project: string; errors: readonly GetErrDiagnostics[]; diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 41792907d8..56bbe989bf 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -1443,36 +1443,17 @@ var x = 10;` // Actually trigger the file move host.reloadFS(files); host.checkTimeoutQueueLength(2); - const fileBErrorTimeoutId = host.getNextTimeoutId(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - files: [fileB.path, fileSubA.path], - delay: 0 - } + verifyGetErrRequest({ + session, + host, + expected: [ + { file: fileB, syntax: [], semantic: [], suggestion: [] }, + { file: fileSubA }, + ], + existingTimeouts: 2, + onErrEvent: () => assert.isFalse(hasErrorMsg()) }); - const getErrSeqId = session.getSeq(); - host.checkTimeoutQueueLength(3); - - session.clearMessages(); - host.runQueuedTimeoutCallbacks(fileBErrorTimeoutId); - checkErrorMessage(session, "syntaxDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - const fileSubAErrorTimeoutId = host.getNextTimeoutId(); - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "suggestionDiag", { file: fileB.path, diagnostics: [] }); - - session.clearMessages(); - host.checkTimeoutQueueLength(3); - host.runQueuedTimeoutCallbacks(fileSubAErrorTimeoutId); - checkCompleteEvent(session, 1, getErrSeqId); - assert.isFalse(hasErrorMsg()); function openFile(file: File) { openFilesForSession([{ file, projectRootPath }], session); diff --git a/src/testRunner/unittests/tsserver/resolutionCache.ts b/src/testRunner/unittests/tsserver/resolutionCache.ts index 65cb84e3ea..7ebb2ee4d3 100644 --- a/src/testRunner/unittests/tsserver/resolutionCache.ts +++ b/src/testRunner/unittests/tsserver/resolutionCache.ts @@ -149,34 +149,20 @@ namespace ts.projectSystem { } }); checkNumberOfProjects(service, { inferredProjects: 1 }); - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file1.path] - } - }); - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: file1.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); const startOffset = file1.content.indexOf('"') + 1; - checkErrorMessage(session, "semanticDiag", { - file: file1.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + '"pad"'.length }, Diagnostics.Cannot_find_module_0, ["pad"]) - ], + verifyGetErrRequest({ + session, + host, + expected: [{ + file: file1, + syntax: [], + semantic: [ + createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + '"pad"'.length }, Diagnostics.Cannot_find_module_0, ["pad"]) + ], + suggestion: [] + }] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: file1.path, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); const padIndex: File = { path: `${folderPath}/node_modules/@types/pad/index.d.ts`, @@ -213,40 +199,22 @@ namespace ts.projectSystem { checkNumberOfProjects(service, { inferredProjects: 1 }); session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); host.checkTimeoutQueueLengthAndRun(2); checkProjectUpdatedInBackgroundEvent(session, [file.path]); - session.clearMessages(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file.path], - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file, + syntax: [], + semantic: [], + suggestion: [ + createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecessary*/ true), + ] + }] }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: file.path, - diagnostics: [ - createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecessary*/ true), - ], - }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); }); it("disable suggestion diagnostics", () => { @@ -273,31 +241,19 @@ namespace ts.projectSystem { checkNumberOfProjects(service, { inferredProjects: 1 }); session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); host.checkTimeoutQueueLengthAndRun(2); checkProjectUpdatedInBackgroundEvent(session, [file.path]); - session.clearMessages(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file.path], - } + verifyGetErrRequest({ + session, + host, + expected: [{ + file, + syntax: [], + semantic: [] + }] }); - - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: [] }, /*isMostRecent*/ true); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: [] }); - // No suggestion event, we're done. - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); }); it("suppressed diagnostic events", () => { diff --git a/src/testRunner/unittests/tsserver/symLinks.ts b/src/testRunner/unittests/tsserver/symLinks.ts index 54b7cb3fab..4576dc90c0 100644 --- a/src/testRunner/unittests/tsserver/symLinks.ts +++ b/src/testRunner/unittests/tsserver/symLinks.ts @@ -149,34 +149,16 @@ new C();` const filesInProjectWithResolvedModule = [...filesInProjectWithUnresolvedModule, recongnizerTextDistTypingFile.path]; function verifyErrors(session: TestSession, semanticErrors: protocol.Diagnostic[]) { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [recognizersDateTimeSrcFile.path], - } + verifyGetErrRequest({ + session, + host: session.testhost, + expected: [{ + file: recognizersDateTimeSrcFile, + syntax: [], + semantic: semanticErrors, + suggestion: [] + }] }); - - const host = session.testhost; - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: semanticErrors }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: recognizersDateTimeSrcFile.path, - diagnostics: [], - }); - checkCompleteEvent(session, 2, expectedSequenceId); } function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: ReadonlyMap, nonRecursiveDirectories: string[]) {