Separate out the tests for tsbuild into its own folder

This commit is contained in:
Sheetal Nandi 2019-01-31 15:34:23 -08:00
parent f7281e3fb5
commit db3a4fe25d
11 changed files with 1107 additions and 1054 deletions

View File

@ -37,6 +37,7 @@
"runner.ts",
"unittests/services/extract/helpers.ts",
"unittests/tsbuild/helpers.ts",
"unittests/tscWatch/helpers.ts",
"unittests/tsserver/helpers.ts",
@ -59,7 +60,6 @@
"unittests/reuseProgramStructure.ts",
"unittests/semver.ts",
"unittests/transform.ts",
"unittests/tsbuild.ts",
"unittests/tsbuildWatchMode.ts",
"unittests/config/commandLineParsing.ts",
"unittests/config/configurationExtension.ts",
@ -88,6 +88,14 @@
"unittests/services/preProcessFile.ts",
"unittests/services/textChanges.ts",
"unittests/services/transpile.ts",
"unittests/tsbuild/emptyFiles.ts",
"unittests/tsbuild/graphOrdering.ts",
"unittests/tsbuild/missingExtendedFile.ts",
"unittests/tsbuild/outFile.ts",
"unittests/tsbuild/referencesWithRootDirInParent.ts",
"unittests/tsbuild/resolveJsonModule.ts",
"unittests/tsbuild/sample.ts",
"unittests/tsbuild/transitiveReferences.ts",
"unittests/tscWatch/consoleClearing.ts",
"unittests/tscWatch/emit.ts",
"unittests/tscWatch/emitAndErrorUpdates.ts",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
namespace ts {
const projFs = loadProjectFromDisk("tests/projects/empty-files");
const allExpectedOutputs = [
"/src/core/index.js",
"/src/core/index.d.ts",
"/src/core/index.d.ts.map",
];
describe("unittests:: tsbuild - empty files option in tsconfig", () => {
it("has empty files diagnostic when files is empty and no references are provided", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/no-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
// Check for outputs to not be written.
for (const output of allExpectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
});
it("does not have empty files diagnostic when files is empty and references are provided", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/with-references"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs to be written.
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
});
});
}

View File

