Handle status and error messages for better checking

This commit is contained in:
Sheetal Nandi
2019-08-08 14:29:42 -07:00
parent 9e8fbcd7f8
commit fd515b519e
9 changed files with 191 additions and 30 deletions

View File

@@ -375,8 +375,47 @@ namespace fakes {
}
}
export type ExpectedDiagnostic = [ts.DiagnosticMessage, ...(string | number)[]];
function expectedDiagnosticToText([message, ...args]: ExpectedDiagnostic) {
export type ExpectedDiagnosticMessage = [ts.DiagnosticMessage, ...(string | number)[]];
export interface ExpectedDiagnosticMessageChain {
message: ExpectedDiagnosticMessage;
next?: ExpectedDiagnosticMessageChain[];
}
export interface ExpectedDiagnosticLocation {
file: string;
start: number;
length: number;
}
export interface ExpectedDiagnosticRelatedInformation extends ExpectedDiagnosticMessageChain {
location?: ExpectedDiagnosticLocation;
}
export enum DiagnosticKind {
Error = "Error",
Status = "Status"
}
export interface ExpectedErrorDiagnostic extends ExpectedDiagnosticRelatedInformation {
relatedInformation?: ExpectedDiagnosticRelatedInformation[];
}
export type ExpectedDiagnostic = ExpectedDiagnosticMessage | ExpectedErrorDiagnostic;
interface SolutionBuilderDiagnostic {
kind: DiagnosticKind;
diagnostic: ts.Diagnostic;
}
function indentedText(indent: number, text: string) {
if (!indent) return text;
let indentText = "";
for (let i = 0; i < indent; i++) {
indentText += " ";
}
return `
${indentText}${text}`;
}
function expectedDiagnosticMessageToText([message, ...args]: ExpectedDiagnosticMessage) {
let text = ts.getLocaleSpecificMessage(message);
if (args.length) {
text = ts.formatStringFromArgs(text, args);
@@ -384,6 +423,70 @@ namespace fakes {
return text;
}
function expectedDiagnosticMessageChainToText({ message, next }: ExpectedDiagnosticMessageChain, indent = 0) {
let text = indentedText(indent, expectedDiagnosticMessageToText(message));
if (next) {
indent++;
next.forEach(kid => text += expectedDiagnosticMessageChainToText(kid, indent));
}
return text;
}
function expectedDiagnosticRelatedInformationToText({ location, ...diagnosticMessage }: ExpectedDiagnosticRelatedInformation) {
const text = expectedDiagnosticMessageChainToText(diagnosticMessage);
if (location) {
const { file, start, length } = location;
return `${file}(${start}:${length}):: ${text}`;
}
return text;
}
function expectedErrorDiagnosticToText({ relatedInformation, ...diagnosticRelatedInformation }: ExpectedErrorDiagnostic) {
let text = `${DiagnosticKind.Error}!: ${expectedDiagnosticRelatedInformationToText(diagnosticRelatedInformation)}`;
if (relatedInformation) {
for (const kid of relatedInformation) {
text += `
related:: ${expectedDiagnosticRelatedInformationToText(kid)}`;
}
}
return text;
}
function expectedDiagnosticToText(errorOrStatus: ExpectedDiagnostic) {
return ts.isArray(errorOrStatus) ?
`${DiagnosticKind.Status}!: ${expectedDiagnosticMessageToText(errorOrStatus)}` :
expectedErrorDiagnosticToText(errorOrStatus);
}
function diagnosticMessageChainToText({ messageText, next}: ts.DiagnosticMessageChain, indent = 0) {
let text = indentedText(indent, messageText);
if (next) {
indent++;
next.forEach(kid => text += diagnosticMessageChainToText(kid, indent));
}
return text;
}
function diagnosticRelatedInformationToText({ file, start, length, messageText }: ts.DiagnosticRelatedInformation) {
const text = typeof messageText === "string" ?
messageText :
diagnosticMessageChainToText(messageText);
return file ?
`${file.fileName}(${start}:${length}):: ${text}` :
text;
}
function diagnosticToText({ kind, diagnostic: { relatedInformation, ...diagnosticRelatedInformation } }: SolutionBuilderDiagnostic) {
let text = `${kind}!: ${diagnosticRelatedInformationToText(diagnosticRelatedInformation)}`;
if (relatedInformation) {
for (const kid of relatedInformation) {
text += `
related:: ${diagnosticRelatedInformationToText(kid)}`;
}
}
return text;
}
function compareProgramBuildInfoDiagnostic(a: ts.ProgramBuildInfoDiagnostic, b: ts.ProgramBuildInfoDiagnostic) {
return ts.compareStringsCaseSensitive(ts.isString(a) ? a : a[0], ts.isString(b) ? b : b[0]);
}
@@ -446,14 +549,14 @@ namespace fakes {
return new Date(this.sys.vfs.time());
}
diagnostics: ts.Diagnostic[] = [];
diagnostics: SolutionBuilderDiagnostic[] = [];
reportDiagnostic(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
this.diagnostics.push({ kind: DiagnosticKind.Error, diagnostic });
}
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
this.diagnostics.push({ kind: DiagnosticKind.Status, diagnostic });
}
clearDiagnostics() {
@@ -461,7 +564,7 @@ namespace fakes {
}
assertDiagnosticMessages(...expectedDiagnostics: ExpectedDiagnostic[]) {
const actual = this.diagnostics.slice().map(d => d.messageText as string);
const actual = this.diagnostics.slice().map(diagnosticToText);
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
assert.deepEqual(actual, expected, `Diagnostic arrays did not match:
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
@@ -471,8 +574,8 @@ Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
printDiagnostics(header = "== Diagnostics ==") {
const out = ts.createDiagnosticReporter(ts.sys);
ts.sys.write(header + "\r\n");
for (const d of this.diagnostics) {
out(d);
for (const { diagnostic } of this.diagnostics) {
out(diagnostic);
}
}
}

View File

@@ -93,7 +93,7 @@ namespace ts {
expectedExitStatus: ExitStatus.ProjectReferenceCycle_OutputsSkupped,
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/core/tsconfig.json", "src/tsconfig.json"),
[
errorDiagnostic([
Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0,
[
"/src/tsconfig.json",
@@ -101,7 +101,7 @@ namespace ts {
"/src/zoo/tsconfig.json",
"/src/animals/tsconfig.json"
].join("\r\n")
]
])
],
expectedOutputs: emptyArray,
notExpectedOutputs: [...coreOutputs(), ...animalOutputs(), ...zooOutputs()]
@@ -121,12 +121,12 @@ namespace ts {
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/lib/core/utilities.js"],
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
[Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/animal.ts", "/src/core"],
[Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/dog.ts", "/src/core"],
[Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/index.ts", "/src/core"],
[Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/animal.ts", "/src/core/tsconfig.json"],
[Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/dog.ts", "/src/core/tsconfig.json"],
[Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/index.ts", "/src/core/tsconfig.json"],
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/animal.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/dog.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/index.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/animal.ts", "/src/core/tsconfig.json"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/dog.ts", "/src/core/tsconfig.json"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/index.ts", "/src/core/tsconfig.json"]),
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/animals/tsconfig.json", "src/core"],
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/animals/tsconfig.json", "/src/core"],
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_was_not_built, "src/zoo/tsconfig.json", "src/animals"],

View File

@@ -15,7 +15,14 @@ namespace ts {
host.clearDiagnostics();
builder.build();
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
host.assertDiagnosticMessages({
message: [Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"],
location: {
file: "/src/no-references/tsconfig.json",
start: lastIndexOf(fs, "/src/no-references/tsconfig.json", "[]"),
length: 2
}
});
// Check for outputs to not be written.
verifyOutputsAbsent(fs, allExpectedOutputs);

View File

@@ -1,4 +1,8 @@
namespace ts {
export function errorDiagnostic(message: fakes.ExpectedDiagnosticMessage): fakes.ExpectedErrorDiagnostic {
return { message };
}
export function getExpectedDiagnosticForProjectsInBuild(...projects: string[]): fakes.ExpectedDiagnostic {
return [Diagnostics.Projects_in_this_build_Colon_0, projects.map(p => "\r\n * " + p).join("")];
}
@@ -42,6 +46,22 @@ namespace ts {
fs.writeFileSync(path, `${old}${additionalContent}`);
}
export function indexOf(fs: vfs.FileSystem, path: string, searchStr: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const content = fs.readFileSync(path, "utf-8");
return content.indexOf(searchStr);
}
export function lastIndexOf(fs: vfs.FileSystem, path: string, searchStr: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const content = fs.readFileSync(path, "utf-8");
return content.lastIndexOf(searchStr);
}
export function getTime() {
let currentTime = 100;
return { tick, time, touch };

View File

@@ -7,10 +7,10 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
builder.build();
host.assertDiagnosticMessages(
[Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
[Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"],
[Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
[Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.second.json", "[\"**/*\"]", "[]"]
errorDiagnostic([Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"]),
errorDiagnostic([Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"]),
errorDiagnostic([Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"]),
errorDiagnostic([Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.second.json", "[\"**/*\"]", "[]"])
);
});
});

View File

@@ -43,7 +43,14 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/other/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.json", "src/dist/a.js"],
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
[Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"]
{
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
location: {
file: "/src/src/main/tsconfig.json",
start: indexOf(fs, "/src/src/main/tsconfig.json", `{ "path": "../other" }`),
length: `{ "path": "../other" }`.length
}
}
);
verifyOutputsPresent(fs, allExpectedOutputs);
verifyOutputsAbsent(fs, missingOutputs);
@@ -75,7 +82,14 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/other/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/src/main/tsconfig.json", "src/dist/a.js"],
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
[Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"]
{
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
location: {
file: "/src/src/main/tsconfig.json",
start: indexOf(fs, "/src/src/main/tsconfig.json", `{"path":"../other"}`),
length: `{"path":"../other"}`.length
}
}
);
verifyOutputsPresent(fs, allExpectedOutputs);
verifyOutputsAbsent(fs, missingOutputs);

View File

@@ -28,11 +28,14 @@ namespace ts {
}
it("with resolveJsonModule and include only", () => {
verifyProjectWithResolveJsonModule("/src/tsconfig_withInclude.json", [
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
"/src/src/hello.json",
"/src/tsconfig_withInclude.json"
]);
verifyProjectWithResolveJsonModule(
"/src/tsconfig_withInclude.json",
errorDiagnostic([
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
"/src/src/hello.json",
"/src/tsconfig_withInclude.json"
])
);
});
it("with resolveJsonModule and include of *.json along with other include", () => {

View File

@@ -450,7 +450,14 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
[Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
{
message: [Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
location: {
file: "/src/logic/index.ts",
start: indexOf(fs, "/src/logic/index.ts", "muitply"),
length: "muitply".length
}
},
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/tests/tsconfig.json", "src/logic"],
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/tests/tsconfig.json", "/src/logic"]
);

View File

@@ -69,7 +69,14 @@ export const b = new A();`);
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "node"),
allExpectedOutputs,
expectedFileTraces,
[Diagnostics.Cannot_find_module_0, "a"],
{
message: [Diagnostics.Cannot_find_module_0, "a"],
location: {
file: "/src/b.ts",
start: `import {A} from 'a';`.indexOf(`'a'`),
length: `'a'`.length
}
},
);
});
});