Skip to content

Commit 2692b2e

Browse files
committed
Add failing test cases for isolatedModules after enabling declaration emit
1 parent 07b9715 commit 2692b2e

File tree

5 files changed

+390
-33
lines changed

5 files changed

+390
-33
lines changed

src/testRunner/unittests/tsbuild/helpers.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ interface Symbol {
230230
}
231231
}
232232

233-
interface BuildInput {
233+
export interface BuildInput {
234234
fs: vfs.FileSystem;
235235
tick: () => void;
236236
rootNames: readonly string[];
@@ -239,7 +239,7 @@ interface Symbol {
239239
baselineBuildInfo?: true;
240240
}
241241

242-
function build({ fs, tick, rootNames, modifyFs, baselineSourceMap, baselineBuildInfo }: BuildInput) {
242+
export function tscBuild({ fs, tick, rootNames, modifyFs, baselineSourceMap, baselineBuildInfo }: BuildInput) {
243243
const actualReadFileMap = createMap<number>();
244244
modifyFs(fs);
245245
tick();
@@ -344,7 +344,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
344344
let host: fakes.SolutionBuilderHost;
345345
let initialWrittenFiles: Map<true>;
346346
before(() => {
347-
const result = build({
347+
const result = tscBuild({
348348
fs: projFs().shadow(),
349349
tick,
350350
rootNames,
@@ -390,7 +390,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
390390
tick();
391391
newFs = fs.shadow();
392392
tick();
393-
({ actualReadFileMap, host } = build({
393+
({ actualReadFileMap, host } = tscBuild({
394394
fs: newFs,
395395
tick,
396396
rootNames,
@@ -429,7 +429,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
429429
});
430430
}
431431
it(`Verify emit output file text is same when built clean`, () => {
432-
const { fs, writtenFiles } = build({
432+
const { fs, writtenFiles } = tscBuild({
433433
fs: newFs.shadow(),
434434
tick,
435435
rootNames,

src/testRunner/unittests/tsbuild/inferredTypeFromTransitiveModule.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace ts {
2525
]
2626
},
2727
incrementalDtsChangedBuild: {
28-
modifyFs: fs => replaceText(fs, "/src/bar.ts", "param: string", ""),
28+
modifyFs: changeBarParam,
2929
expectedDiagnostics: [
3030
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
3131
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/obj/bar.js", "src/bar.ts"],
@@ -36,5 +36,55 @@ namespace ts {
3636
baselineOnly: true,
3737
verifyDiagnostics: true
3838
});
39+
40+
verifyTsbuildOutput({
41+
scenario: "inferred type from transitive module with isolatedModules",
42+
projFs: () => projFs,
43+
time,
44+
tick,
45+
proj: "inferredTypeFromTransitiveModule",
46+
rootNames: ["/src"],
47+
initialBuild: { modifyFs: changeToIsolatedModules },
48+
incrementalDtsChangedBuild: { modifyFs: changeBarParam },
49+
baselineOnly: true,
50+
});
51+
52+
it("reports errors in files affected by change in signature", () => {
53+
const { fs, host } = tscBuild({
54+
fs: projFs.shadow(),
55+
tick,
56+
rootNames: ["/src"],
57+
modifyFs: fs => {
58+
changeToIsolatedModules(fs);
59+
appendText(fs, "/src/lazyIndex.ts", `
60+
import { default as bar } from './bar';
61+
bar("hello");`);
62+
}
63+
});
64+
host.assertErrors(/*empty*/);
65+
66+
tick();
67+
const { fs: newFs, host: newHost, writtenFiles } = tscBuild({
68+
fs: fs.shadow(),
69+
tick,
70+
rootNames: ["/src"],
71+
modifyFs: changeBarParam
72+
});
73+
// Has errors
74+
newHost.assertErrors({
75+
message: [Diagnostics.Expected_0_arguments_but_got_1, 0, 1],
76+
location: expectedLocationIndexOf(newFs, "/src/lazyIndex.ts", `"hello"`)
77+
});
78+
// No written files
79+
assert.equal(writtenFiles.size, 0);
80+
});
3981
});
82+
83+
function changeToIsolatedModules(fs: vfs.FileSystem) {
84+
replaceText(fs, "/src/tsconfig.json", `"incremental": true`, `"incremental": true, "isolatedModules": true`);
85+
}
86+
87+
function changeBarParam(fs: vfs.FileSystem) {
88+
replaceText(fs, "/src/bar.ts", "param: string", "");
89+
}
4090
}

src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts

Lines changed: 117 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace ts.tscWatch {
1212
file,
1313
fileStamp: host.getModifiedTime(file.path.replace(".ts", ".js")),
1414
errors: builderProgram.getSemanticDiagnostics(watch().getSourceFileByPath(file.path as Path)),
15-
errorsFromOldState: !!state.semanticDiagnosticsFromOldState && state.semanticDiagnosticsFromOldState.has(file.path)
15+
errorsFromOldState: !!state.semanticDiagnosticsFromOldState && state.semanticDiagnosticsFromOldState.has(file.path),
16+
dtsStamp: host.getModifiedTime(file.path.replace(".ts", ".d.ts"))
1617
};
1718
}
1819

@@ -24,21 +25,36 @@ namespace ts.tscWatch {
2425
return find(stampsAndErrors, info => info.file === file)!;
2526
}
2627

27-
function verifyOutputFileStampsAndErrors(
28-
file: File,
29-
emitExpected: boolean,
30-
errorRefershExpected: boolean,
31-
beforeChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[],
32-
afterChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[]
33-
) {
28+
interface VerifyOutputFileStampAndErrors {
29+
file: File;
30+
jsEmitExpected: boolean;
31+
dtsEmitExpected: boolean;
32+
errorRefershExpected: boolean;
33+
beforeChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[];
34+
afterChangeFileStampsAndErrors: readonly ReturnType<typeof getOutputFileStampAndError>[];
35+
}
36+
function verifyOutputFileStampsAndErrors({
37+
file,
38+
jsEmitExpected,
39+
dtsEmitExpected,
40+
errorRefershExpected,
41+
beforeChangeFileStampsAndErrors,
42+
afterChangeFileStampsAndErrors
43+
}: VerifyOutputFileStampAndErrors) {
3444
const beforeChange = findStampAndErrors(beforeChangeFileStampsAndErrors, file);
3545
const afterChange = findStampAndErrors(afterChangeFileStampsAndErrors, file);
36-
if (emitExpected) {
46+
if (jsEmitExpected) {
3747
assert.notStrictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Expected emit for file ${file.path}`);
3848
}
3949
else {
4050
assert.strictEqual(afterChange.fileStamp, beforeChange.fileStamp, `Did not expect new emit for file ${file.path}`);
4151
}
52+
if (dtsEmitExpected) {
53+
assert.notStrictEqual(afterChange.dtsStamp, beforeChange.dtsStamp, `Expected emit for file ${file.path}`);
54+
}
55+
else {
56+
assert.strictEqual(afterChange.dtsStamp, beforeChange.dtsStamp, `Did not expect new emit for file ${file.path}`);
57+
}
4258
if (errorRefershExpected) {
4359
if (afterChange.errors !== emptyArray || beforeChange.errors !== emptyArray) {
4460
assert.notStrictEqual(afterChange.errors, beforeChange.errors, `Expected new errors for file ${file.path}`);
@@ -51,19 +67,22 @@ namespace ts.tscWatch {
5167
}
5268
}
5369

54-
interface VerifyEmitAndErrorUpdates {
55-
change: (host: WatchedSystem) => void;
56-
getInitialErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
57-
getIncrementalErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
58-
filesWithNewEmit: readonly File[];
59-
filesWithOnlyErrorRefresh: readonly File[];
60-
filesNotTouched: readonly File[];
61-
configFile?: File;
70+
interface VerifyEmitAndErrorUpdatesWorker extends VerifyEmitAndErrorUpdates {
71+
configFile: File;
6272
}
63-
64-
function verifyEmitAndErrorUpdates({ filesWithNewEmit, filesWithOnlyErrorRefresh, filesNotTouched, configFile = config, change, getInitialErrors, getIncrementalErrors }: VerifyEmitAndErrorUpdates) {
73+
function verifyEmitAndErrorUpdatesWorker({
74+
fileWithChange,
75+
filesWithNewEmit,
76+
filesWithOnlyErrorRefresh,
77+
filesNotTouched,
78+
configFile,
79+
change,
80+
getInitialErrors,
81+
getIncrementalErrors
82+
}: VerifyEmitAndErrorUpdatesWorker) {
6583
const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched];
6684
const files = [...nonLibFiles, configFile, libFile];
85+
const compilerOptions = (JSON.parse(configFile.content).compilerOptions || {}) as CompilerOptions;
6786
const host = createWatchedSystem(files, { currentDirectory });
6887
const watch = createWatchOfConfigFile("tsconfig.json", host);
6988
checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]);
@@ -73,9 +92,77 @@ namespace ts.tscWatch {
7392
host.runQueuedTimeoutCallbacks();
7493
checkOutputErrorsIncremental(host, getIncrementalErrors(watch));
7594
const afterChange = getOutputFileStampsAndErrors(host, watch, nonLibFiles);
76-
filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ true, /*errorRefershExpected*/ true, beforeChange, afterChange));
77-
filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ true, beforeChange, afterChange));
78-
filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors(file, /*emitExpected*/ false, /*errorRefershExpected*/ false, beforeChange, afterChange));
95+
filesWithNewEmit.forEach(file => verifyOutputFileStampsAndErrors({
96+
file,
97+
jsEmitExpected: !compilerOptions.isolatedModules || fileWithChange === file,
98+
dtsEmitExpected: getEmitDeclarations(compilerOptions),
99+
errorRefershExpected: true,
100+
beforeChangeFileStampsAndErrors: beforeChange,
101+
afterChangeFileStampsAndErrors: afterChange
102+
}));
103+
filesWithOnlyErrorRefresh.forEach(file => verifyOutputFileStampsAndErrors({
104+
file,
105+
jsEmitExpected: false,
106+
dtsEmitExpected: getEmitDeclarations(compilerOptions) && !file.path.endsWith(".d.ts"),
107+
errorRefershExpected: true,
108+
beforeChangeFileStampsAndErrors: beforeChange,
109+
afterChangeFileStampsAndErrors: afterChange
110+
}));
111+
filesNotTouched.forEach(file => verifyOutputFileStampsAndErrors({
112+
file,
113+
jsEmitExpected: false,
114+
dtsEmitExpected: false,
115+
errorRefershExpected: false,
116+
beforeChangeFileStampsAndErrors: beforeChange,
117+
afterChangeFileStampsAndErrors: afterChange
118+
}));
119+
}
120+
121+
function changeCompilerOptions(input: VerifyEmitAndErrorUpdates, additionalOptions: CompilerOptions): File {
122+
const configFile = input.configFile || config;
123+
const content = JSON.parse(configFile.content);
124+
content.compilerOptions = { ...content.compilerOptions, ...additionalOptions };
125+
return { path: configFile.path, content: JSON.stringify(content) };
126+
}
127+
128+
interface VerifyEmitAndErrorUpdates {
129+
change: (host: WatchedSystem) => void;
130+
getInitialErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
131+
getIncrementalErrors: (watch: Watch) => readonly Diagnostic[] | readonly string[];
132+
fileWithChange: File;
133+
filesWithNewEmit: readonly File[];
134+
filesWithOnlyErrorRefresh: readonly File[];
135+
filesNotTouched: readonly File[];
136+
configFile?: File;
137+
}
138+
function verifyEmitAndErrorUpdates(input: VerifyEmitAndErrorUpdates) {
139+
it("with default config", () => {
140+
verifyEmitAndErrorUpdatesWorker({
141+
...input,
142+
configFile: input.configFile || config
143+
});
144+
});
145+
146+
it("with default config and --declaration", () => {
147+
verifyEmitAndErrorUpdatesWorker({
148+
...input,
149+
configFile: changeCompilerOptions(input, { declaration: true })
150+
});
151+
});
152+
153+
it("config with --isolatedModules", () => {
154+
verifyEmitAndErrorUpdatesWorker({
155+
...input,
156+
configFile: changeCompilerOptions(input, { isolatedModules: true })
157+
});
158+
});
159+
160+
it("config with --isolatedModules and --declaration", () => {
161+
verifyEmitAndErrorUpdatesWorker({
162+
...input,
163+
configFile: changeCompilerOptions(input, { isolatedModules: true, declaration: true })
164+
});
165+
});
79166
}
80167

81168
describe("deep import changes", () => {
@@ -93,6 +180,7 @@ console.log(b.c.d);`
93180
addImportedModule(bFile);
94181
addImportedModule(cFile);
95182
verifyEmitAndErrorUpdates({
183+
fileWithChange: cFile,
96184
filesWithNewEmit,
97185
filesWithOnlyErrorRefresh,
98186
filesNotTouched: emptyArray,
@@ -113,7 +201,7 @@ console.log(b.c.d);`
113201
}
114202
}
115203

116-
it("updates errors when deep import file changes", () => {
204+
describe("updates errors when deep import file changes", () => {
117205
const bFile: File = {
118206
path: `${currentDirectory}/b.ts`,
119207
content: `import {C} from './c';
@@ -132,7 +220,7 @@ export class B
132220
verifyDeepImportChange(bFile, cFile);
133221
});
134222

135-
it("updates errors when deep import through declaration file changes", () => {
223+
describe("updates errors when deep import through declaration file changes", () => {
136224
const bFile: File = {
137225
path: `${currentDirectory}/b.d.ts`,
138226
content: `import {C} from './c';
@@ -152,7 +240,7 @@ export class B
152240
});
153241
});
154242

155-
it("updates errors in file not exporting a deep multilevel import that changes", () => {
243+
describe("updates errors in file not exporting a deep multilevel import that changes", () => {
156244
const aFile: File = {
157245
path: `${currentDirectory}/a.ts`,
158246
content: `export interface Point {
@@ -193,6 +281,7 @@ getPoint().c.x;`
193281
content: `import "./d";`
194282
};
195283
verifyEmitAndErrorUpdates({
284+
fileWithChange: aFile,
196285
filesWithNewEmit: [aFile, bFile],
197286
filesWithOnlyErrorRefresh: [cFile, dFile],
198287
filesNotTouched: [eFile],
@@ -265,6 +354,7 @@ export class Data {
265354
filesWithOnlyErrorRefresh.push(lib2Data2);
266355
}
267356
verifyEmitAndErrorUpdates({
357+
fileWithChange: lib1ToolsInterface,
268358
filesWithNewEmit,
269359
filesWithOnlyErrorRefresh,
270360
filesNotTouched: emptyArray,
@@ -276,11 +366,11 @@ export class Data {
276366
]
277367
});
278368
}
279-
it("when there are no circular import and exports", () => {
369+
describe("when there are no circular import and exports", () => {
280370
verifyTransitiveExports(lib2Data);
281371
});
282372

283-
it("when there are circular import and exports", () => {
373+
describe("when there are circular import and exports", () => {
284374
const lib2Data: File = {
285375
path: `${currentDirectory}/lib2/data.ts`,
286376
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";

0 commit comments

Comments
 (0)