diff options
Diffstat (limited to 'js/compiler_test.ts')
-rw-r--r-- | js/compiler_test.ts | 271 |
1 files changed, 217 insertions, 54 deletions
diff --git a/js/compiler_test.ts b/js/compiler_test.ts index 3336b64b4..5c4da22e2 100644 --- a/js/compiler_test.ts +++ b/js/compiler_test.ts @@ -6,7 +6,7 @@ import * as ts from "typescript"; // We use a silly amount of `any` in these tests... // tslint:disable:no-any -const { Compiler, jsonAmdTemplate } = (deno as any)._compiler; +const { DenoCompiler, jsonAmdTemplate } = (deno as any)._compiler; interface ModuleInfo { moduleName: string | undefined; @@ -17,14 +17,16 @@ interface ModuleInfo { sourceMap: string | undefined; } -const compilerInstance = Compiler.instance(); +const compilerInstance = DenoCompiler.instance(); // References to original items we are going to mock const originals = { + _globalEval: (compilerInstance as any)._globalEval, _log: (compilerInstance as any)._log, _os: (compilerInstance as any)._os, _ts: (compilerInstance as any)._ts, - _service: (compilerInstance as any)._service + _service: (compilerInstance as any)._service, + _window: (compilerInstance as any)._window }; enum MediaType { @@ -250,6 +252,7 @@ const emittedFiles = { "/root/project/foo/qat.ts": "console.log('foo');" }; +let globalEvalStack: string[] = []; let getEmitOutputStack: string[] = []; let logStack: any[][] = []; let codeCacheStack: Array<{ @@ -266,6 +269,12 @@ let codeFetchStack: Array<{ let mockDepsStack: string[][] = []; let mockFactoryStack: any[] = []; +function globalEvalMock(x: string): void { + globalEvalStack.push(x); + if (windowMock.define && mockDepsStack.length && mockFactoryStack.length) { + windowMock.define(mockDepsStack.pop(), mockFactoryStack.pop()); + } +} function logMock(...args: any[]): void { logStack.push(args); } @@ -305,7 +314,6 @@ const osMock = { throw new Error(`Unexpected call to os.exit(${code})`); } }; - const tsMock = { createLanguageService(host: ts.LanguageServiceHost): ts.LanguageService { return {} as ts.LanguageService; @@ -355,11 +363,14 @@ const serviceMock = { ); } }; +const windowMock: { define?: any } = {}; const mocks = { + _globalEval: globalEvalMock, _log: logMock, _os: osMock, _ts: tsMock, - _service: serviceMock + _service: serviceMock, + _window: windowMock }; /** @@ -383,6 +394,7 @@ function teardown() { codeCacheStack = []; logStack = []; getEmitOutputStack = []; + globalEvalStack = []; assertEqual(mockDepsStack.length, 0); assertEqual(mockFactoryStack.length, 0); @@ -401,10 +413,7 @@ test(function testJsonAmdTemplate() { factory = f; } - const code = jsonAmdTemplate( - `{ "hello": "world", "foo": "bar" }`, - "example.json" - ); + const code = jsonAmdTemplate(`{ "hello": "world", "foo": "bar" }`); const result = eval(code); assert(result == null); assertEqual(deps && deps.length, 0); @@ -414,51 +423,156 @@ test(function testJsonAmdTemplate() { }); test(function compilerInstance() { - assert(Compiler != null); - assert(Compiler.instance() != null); + assert(DenoCompiler != null); + assert(DenoCompiler.instance() != null); }); // Testing the internal APIs -test(function testGetFilename() { +test(function compilerRun() { + // equal to `deno foo/bar.ts` setup(); + let factoryRun = false; + mockDepsStack.push(["require", "exports", "deno"]); + mockFactoryStack.push((_require, _exports, _deno) => { + factoryRun = true; + assertEqual(typeof _require, "function"); + assertEqual(typeof _exports, "object"); + assert(_deno === deno); + _exports.foo = "bar"; + }); + const moduleMetaData = compilerInstance.run("foo/bar.ts", "/root/project"); + assert(factoryRun); + assert(moduleMetaData.hasRun); + assertEqual(moduleMetaData.sourceCode, fooBarTsSource); + assertEqual(moduleMetaData.outputCode, fooBarTsOutput); + // assertEqual(JSON.stringify(moduleMetaData.sourceMap), fooBarTsSourcemap); + assertEqual(moduleMetaData.exports, { foo: "bar" }); + assertEqual( - compilerInstance.getFilename("foo/bar.ts", "/root/project"), - "/root/project/foo/bar.ts" + codeFetchStack.length, + 1, + "Module should have only been fetched once." ); + assertEqual( + codeCacheStack.length, + 1, + "Compiled code should have only been cached once." + ); + const [codeCacheCall] = codeCacheStack; + assertEqual(codeCacheCall.fileName, "/root/project/foo/bar.ts"); + assertEqual(codeCacheCall.sourceCode, fooBarTsSource); + assertEqual(codeCacheCall.outputCode, fooBarTsOutput); + assertEqual(codeCacheCall.sourceMap, fooBarTsSourcemap); teardown(); }); -test(function testGetOutput() { +test(function compilerRunMultiModule() { + // equal to `deno foo/baz.ts` setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - assertEqual(compilerInstance.getOutput(filename), fooBarTsOutput); + const factoryStack: string[] = []; + const bazDeps = ["require", "exports", "./bar.ts"]; + const bazFactory = (_require, _exports, _bar) => { + factoryStack.push("baz"); + assertEqual(_bar.foo, "bar"); + }; + const barDeps = ["require", "exports", "deno"]; + const barFactory = (_require, _exports, _deno) => { + factoryStack.push("bar"); + _exports.foo = "bar"; + }; + mockDepsStack.push(barDeps); + mockFactoryStack.push(barFactory); + mockDepsStack.push(bazDeps); + mockFactoryStack.push(bazFactory); + compilerInstance.run("foo/baz.ts", "/root/project"); + assertEqual(factoryStack, ["bar", "baz"]); + + assertEqual( + codeFetchStack.length, + 2, + "Modules should have only been fetched once." + ); + assertEqual(codeCacheStack.length, 0, "No code should have been cached."); teardown(); }); -test(function testGetOutputJson() { +test(function compilerRunCircularDependency() { setup(); - const filename = compilerInstance.getFilename( - "./config.json", - "/root/project/loadConfig.ts" - ); - assertEqual( - compilerInstance.getOutput(filename), - jsonAmdTemplate(configJsonSource, filename) - ); + const factoryStack: string[] = []; + const modADeps = ["require", "exports", "./modB.ts"]; + const modAFactory = (_require, _exports, _modB) => { + assertEqual(_modB.foo, "bar"); + factoryStack.push("modA"); + _exports.bar = "baz"; + _modB.assertModA(); + }; + const modBDeps = ["require", "exports", "./modA.ts"]; + const modBFactory = (_require, _exports, _modA) => { + assertEqual(_modA, {}); + factoryStack.push("modB"); + _exports.foo = "bar"; + _exports.assertModA = () => { + assertEqual(_modA, { + bar: "baz" + }); + }; + }; + mockDepsStack.push(modBDeps); + mockFactoryStack.push(modBFactory); + mockDepsStack.push(modADeps); + mockFactoryStack.push(modAFactory); + compilerInstance.run("modA.ts", "/root/project"); + assertEqual(factoryStack, ["modB", "modA"]); + teardown(); }); -test(function testGetSource() { +test(function compilerLoadJsonModule() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - assertEqual(compilerInstance.getSource(filename), fooBarTsSource); + const factoryStack: string[] = []; + const configJsonDeps: string[] = []; + const configJsonFactory = () => { + factoryStack.push("configJson"); + return JSON.parse(configJsonSource); + }; + const loadConfigDeps = ["require", "exports", "./config.json"]; + const loadConfigFactory = (_require, _exports, _config) => { + factoryStack.push("loadConfig"); + assertEqual(_config, JSON.parse(configJsonSource)); + }; + + mockDepsStack.push(configJsonDeps); + mockFactoryStack.push(configJsonFactory); + mockDepsStack.push(loadConfigDeps); + mockFactoryStack.push(loadConfigFactory); + compilerInstance.run("loadConfig.ts", "/root/project"); + assertEqual(factoryStack, ["configJson", "loadConfig"]); + teardown(); }); -test(function testGetOutputUnknownMediaType() { +test(function compilerResolveModule() { + setup(); + const moduleMetaData = compilerInstance.resolveModule( + "foo/baz.ts", + "/root/project" + ); + assertEqual(moduleMetaData.sourceCode, fooBazTsSource); + assertEqual(moduleMetaData.outputCode, fooBazTsOutput); + assertEqual(JSON.stringify(moduleMetaData.sourceMap), fooBazTsSourcemap); + assert(!moduleMetaData.hasRun); + assert(!moduleMetaData.deps); + assertEqual(moduleMetaData.exports, {}); + assertEqual(moduleMetaData.scriptVersion, "1"); + + assertEqual(codeFetchStack.length, 1, "Only initial module is resolved."); + teardown(); +}); + +test(function compilerResolveModuleUnknownMediaType() { setup(); let didThrow = false; try { - compilerInstance.getFilename("some.txt", "/root/project"); + compilerInstance.resolveModule("some.txt", "/root/project"); } catch (e) { assert(e instanceof Error); assertEqual( @@ -471,6 +585,28 @@ test(function testGetOutputUnknownMediaType() { teardown(); }); +test(function compilerGetModuleDependencies() { + setup(); + const bazDeps = ["require", "exports", "./bar.ts"]; + const bazFactory = () => { + throw new Error("Unexpected factory call"); + }; + const barDeps = ["require", "exports", "deno"]; + const barFactory = () => { + throw new Error("Unexpected factory call"); + }; + mockDepsStack.push(barDeps); + mockFactoryStack.push(barFactory); + mockDepsStack.push(bazDeps); + mockFactoryStack.push(bazFactory); + const deps = compilerInstance.getModuleDependencies( + "foo/baz.ts", + "/root/project" + ); + assertEqual(deps, ["/root/project/foo/bar.ts", "/root/project/foo/baz.ts"]); + teardown(); +}); + // TypeScript LanguageServiceHost APIs test(function compilerGetCompilationSettings() { @@ -498,8 +634,7 @@ test(function compilerGetNewLine() { test(function compilerGetScriptFileNames() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - compilerInstance.getOutput(filename); + compilerInstance.run("foo/bar.ts", "/root/project"); const result = compilerInstance.getScriptFileNames(); assertEqual(result.length, 1, "Expected only a single filename."); assertEqual(result[0], "/root/project/foo/bar.ts"); @@ -508,22 +643,21 @@ test(function compilerGetScriptFileNames() { test(function compilerRecompileFlag() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - compilerInstance.getOutput(filename); + compilerInstance.run("foo/bar.ts", "/root/project"); assertEqual( getEmitOutputStack.length, 1, "Expected only a single emitted file." ); // running compiler against same file should use cached code - compilerInstance.getOutput(filename); + compilerInstance.run("foo/bar.ts", "/root/project"); assertEqual( getEmitOutputStack.length, 1, "Expected only a single emitted file." ); compilerInstance.recompile = true; - compilerInstance.getOutput(filename); + compilerInstance.run("foo/bar.ts", "/root/project"); assertEqual(getEmitOutputStack.length, 2, "Expected two emitted file."); assert( getEmitOutputStack[0] === getEmitOutputStack[1], @@ -534,25 +668,43 @@ test(function compilerRecompileFlag() { test(function compilerGetScriptKind() { setup(); - compilerInstance.getFilename("foo.ts", "/moduleKinds"); - compilerInstance.getFilename("foo.d.ts", "/moduleKinds"); - compilerInstance.getFilename("foo.js", "/moduleKinds"); - compilerInstance.getFilename("foo.json", "/moduleKinds"); - compilerInstance.getFilename("foo.txt", "/moduleKinds"); - assertEqual(compilerInstance.getScriptKind("/moduleKinds/foo.ts"), 3); - assertEqual(compilerInstance.getScriptKind("/moduleKinds/foo.d.ts"), 3); - assertEqual(compilerInstance.getScriptKind("/moduleKinds/foo.js"), 1); - assertEqual(compilerInstance.getScriptKind("/moduleKinds/foo.json"), 6); - assertEqual(compilerInstance.getScriptKind("/moduleKinds/foo.txt"), 1); + compilerInstance.resolveModule("foo.ts", "/moduleKinds"); + compilerInstance.resolveModule("foo.d.ts", "/moduleKinds"); + compilerInstance.resolveModule("foo.js", "/moduleKinds"); + compilerInstance.resolveModule("foo.json", "/moduleKinds"); + compilerInstance.resolveModule("foo.txt", "/moduleKinds"); + assertEqual( + compilerInstance.getScriptKind("/moduleKinds/foo.ts"), + ts.ScriptKind.TS + ); + assertEqual( + compilerInstance.getScriptKind("/moduleKinds/foo.d.ts"), + ts.ScriptKind.TS + ); + assertEqual( + compilerInstance.getScriptKind("/moduleKinds/foo.js"), + ts.ScriptKind.JS + ); + assertEqual( + compilerInstance.getScriptKind("/moduleKinds/foo.json"), + ts.ScriptKind.JSON + ); + assertEqual( + compilerInstance.getScriptKind("/moduleKinds/foo.txt"), + ts.ScriptKind.JS + ); teardown(); }); test(function compilerGetScriptVersion() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - compilerInstance.getOutput(filename); + const moduleMetaData = compilerInstance.resolveModule( + "foo/bar.ts", + "/root/project" + ); + compilerInstance.compile(moduleMetaData); assertEqual( - compilerInstance.getScriptVersion(filename), + compilerInstance.getScriptVersion(moduleMetaData.fileName), "1", "Expected known module to have script version of 1" ); @@ -569,8 +721,11 @@ test(function compilerGetScriptVersionUnknown() { test(function compilerGetScriptSnapshot() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - const result = compilerInstance.getScriptSnapshot(filename); + const moduleMetaData = compilerInstance.resolveModule( + "foo/bar.ts", + "/root/project" + ); + const result = compilerInstance.getScriptSnapshot(moduleMetaData.fileName); assert(result != null, "Expected snapshot to be defined."); assertEqual(result.getLength(), fooBarTsSource.length); assertEqual( @@ -582,6 +737,11 @@ test(function compilerGetScriptSnapshot() { // This is and optional part of the `IScriptSnapshot` API which we don't // define, os checking for the lack of this property. assert(!("dispose" in result)); + + assert( + result === moduleMetaData, + "result should strictly equal moduleMetaData" + ); teardown(); }); @@ -615,8 +775,11 @@ test(function compilerReadFile() { test(function compilerFileExists() { setup(); - const filename = compilerInstance.getFilename("foo/bar.ts", "/root/project"); - assert(compilerInstance.fileExists(filename)); + const moduleMetaData = compilerInstance.resolveModule( + "foo/bar.ts", + "/root/project" + ); + assert(compilerInstance.fileExists(moduleMetaData.fileName)); assert(compilerInstance.fileExists("$asset$/lib.deno_runtime.d.ts")); assertEqual( compilerInstance.fileExists("/root/project/unknown-module.ts"), |