@ -0,0 +1,81 @@
namespace ts {
describe("unittests:: tsbuild - graph-ordering", () => {
let host: fakes.SolutionBuilderHost | undefined;
const deps: [string, string][] = [
["A", "B"],
["B", "C"],
["A", "C"],
["B", "D"],
["C", "D"],
["C", "E"],
["F", "E"]
];
before(() => {
const fs = new vfs.FileSystem(false);
host = new fakes.SolutionBuilderHost(fs);
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps);
});
after(() => {
host = undefined;
});
it("orders the graph correctly - specify two roots", () => {
checkGraphOrdering(["A", "G"], ["A", "B", "C", "D", "E", "G"]);
});
it("orders the graph correctly - multiple parts of the same graph in various orders", () => {
checkGraphOrdering(["A"], ["A", "B", "C", "D", "E"]);
checkGraphOrdering(["A", "C", "D"], ["A", "B", "C", "D", "E"]);
checkGraphOrdering(["D", "C", "A"], ["A", "B", "C", "D", "E"]);
});
it("orders the graph correctly - other orderings", () => {
checkGraphOrdering(["F"], ["F", "E"]);
checkGraphOrdering(["E"], ["E"]);
checkGraphOrdering(["F", "C", "A"], ["A", "B", "C", "D", "E", "F"]);
});
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false });
const projFileNames = rootNames.map(getProjectFileName);
const graph = builder.getBuildGraph(projFileNames);
assert.sameMembers(graph.buildQueue, expectedBuildSet.map(getProjectFileName));
for (const dep of deps) {
const child = getProjectFileName(dep[0]);
if (graph.buildQueue.indexOf(child) < 0) continue;
const parent = getProjectFileName(dep[1]);
assert.isAbove(graph.buildQueue.indexOf(child), graph.buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`);
}
}
function getProjectFileName(proj: string) {
return `/project/${proj}/tsconfig.json` as ResolvedConfigFileName;
}
function writeProjects(fileSystem: vfs.FileSystem, projectNames: string[], deps: [string, string][]): string[] {
const projFileNames: string[] = [];
for (const dep of deps) {
if (projectNames.indexOf(dep[0]) < 0) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`);
if (projectNames.indexOf(dep[1]) < 0) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`);
}
for (const proj of projectNames) {
fileSystem.mkdirpSync(`/project/${proj}`);
fileSystem.writeFileSync(`/project/${proj}/${proj}.ts`, "export {}");
const configFileName = getProjectFileName(proj);
const configContent = JSON.stringify({
compilerOptions: { composite: true },
files: [`./${proj}.ts`],
references: deps.filter(d => d[0] === proj).map(d => ({ path: `../${d[1]}` }))
}, undefined, 2);
fileSystem.writeFileSync(configFileName, configContent);
projFileNames.push(configFileName);
}
return projFileNames;
}
});
}

View File

@ -0,0 +1,62 @@
namespace ts {
export function getExpectedDiagnosticForProjectsInBuild(...projects: string[]): fakes.ExpectedDiagnostic {
return [Diagnostics.Projects_in_this_build_Colon_0, projects.map(p => "\r\n * " + p).join("")];
}
export function replaceText(fs: vfs.FileSystem, path: string, oldText: string, newText: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
const old = fs.readFileSync(path, "utf-8");
if (old.indexOf(oldText) < 0) {
throw new Error(`Text "${oldText}" does not exist in file ${path}`);
}
const newContent = old.replace(oldText, newText);
fs.writeFileSync(path, newContent, "utf-8");
}
export function getTime() {
let currentTime = 100;
return { tick, time, touch };
function tick() {
currentTime += 60_000;
}
function time() {
return currentTime;
}
function touch(fs: vfs.FileSystem, path: string) {
if (!fs.statSync(path).isFile()) {
throw new Error(`File ${path} does not exist`);
}
fs.utimesSync(path, new Date(time()), new Date(time()));
}
}
export function loadProjectFromDisk(root: string, time?: vfs.FileSystemOptions["time"]): vfs.FileSystem {
const resolver = vfs.createResolver(Harness.IO);
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files: {
["/lib"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), "built/local"), resolver),
["/src"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), root), resolver)
},
cwd: "/",
meta: { defaultLibLocation: "/lib" },
time
});
fs.makeReadonly();
return fs;
}
export function getLibs() {
return [
"/lib/lib.d.ts",
"/lib/lib.es5.d.ts",
"/lib/lib.dom.d.ts",
"/lib/lib.webworker.importscripts.d.ts",
"/lib/lib.scripthost.d.ts"
];
}
}

View File

@ -0,0 +1,17 @@
namespace ts {
describe("unittests:: tsbuild:: when tsconfig extends the missing file", () => {
it("unittests:: tsbuild - when tsconfig extends the missing file", () => {
const projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
builder.buildAllProjects();
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", "[\"**/*\"]", "[]"]
);
});
});
}

View File

@ -0,0 +1,379 @@
namespace ts {
describe("unittests:: tsbuild:: outFile::", () => {
let outFileFs: vfs.FileSystem;
const outputFiles: [ReadonlyArray<string>, ReadonlyArray<string>, ReadonlyArray<string>] = [
[
"/src/first/bin/first-output.js",
"/src/first/bin/first-output.js.map",
"/src/first/bin/first-output.d.ts",
"/src/first/bin/first-output.d.ts.map",
"/src/first/bin/.tsbuildinfo"
],
[
"/src/2/second-output.js",
"/src/2/second-output.js.map",
"/src/2/second-output.d.ts",
"/src/2/second-output.d.ts.map",
"/src/2/.tsbuildinfo"
],
[
"/src/third/thirdjs/output/third-output.js",
"/src/third/thirdjs/output/third-output.js.map",
"/src/third/thirdjs/output/third-output.d.ts",
"/src/third/thirdjs/output/third-output.d.ts.map",
"/src/third/thirdjs/output/.tsbuildinfo"
]
];
const { time, tick } = getTime();
before(() => {
outFileFs = loadProjectFromDisk("tests/projects/outfile-concat", time);
});
after(() => {
outFileFs = undefined!;
});
function createSolutionBuilder(host: fakes.SolutionBuilderHost) {
return ts.createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: true });
}
function verifyOutFileScenarioWorker(scenario: string, modifyFs: (fs: vfs.FileSystem) => void | ReadonlyArray<string>, withoutBuildInfo: boolean) {
describe(`${scenario}${withoutBuildInfo ? " without build info" : ""}`, () => {
let fs: vfs.FileSystem | undefined;
const actualReadFileMap = createMap<number>();
let additionalSourceFiles: ReadonlyArray<string> | void;
before(() => {
fs = outFileFs.shadow();
additionalSourceFiles = modifyFs(fs);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
host.clearDiagnostics();
const originalReadFile = host.readFile;
host.readFile = path => {
// Dont record libs
if (path.startsWith("/src/")) {
actualReadFileMap.set(path, (actualReadFileMap.get(path) || 0) + 1);
}
if (withoutBuildInfo && getBaseFileName(path) === infoFile) {
return undefined;
}
return originalReadFile.call(host, path);
};
if (withoutBuildInfo) {
const originalWriteFile = host.writeFile;
host.writeFile = (fileName, content, writeByteOrder) => {
return getBaseFileName(fileName) !== infoFile &&
originalWriteFile.call(host, fileName, content, writeByteOrder);
};
}
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/first/tsconfig.json", "src/first/bin/first-output.js"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/second/tsconfig.json", "src/2/second-output.js"],
[Diagnostics.Building_project_0, "/src/second/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/third/tsconfig.json", "src/third/thirdjs/output/third-output.js"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
});
after(() => {
fs = undefined;
});
it(`Generates files matching the baseline`, () => {
for (const mapFile of [
"/src/first/bin/first-output.js.map",
"/src/first/bin/first-output.d.ts.map",
"/src/2/second-output.js.map",
"/src/2/second-output.d.ts.map",
"/src/third/thirdjs/output/third-output.js.map",
"/src/third/thirdjs/output/third-output.d.ts.map"
]) {
const text = Harness.SourceMapRecorder.getSourceMapRecordWithVFS(fs!, mapFile);
fs!.writeFileSync(`${mapFile}.baseline.txt`, text);
}
const patch = fs!.diff();
// tslint:disable-next-line:no-null-keyword
Harness.Baseline.runBaseline(`outFile-${scenario.split(" ").join("-")}${withoutBuildInfo ? "-no-buildInfo" : ""}.js`, patch ? vfs.formatPatch(patch) : null);
});
it("verify readFile calls", () => {
const expected = [
// Configs
"/src/third/tsconfig.json",
"/src/second/tsconfig.json",
"/src/first/tsconfig.json",
// Source files
"/src/third/third_part1.ts",
"/src/second/second_part1.ts",
"/src/second/second_part2.ts",
"/src/first/first_PART1.ts",
"/src/first/first_part2.ts",
"/src/first/first_part3.ts",
// Additional source Files
...(additionalSourceFiles || emptyArray),
// outputs
...outputFiles[0],
...outputFiles[1]
];
assert.equal(actualReadFileMap.size, expected.length, `Expected: ${JSON.stringify(expected)} \nActual: ${JSON.stringify(arrayFrom(actualReadFileMap.entries()))}`);
expected.forEach(expectedValue => {
const actual = actualReadFileMap.get(expectedValue);
assert.equal(actual, 1, `Mismatch in read file call number for: ${expectedValue}\nExpected: ${JSON.stringify(expected)} \nActual: ${JSON.stringify(arrayFrom(actualReadFileMap.entries()))}`);
});
});
});
}
function verifyOutFileScenario(scenario: string, modifyFs: (fs: vfs.FileSystem) => void | ReadonlyArray<string>) {
verifyOutFileScenarioWorker(scenario, modifyFs, /*withoutBuildInfo*/ false);
verifyOutFileScenarioWorker(scenario, modifyFs, /*withoutBuildInfo*/ true);
}
verifyOutFileScenario("baseline sectioned sourcemaps", noop);
verifyOutFileScenario("when final project is not composite but uses project references", fs => replaceFileContent(fs, "/src/third/tsconfig.json", `"composite": true,`, ""));
describe("downstream prepend projects always get rebuilt", () => {
function verify(modifyFs: (fs: vfs.FileSystem) => void, ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
const fs = outFileFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/first/tsconfig.json", "src/first/bin/first-output.js"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/second/tsconfig.json", "src/2/second-output.js"],
[Diagnostics.Building_project_0, "/src/second/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/third/tsconfig.json", "src/third/thirdjs/output/third-output.js"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct");
tick();
host.clearDiagnostics();
modifyFs(fs);
tick();
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(...expectedDiagnostics);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct");
}
it("when declaration changes", () => {
verify(fs => replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola"),
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/first/tsconfig.json", "src/first/bin/first-output.js", "src/first/first_PART1.ts"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/second/tsconfig.json", "src/second/second_part1.ts", "src/2/second-output.js"],
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/third/tsconfig.json", "src/third/thirdjs/output/third-output.js", "src/first"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
});
it("when declaration doesnt change", () => {
verify(fs => appendFileContent(fs, "src/first/first_PART1.ts", "console.log(s);"),
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/first/tsconfig.json", "src/first/bin/first-output.js", "src/first/first_PART1.ts"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/second/tsconfig.json", "src/second/second_part1.ts", "src/2/second-output.js"],
[Diagnostics.Project_0_is_out_of_date_because_output_to_prepend_from_its_dependency_1_has_changed, "src/third/tsconfig.json", "src/first"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
});
});
it("clean projects", () => {
const fs = outFileFs.shadow();
const expectedOutputs = [
...outputFiles[0],
...outputFiles[1],
...outputFiles[2]
];
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/first/tsconfig.json", "src/first/bin/first-output.js"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/second/tsconfig.json", "src/2/second-output.js"],
[Diagnostics.Building_project_0, "/src/second/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/third/tsconfig.json", "src/third/thirdjs/output/third-output.js"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
// Verify they exist
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
host.clearDiagnostics();
builder.cleanAllProjects();
host.assertDiagnosticMessages(/*none*/);
// Verify they are gone
for (const output of expectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
// Subsequent clean shouldn't throw / etc
builder.cleanAllProjects();
});
it("verify buildInfo presence or absence does not result in new build", () => {
const fs = outFileFs.shadow();
const expectedOutputs = [
...outputFiles[0],
...outputFiles[1],
...outputFiles[2]
];
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host);
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/first/tsconfig.json", "src/first/bin/first-output.js"],
[Diagnostics.Building_project_0, "/src/first/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/second/tsconfig.json", "src/2/second-output.js"],
[Diagnostics.Building_project_0, "/src/second/tsconfig.json"],
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/third/tsconfig.json", "src/third/thirdjs/output/third-output.js"],
[Diagnostics.Building_project_0, "/src/third/tsconfig.json"]
);
// Verify they exist
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
// Delete bundle info
host.clearDiagnostics();
host.deleteFile(last(outputFiles[0]));
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/first/tsconfig.json", "src/second/tsconfig.json", "src/third/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/first/tsconfig.json", "src/first/first_PART1.ts", "src/first/bin/first-output.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/second/tsconfig.json", "src/second/second_part1.ts", "src/2/second-output.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/third/tsconfig.json", "src/third/third_part1.ts", "src/third/thirdjs/output/third-output.js"],
);
});
function replaceFileContent(fs: vfs.FileSystem, path: string, searchValue: string, replaceValue: string) {
const content = fs.readFileSync(path, "utf8");
fs.writeFileSync(path, content.replace(searchValue, replaceValue));
}
function prependFileContent(fs: vfs.FileSystem, path: string, additionalContent: string) {
const content = fs.readFileSync(path, "utf8");
fs.writeFileSync(path, `${additionalContent}${content}`);
}
function appendFileContent(fs: vfs.FileSystem, path: string, additionalContent: string) {
const content = fs.readFileSync(path, "utf8");
fs.writeFileSync(path, `${content}${additionalContent}`);
}
// Prologues
function enableStrict(fs: vfs.FileSystem, path: string) {
replaceFileContent(fs, path, `"strict": false`, `"strict": true`);
}
verifyOutFileScenario("strict in all projects", fs => {
enableStrict(fs, "src/first/tsconfig.json");
enableStrict(fs, "src/second/tsconfig.json");
enableStrict(fs, "src/third/tsconfig.json");
});
verifyOutFileScenario("strict in one dependency", fs => {
enableStrict(fs, "src/second/tsconfig.json");
});
function addPrologue(fs: vfs.FileSystem, path: string, prologue: string) {
prependFileContent(fs, path, `${prologue}
`);
}
verifyOutFileScenario("multiple prologues in all projects", fs => {
enableStrict(fs, "src/first/tsconfig.json");
addPrologue(fs, "src/first/first_PART1.ts", `"myPrologue"`);
enableStrict(fs, "src/second/tsconfig.json");
addPrologue(fs, "src/second/second_part1.ts", `"myPrologue"`);
addPrologue(fs, "src/second/second_part2.ts", `"myPrologue2";`);
enableStrict(fs, "src/third/tsconfig.json");
addPrologue(fs, "src/third/third_part1.ts", `"myPrologue";`);
addPrologue(fs, "src/third/third_part1.ts", `"myPrologue3";`);
});
verifyOutFileScenario("multiple prologues in different projects", fs => {
enableStrict(fs, "src/first/tsconfig.json");
addPrologue(fs, "src/second/second_part1.ts", `"myPrologue"`);
addPrologue(fs, "src/second/second_part2.ts", `"myPrologue2";`);
enableStrict(fs, "src/third/tsconfig.json");
});
// Shebang
function addShebang(fs: vfs.FileSystem, project: string, file: string) {
prependFileContent(fs, `src/${project}/${file}.ts`, `#!someshebang ${project} ${file}
`);
}
verifyOutFileScenario("shebang in all projects", fs => {
addShebang(fs, "first", "first_PART1");
addShebang(fs, "first", "first_part2");
addShebang(fs, "second", "second_part1");
addShebang(fs, "third", "third_part1");
});
verifyOutFileScenario("shebang in only one dependency project", fs => {
addShebang(fs, "second", "second_part1");
});
// emitHelpers
function addExtendsClause(fs: vfs.FileSystem, project: string, file: string) {
appendFileContent(fs, `src/${project}/${file}.ts`, `
class ${project}1 { }
class ${project}2 extends ${project}1 { }`);
}
verifyOutFileScenario("emitHelpers in all projects", fs => {
addExtendsClause(fs, "first", "first_part2");
addExtendsClause(fs, "second", "second_part1");
addExtendsClause(fs, "third", "third_part1");
});
verifyOutFileScenario("emitHelpers in only one dependency project", fs => {
addExtendsClause(fs, "second", "second_part1");
});
function addSpread(fs: vfs.FileSystem, project: string, file: string) {
const path = `src/${project}/${file}.ts`;
const content = fs.readFileSync(path, "utf8");
fs.writeFileSync(path, `${content}
function ${project}${file}Spread(...b: number[]) { }
${project}${file}Spread(...[10, 20, 30]);`);
replaceFileContent(fs, `src/${project}/tsconfig.json`, `"strict": false,`, `"strict": false,
"downlevelIteration": true,`);
}
verifyOutFileScenario("multiple emitHelpers in all projects", fs => {
addExtendsClause(fs, "first", "first_part2");
addSpread(fs, "first", "first_part3");
addExtendsClause(fs, "second", "second_part1");
addSpread(fs, "second", "second_part2");
addExtendsClause(fs, "third", "third_part1");
addSpread(fs, "third", "third_part1");
});
verifyOutFileScenario("multiple emitHelpers in different projects", fs => {
addSpread(fs, "first", "first_part3");
addExtendsClause(fs, "second", "second_part1");
addSpread(fs, "third", "third_part1");
});
// triple slash refs
function addTripleSlashRef(fs: vfs.FileSystem, project: string, file: string) {
const tripleSlashRef = `/src/${project}/tripleRef.d.ts`;
fs.writeFileSync(tripleSlashRef, `declare class ${project}${file} { }`);
prependFileContent(fs, `src/${project}/${file}.ts`, `///<reference path="./tripleRef.d.ts"/>
const ${file}Const = new ${project}${file}();
`);
return tripleSlashRef;
}
verifyOutFileScenario("triple slash refs in all projects", fs => [
addTripleSlashRef(fs, "first", "first_part2"),
addTripleSlashRef(fs, "second", "second_part1"),
addTripleSlashRef(fs, "third", "third_part1")
]);
verifyOutFileScenario("triple slash refs in one project", fs => [
addTripleSlashRef(fs, "second", "second_part1")
]);
});
}

View File

@ -0,0 +1,20 @@
namespace ts {
describe("unittests:: tsbuild:: with rootDir of project reference in parentDirectory", () => {
it("verify that it builds correctly", () => {
const projFs = loadProjectFromDisk("tests/projects/projectReferenceWithRootDirInParent");
const allExpectedOutputs = [
"/src/dist/other/other.js", "/src/dist/other/other.d.ts",
"/src/dist/main/a.js", "/src/dist/main/a.d.ts",
"/src/dist/main/b.js", "/src/dist/main/b.d.ts"
];
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/src/main", "/src/src/other"], {});
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
});
});
}

View File

@ -0,0 +1,61 @@
namespace ts {
describe("unittests:: tsbuild:: with resolveJsonModule option", () => {
let projFs: vfs.FileSystem;
const allExpectedOutputs = ["/src/tests/dist/src/index.js", "/src/tests/dist/src/index.d.ts", "/src/tests/dist/src/hello.json"];
before(() => {
projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite");
});
after(() => {
projFs = undefined!; // Release the contents
});
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
const fs = projFs.shadow();
verifyProjectWithResolveJsonModuleWithFs(fs, configFile, allExpectedOutputs, ...expectedDiagnosticMessages);
}
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray<string>, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
if (!expectedDiagnosticMessages.length) {
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
}
}
it("with resolveJsonModule and include only", () => {
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withInclude.json", [
Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern,
"/src/tests/src/hello.json"
]);
});
it("with resolveJsonModule and include of *.json along with other include", () => {
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withIncludeOfJson.json");
});
it("with resolveJsonModule and include of *.json along with other include and file name matches ts file", () => {
const fs = projFs.shadow();
fs.rimrafSync("/src/tests/src/hello.json");
fs.writeFileSync("/src/tests/src/index.json", JSON.stringify({ hello: "world" }));
fs.writeFileSync("/src/tests/src/index.ts", `import hello from "./index.json"
export default hello.hello`);
const allExpectedOutputs = ["/src/tests/dist/src/index.js", "/src/tests/dist/src/index.d.ts", "/src/tests/dist/src/index.json"];
verifyProjectWithResolveJsonModuleWithFs(fs, "/src/tests/tsconfig_withIncludeOfJson.json", allExpectedOutputs);
});
it("with resolveJsonModule and files containing json file", () => {
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withFiles.json");
});
it("with resolveJsonModule and include and files", () => {
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withIncludeAndFiles.json");
});
});
}

View File

@ -0,0 +1,361 @@
namespace ts {
describe("unittests:: tsbuild:: on 'sample1' project", () => {
let projFs: vfs.FileSystem;
const { time, tick, touch } = getTime();
const allExpectedOutputs = ["/src/tests/index.js",
"/src/core/index.js", "/src/core/index.d.ts", "/src/core/index.d.ts.map",
"/src/logic/index.js", "/src/logic/index.js.map", "/src/logic/index.d.ts"];
before(() => {
projFs = loadProjectFromDisk("tests/projects/sample1", time);
});
after(() => {
projFs = undefined!; // Release the contents
});
describe("sanity check of clean build of 'sample1' project", () => {
it("can build the sample project 'sample1' without error", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
});
it("builds correctly when outDir is specified", () => {
const fs = projFs.shadow();
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
compilerOptions: { composite: true, declaration: true, sourceMap: true, outDir: "outDir" },
references: [{ path: "../core" }]
}));
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/", "/logic/outDir/"));
// Check for outputs. Not an exhaustive list
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
});
it("builds correctly when declarationDir is specified", () => {
const fs = projFs.shadow();
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
compilerOptions: { composite: true, declaration: true, sourceMap: true, declarationDir: "out/decls" },
references: [{ path: "../core" }]
}));
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], {});
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
const expectedOutputs = allExpectedOutputs.map(f => f.replace("/logic/index.d.ts", "/logic/out/decls/index.d.ts"));
// Check for outputs. Not an exhaustive list
for (const output of expectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
});
});
describe("dry builds", () => {
it("doesn't write any files in a dry build", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
host.assertDiagnosticMessages(
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/core/tsconfig.json"],
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/logic/tsconfig.json"],
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/tests/tsconfig.json"]
);
// Check for outputs to not be written. Not an exhaustive list
for (const output of allExpectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
});
it("indicates that it would skip builds during a dry build", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
tick();
host.clearDiagnostics();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
host.assertDiagnosticMessages(
[Diagnostics.Project_0_is_up_to_date, "/src/core/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date, "/src/logic/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date, "/src/tests/tsconfig.json"]
);
});
});
describe("clean builds", () => {
it("removes all files it built", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
// Verify they exist
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
builder.cleanAllProjects();
// Verify they are gone
for (const output of allExpectedOutputs) {
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
}
// Subsequent clean shouldn't throw / etc
builder.cleanAllProjects();
});
});
describe("force builds", () => {
it("always builds under --force", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.buildAllProjects();
let currentTime = time();
checkOutputTimestamps(currentTime);
tick();
Debug.assert(time() !== currentTime, "Time moves on");
currentTime = time();
builder.buildAllProjects();
checkOutputTimestamps(currentTime);
function checkOutputTimestamps(expected: number) {
// Check timestamps
for (const output of allExpectedOutputs) {
const actual = fs.statSync(output).mtimeMs;
assert(actual === expected, `File ${output} has timestamp ${actual}, expected ${expected}`);
}
}
});
});
describe("can detect when and what to rebuild", () => {
let fs: vfs.FileSystem;
let host: fakes.SolutionBuilderHost;
let builder: SolutionBuilder;
before(() => {
fs = projFs.shadow();
host = new fakes.SolutionBuilderHost(fs);
builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
});
after(() => {
fs = undefined!;
host = undefined!;
builder = undefined!;
});
it("Builds the project", () => {
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
[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.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
);
tick();
});
// All three projects are up to date
it("Detects that all projects are up to date", () => {
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"]
);
tick();
});
// Update a file in the leaf node (tests), only it should rebuild the last one
it("Only builds the leaf node project", () => {
host.clearDiagnostics();
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/tests/index.ts"],
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
);
tick();
});
// Update a file in the parent (without affecting types), should get fast downstream builds
it("Detects type-only changes in upstream projects", () => {
host.clearDiagnostics();
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
builder.resetBuildContext();
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/index.ts"],
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/logic/tsconfig.json"],
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/logic/tsconfig.json"],
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/tests/tsconfig.json"],
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/tests/tsconfig.json"]
);
});
});
describe("downstream-blocked compilations", () => {
it("won't build downstream projects if upstream projects have errors", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
// Induce an error in the middle project
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
builder.buildAllProjects();
host.assertDiagnosticMessages(
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
[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")`],
[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"]
);
});
});
describe("project invalidation", () => {
it("invalidates projects correctly", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
host.assertDiagnosticMessages(/*empty*/);
// Update a timestamp in the middle project
tick();
touch(fs, "/src/logic/index.ts");
const originalWriteFile = fs.writeFileSync;
const writtenFiles = createMap<true>();
fs.writeFileSync = (path, data, encoding) => {
writtenFiles.set(path, true);
originalWriteFile.call(fs, path, data, encoding);
};
// Because we haven't reset the build context, the builder should assume there's nothing to do right now
const status = builder.getUpToDateStatusOfFile(builder.resolveProjectName("/src/logic"));
assert.equal(status.type, UpToDateStatusType.UpToDate, "Project should be assumed to be up-to-date");
verifyInvalidation(/*expectedToWriteTests*/ false);
// Rebuild this project
fs.writeFileSync("/src/logic/index.ts", `${fs.readFileSync("/src/logic/index.ts")}
export class cNew {}`);
verifyInvalidation(/*expectedToWriteTests*/ true);
function verifyInvalidation(expectedToWriteTests: boolean) {
// Rebuild this project
tick();
builder.invalidateProject("/src/logic");
builder.buildInvalidatedProject();
// The file should be updated
assert.isTrue(writtenFiles.has("/src/logic/index.js"), "JS file should have been rebuilt");
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
assert.isFalse(writtenFiles.has("/src/tests/index.js"), "Downstream JS file should *not* have been rebuilt");
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
writtenFiles.clear();
// Build downstream projects should update 'tests', but not 'core'
tick();
builder.buildInvalidatedProject();
if (expectedToWriteTests) {
assert.isTrue(writtenFiles.has("/src/tests/index.js"), "Downstream JS file should have been rebuilt");
}
else {
assert.equal(writtenFiles.size, 0, "Should not write any new files");
}
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have new timestamp");
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
}
});
});
describe("lists files", () => {
it("listFiles", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { listFiles: true });
builder.buildAllProjects();
assert.deepEqual(host.traces, [
...getLibs(),
"/src/core/anotherModule.ts",
"/src/core/index.ts",
"/src/core/some_decl.d.ts",
...getLibs(),
...getCoreOutputs(),
"/src/logic/index.ts",
...getLibs(),
...getCoreOutputs(),
"/src/logic/index.d.ts",
"/src/tests/index.ts"
]);
function getCoreOutputs() {
return [
"/src/core/index.d.ts",
"/src/core/anotherModule.d.ts"
];
}
});
it("listEmittedFiles", () => {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { listEmittedFiles: true });
builder.buildAllProjects();
assert.deepEqual(host.traces, [
"TSFILE: /src/core/anotherModule.js",
"TSFILE: /src/core/anotherModule.d.ts",
"TSFILE: /src/core/anotherModule.d.ts.map",
"TSFILE: /src/core/index.js",
"TSFILE: /src/core/index.d.ts",
"TSFILE: /src/core/index.d.ts.map",
"TSFILE: /src/logic/index.js",
"TSFILE: /src/logic/index.js.map",
"TSFILE: /src/logic/index.d.ts",
"TSFILE: /src/tests/index.js",
"TSFILE: /src/tests/index.d.ts",
]);
});
});
});
}

