diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index c571a7d9f94..01637a7e822 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -189,6 +189,15 @@ module FourSlash {
// Whether or not we should format on keystrokes
public enableFormatting = true;
+ // Whether or not to generate .d.ts file
+ public enableDeclaration = false;
+
+ // Whether or not to generate one output javascript file
+ public enableSingleOutputFile = false;
+
+ // Output filename for single-output-file option
+ public singleOutputFilename: string = undefined;
+
public formatCodeOptions: ts.FormatCodeOptions;
public cancellationToken: TestCancellationToken;
@@ -452,6 +461,48 @@ module FourSlash {
}
}
+ public verifyEmitOutput(state: ts.EmitOutputResult, filename?: string) {
+ if (this.enableDeclaration) {
+ this.languageServiceShimHost.setCompilationSettings({generateDeclarationFiles: true});
+ }
+
+ if (this.enableSingleOutputFile) {
+ this.languageServiceShimHost.setCompilationSettings({ outFileOption: this.singleOutputFilename });
+ }
+
+ var expectedFilenames:string[] = [];
+ if (filename !== undefined) {
+ expectedFilenames = filename.split(" ");
+ }
+
+ var emit = this.languageService.getEmitOutput(this.activeFile.fileName);
+
+ if (emit.emitOutputResult !== state) {
+ throw new Error("Expected emitOutputResult '" + state + "', but actual emitOutputResult '" + emit.emitOutputResult + "'");
+ }
+
+ var passed = true;
+ if (emit.outputFiles.length > 0) {
+ passed = expectedFilenames.every(expectedFilename => {
+ return emit.outputFiles.some(outputFile => {
+ return outputFile.name === expectedFilename;
+ });
+ });
+ }
+
+ if (!passed) {
+ var errorMessage = "Expected outputFilename '" + filename + "', but actualy outputFilename '";
+ emit.outputFiles.forEach((outputFile, idx, array) => {
+ errorMessage += outputFile.name;
+ if (idx !== emit.outputFiles.length - 1) {
+ errorMessage += " ";
+ }
+ });
+ errorMessage += "'";
+ throw new Error(errorMessage);
+ }
+ }
+
public verifyMemberListContains(symbol: string, type?: string, docComment?: string, fullSymbolName?: string, kind?: string) {
this.scenarioActions.push('');
this.scenarioActions.push('');
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 1a02157d729..e15063204ca 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -135,6 +135,8 @@ module Harness.LanguageService {
private fileNameToScript: ts.Map = {};
+ private settings: any = {};
+
constructor(private cancellationToken: ts.CancellationToken = CancellationToken.None) {
}
@@ -179,6 +181,14 @@ module Harness.LanguageService {
throw new Error("No script with name '" + fileName + "'");
}
+ public getDefaultLibFilename(): string {
+ return undefined;
+ }
+
+ public getCurrentDirectory(): string {
+ return undefined;
+ }
+
//////////////////////////////////////////////////////////////////////
// ILogger implementation
//
@@ -199,7 +209,7 @@ module Harness.LanguageService {
/// Returns json for Tools.CompilationSettings
public getCompilationSettings(): string {
- return JSON.stringify({}); // i.e. default settings
+ return JSON.stringify(this.settings);
}
public getCancellationToken(): ts.CancellationToken {
@@ -236,6 +246,12 @@ module Harness.LanguageService {
return this.ls;
}
+ public setCompilationSettings(settings: any) {
+ for (var key in settings) {
+ this.settings[key] = settings[key];
+ }
+ }
+
/** Return a new instance of the classifier service shim */
public getClassifier(): ts.ClassifierShim {
return new TypeScript.Services.TypeScriptServicesFactory().createClassifierShim(this);
diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts
index b0f3e939d30..7a5fa51e147 100644
--- a/src/harness/projectsRunner.ts
+++ b/src/harness/projectsRunner.ts
@@ -226,7 +226,7 @@ class ProjectRunner extends RunnerBase {
? filename
: ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(filename);
- var diskRelativeName = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, diskFileName, getCurrentDirectory(), false);
+ var diskRelativeName = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, diskFileName, getCurrentDirectory, false);
if (ts.isRootedDiskPath(diskRelativeName) || diskRelativeName.substr(0, 3) === "../") {
// If the generated output file recides in the parent folder or is rooted path,
// we need to instead create files that can live in the project reference folder
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index b4080d12e2f..91bce54525a 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -44,6 +44,14 @@ enum TypingFidelity {
High = FourSlash.TypingFidelity.High
}
+// We have to duplicate EmitOutputResult from Services.ts to expose the enum to getEmitOutput testcases in fourslah
+enum EmitOutputResult {
+ Succeeded,
+ FailedBecauseOfSyntaxErrors,
+ FailedBecauseOfCompilerOptionsErrors,
+ FailedToGenerateDeclarationsBecauseOfSemanticErrors
+}
+
module FourSlashInterface {
declare var FourSlash;
@@ -255,6 +263,10 @@ module FourSlashInterface {
FourSlash.currentTestState.verifyEval(expr, value);
}
+ public emitOutput(expectedState: EmitOutputResult, expectedFilename?: string) {
+ FourSlash.currentTestState.verifyEmitOutput(expectedState, expectedFilename);
+ }
+
public currentLineContentIs(text: string) {
FourSlash.currentTestState.verifyCurrentLineContent(text);
}
@@ -431,6 +443,23 @@ module FourSlashInterface {
public disableFormatting() {
FourSlash.currentTestState.enableFormatting = false;
}
+
+ public enableDeclaration() {
+ FourSlash.currentTestState.enableDeclaration = true;
+ }
+
+ public disableDeclaration() {
+ FourSlash.currentTestState.enableDeclaration = false;
+ }
+
+ public enableSingleOutputFile(outputFilename: string) {
+ FourSlash.currentTestState.enableSingleOutputFile = true;
+ FourSlash.currentTestState.singleOutputFilename = outputFilename;
+ }
+
+ public disableSingleOutputFile() {
+ FourSlash.currentTestState.enableSingleOutputFile = false;
+ }
}
export class debug {