mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-20 05:17:43 -05:00
Handle status and error messages for better checking
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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", "[\"**/*\"]", "[]"])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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"]
|
||||
);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user