View File

@ -0,0 +1,76 @@
namespace ts {
describe("unittests:: tsbuild:: when project reference is referenced transitively", () => {
let projFs: vfs.FileSystem;
const allExpectedOutputs = [
"/src/a.js", "/src/a.d.ts",
"/src/b.js", "/src/b.d.ts",
"/src/c.js"
];
const expectedFileTraces = [
...getLibs(),
"/src/a.ts",
...getLibs(),
"/src/a.d.ts",
"/src/b.ts",
...getLibs(),
"/src/a.d.ts",
"/src/b.d.ts",
"/src/refs/a.d.ts",
"/src/c.ts"
];
before(() => {
projFs = loadProjectFromDisk("tests/projects/transitiveReferences");
});
after(() => {
projFs = undefined!; // Release the contents
});
function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: ReadonlyArray<string>, expectedFileTraces: ReadonlyArray<string>, ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
const fs = projFs.shadow();
const host = new fakes.SolutionBuilderHost(fs);
modifyDiskLayout(fs);
const builder = createSolutionBuilder(host, ["/src/tsconfig.c.json"], { listFiles: true });
builder.buildAllProjects();
host.assertDiagnosticMessages(...expectedDiagnostics);
for (const output of allExpectedOutputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
}
assert.deepEqual(host.traces, expectedFileTraces);
}
function modifyFsBTsToNonRelativeImport(fs: vfs.FileSystem, moduleResolution: "node" | "classic") {
fs.writeFileSync("/src/b.ts", `import {A} from 'a';
export const b = new A();`);
fs.writeFileSync("/src/tsconfig.b.json", JSON.stringify({
compilerOptions: {
composite: true,
moduleResolution
},
files: ["b.ts"],
references: [{ path: "tsconfig.a.json" }]
}));
}
it("verify that it builds correctly", () => {
verifyBuild(noop, allExpectedOutputs, expectedFileTraces);
});
it("verify that it builds correctly when the referenced project uses different module resolution", () => {
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, expectedFileTraces);
});
it("verify that it build reports error about module not found with node resolution with external module name", () => {
// Error in b build only a
const allExpectedOutputs = ["/src/a.js", "/src/a.d.ts"];
const expectedFileTraces = [
...getLibs(),
"/src/a.ts",
];
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "node"),
allExpectedOutputs,
expectedFileTraces,
[Diagnostics.Cannot_find_module_0, "a"],
);
});
});
}