michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: 'use strict'; michael@0: michael@0: let { michael@0: Loader, main, unload, parseStack, generateMap, resolve, join michael@0: } = require('toolkit/loader'); michael@0: let { readURI } = require('sdk/net/url'); michael@0: michael@0: let root = module.uri.substr(0, module.uri.lastIndexOf('/')) michael@0: michael@0: michael@0: // The following adds Debugger constructor to the global namespace. michael@0: const { Cu } = require('chrome'); michael@0: const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {}); michael@0: addDebuggerToGlobal(this); michael@0: michael@0: exports['test resolve'] = function (assert) { michael@0: let cuddlefish_id = 'sdk/loader/cuddlefish'; michael@0: assert.equal(resolve('../index.js', './dir/c.js'), './index.js'); michael@0: assert.equal(resolve('./index.js', './dir/c.js'), './dir/index.js'); michael@0: assert.equal(resolve('./dir/c.js', './index.js'), './dir/c.js'); michael@0: assert.equal(resolve('../utils/file.js', './dir/b.js'), './utils/file.js'); michael@0: michael@0: assert.equal(resolve('../utils/./file.js', './dir/b.js'), './utils/file.js'); michael@0: assert.equal(resolve('../utils/file.js', './'), './../utils/file.js'); michael@0: assert.equal(resolve('./utils/file.js', './'), './utils/file.js'); michael@0: assert.equal(resolve('./utils/file.js', './index.js'), './utils/file.js'); michael@0: michael@0: assert.equal(resolve('../utils/./file.js', cuddlefish_id), 'sdk/utils/file.js'); michael@0: assert.equal(resolve('../utils/file.js', cuddlefish_id), 'sdk/utils/file.js'); michael@0: assert.equal(resolve('./utils/file.js', cuddlefish_id), 'sdk/loader/utils/file.js'); michael@0: michael@0: assert.equal(resolve('..//index.js', './dir/c.js'), './index.js'); michael@0: assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'resource://thing/utils/file.js'), 'resource://gre/modules/XPCOMUtils.jsm'); michael@0: assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'chrome://thing/utils/file.js'), 'chrome://gre/modules/XPCOMUtils.jsm'); michael@0: assert.equal(resolve('../../a/b/c.json', 'file:///thing/utils/file.js'), 'file:///a/b/c.json'); michael@0: michael@0: // Does not change absolute paths michael@0: assert.equal(resolve('resource://gre/modules/file.js', './dir/b.js'), michael@0: 'resource://gre/modules/file.js'); michael@0: assert.equal(resolve('file:///gre/modules/file.js', './dir/b.js'), michael@0: 'file:///gre/modules/file.js'); michael@0: assert.equal(resolve('/root.js', './dir/b.js'), michael@0: '/root.js'); michael@0: }; michael@0: michael@0: exports['test join'] = function (assert) { michael@0: assert.equal(join('a/path', '../../../module'), '../module'); michael@0: assert.equal(join('a/path/to', '../module'), 'a/path/module'); michael@0: assert.equal(join('a/path/to', './module'), 'a/path/to/module'); michael@0: assert.equal(join('a/path/to', '././../module'), 'a/path/module'); michael@0: assert.equal(join('resource://my/path/yeah/yuh', '../whoa'), michael@0: 'resource://my/path/yeah/whoa'); michael@0: assert.equal(join('resource://my/path/yeah/yuh', './whoa'), michael@0: 'resource://my/path/yeah/yuh/whoa'); michael@0: assert.equal(join('file:///my/path/yeah/yuh', '../whoa'), michael@0: 'file:///my/path/yeah/whoa'); michael@0: assert.equal(join('file:///my/path/yeah/yuh', './whoa'), michael@0: 'file:///my/path/yeah/yuh/whoa'); michael@0: assert.equal(join('a/path/to', '..//module'), 'a/path/module'); michael@0: }; michael@0: michael@0: exports['test dependency cycles'] = function(assert) { michael@0: let uri = root + '/fixtures/loader/cycles/'; michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: michael@0: let program = main(loader, 'main'); michael@0: michael@0: assert.equal(program.a.b, program.b, 'module `a` gets correct `b`'); michael@0: assert.equal(program.b.a, program.a, 'module `b` gets correct `a`'); michael@0: assert.equal(program.c.main, program, 'module `c` gets correct `main`'); michael@0: michael@0: unload(loader); michael@0: } michael@0: michael@0: exports['test syntax errors'] = function(assert) { michael@0: let uri = root + '/fixtures/loader/syntax-error/'; michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: michael@0: try { michael@0: let program = main(loader, 'main'); michael@0: } catch (error) { michael@0: assert.equal(error.name, "SyntaxError", "throws syntax error"); michael@0: assert.equal(error.fileName.split("/").pop(), "error.js", michael@0: "Error contains filename"); michael@0: assert.equal(error.lineNumber, 11, "error is on line 11"); michael@0: let stack = parseStack(error.stack); michael@0: michael@0: assert.equal(stack.pop().fileName, uri + "error.js", michael@0: "last frame file containing syntax error"); michael@0: assert.equal(stack.pop().fileName, uri + "main.js", michael@0: "previous frame is a requirer module"); michael@0: assert.equal(stack.pop().fileName, module.uri, michael@0: "previous to it is a test module"); michael@0: michael@0: } finally { michael@0: unload(loader); michael@0: } michael@0: } michael@0: michael@0: exports['test sandboxes are not added if error'] = function (assert) { michael@0: let uri = root + '/fixtures/loader/missing-twice/'; michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: let program = main(loader, 'main'); michael@0: assert.ok(!(uri + 'not-found.js' in loader.sandboxes), 'not-found.js not in loader.sandboxes'); michael@0: } michael@0: michael@0: exports['test missing module'] = function(assert) { michael@0: let uri = root + '/fixtures/loader/missing/' michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: michael@0: try { michael@0: let program = main(loader, 'main') michael@0: } catch (error) { michael@0: assert.equal(error.message, "Module `not-found` is not found at " + michael@0: uri + "not-found.js", "throws if error not found"); michael@0: michael@0: assert.equal(error.fileName.split("/").pop(), "main.js", michael@0: "Error fileName is requirer module"); michael@0: michael@0: assert.equal(error.lineNumber, 7, "error is on line 7"); michael@0: michael@0: let stack = parseStack(error.stack); michael@0: michael@0: assert.equal(stack.pop().fileName, uri + "main.js", michael@0: "loader stack is omitted"); michael@0: michael@0: assert.equal(stack.pop().fileName, module.uri, michael@0: "previous in the stack is test module"); michael@0: } finally { michael@0: unload(loader); michael@0: } michael@0: } michael@0: michael@0: exports["test invalid module not cached and throws everytime"] = function(assert) { michael@0: let uri = root + "/fixtures/loader/missing-twice/"; michael@0: let loader = Loader({ paths: { "": uri } }); michael@0: michael@0: let { firstError, secondError, invalidJSON1, invalidJSON2 } = main(loader, "main"); michael@0: assert.equal(firstError.message, "Module `not-found` is not found at " + michael@0: uri + "not-found.js", "throws on first invalid require"); michael@0: assert.equal(firstError.lineNumber, 8, "first error is on line 7"); michael@0: assert.equal(secondError.message, "Module `not-found` is not found at " + michael@0: uri + "not-found.js", "throws on second invalid require"); michael@0: assert.equal(secondError.lineNumber, 14, "second error is on line 14"); michael@0: michael@0: assert.equal(invalidJSON1.message, michael@0: "JSON.parse: unexpected character at line 1 column 1 of the JSON data", michael@0: "throws on invalid JSON"); michael@0: assert.equal(invalidJSON2.message, michael@0: "JSON.parse: unexpected character at line 1 column 1 of the JSON data", michael@0: "throws on invalid JSON second time"); michael@0: }; michael@0: michael@0: exports['test exceptions in modules'] = function(assert) { michael@0: let uri = root + '/fixtures/loader/exceptions/' michael@0: michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: michael@0: try { michael@0: let program = main(loader, 'main') michael@0: } catch (error) { michael@0: assert.equal(error.message, "Boom!", "thrown errors propagate"); michael@0: michael@0: assert.equal(error.fileName.split("/").pop(), "boomer.js", michael@0: "Error comes from the module that threw it"); michael@0: michael@0: assert.equal(error.lineNumber, 8, "error is on line 8"); michael@0: michael@0: let stack = parseStack(error.stack); michael@0: michael@0: let frame = stack.pop() michael@0: assert.equal(frame.fileName, uri + "boomer.js", michael@0: "module that threw is first in the stack"); michael@0: assert.equal(frame.name, "exports.boom", michael@0: "name is in the stack"); michael@0: michael@0: frame = stack.pop() michael@0: assert.equal(frame.fileName, uri + "main.js", michael@0: "module that called it is next in the stack"); michael@0: assert.equal(frame.lineNumber, 9, "caller line is in the stack"); michael@0: michael@0: michael@0: assert.equal(stack.pop().fileName, module.uri, michael@0: "this test module is next in the stack"); michael@0: } finally { michael@0: unload(loader); michael@0: } michael@0: } michael@0: michael@0: exports['test early errors in module'] = function(assert) { michael@0: let uri = root + '/fixtures/loader/errors/'; michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: michael@0: try { michael@0: let program = main(loader, 'main') michael@0: } catch (error) { michael@0: assert.equal(String(error), michael@0: "Error: opening input stream (invalid filename?)", michael@0: "thrown errors propagate"); michael@0: michael@0: assert.equal(error.fileName.split("/").pop(), "boomer.js", michael@0: "Error comes from the module that threw it"); michael@0: michael@0: assert.equal(error.lineNumber, 7, "error is on line 7"); michael@0: michael@0: let stack = parseStack(error.stack); michael@0: michael@0: let frame = stack.pop() michael@0: assert.equal(frame.fileName, uri + "boomer.js", michael@0: "module that threw is first in the stack"); michael@0: michael@0: frame = stack.pop() michael@0: assert.equal(frame.fileName, uri + "main.js", michael@0: "module that called it is next in the stack"); michael@0: assert.equal(frame.lineNumber, 7, "caller line is in the stack"); michael@0: michael@0: michael@0: assert.equal(stack.pop().fileName, module.uri, michael@0: "this test module is next in the stack"); michael@0: } finally { michael@0: unload(loader); michael@0: } michael@0: }; michael@0: michael@0: exports['test require json'] = function (assert) { michael@0: let data = require('./fixtures/loader/json/manifest.json'); michael@0: assert.equal(data.name, 'Jetpack Loader Test', 'loads json with strings'); michael@0: assert.equal(data.version, '1.0.1', 'loads json with strings'); michael@0: assert.equal(data.dependencies.async, '*', 'loads json with objects'); michael@0: assert.equal(data.dependencies.underscore, '*', 'loads json with objects'); michael@0: assert.equal(data.contributors.length, 4, 'loads json with arrays'); michael@0: assert.ok(Array.isArray(data.contributors), 'loads json with arrays'); michael@0: data.version = '2.0.0'; michael@0: let newdata = require('./fixtures/loader/json/manifest.json'); michael@0: assert.equal(newdata.version, '2.0.0', michael@0: 'JSON objects returned should be cached and the same instance'); michael@0: michael@0: try { michael@0: require('./fixtures/loader/json/invalid.json'); michael@0: assert.fail('Error not thrown when loading invalid json'); michael@0: } catch (err) { michael@0: assert.ok(err, 'error thrown when loading invalid json'); michael@0: assert.ok(/JSON\.parse/.test(err.message), michael@0: 'should thrown an error from JSON.parse, not attempt to load .json.js'); michael@0: } michael@0: michael@0: // Try again to ensure an empty module isn't loaded from cache michael@0: try { michael@0: require('./fixtures/loader/json/invalid.json'); michael@0: assert.fail('Error not thrown when loading invalid json a second time'); michael@0: } catch (err) { michael@0: assert.ok(err, michael@0: 'error thrown when loading invalid json a second time'); michael@0: assert.ok(/JSON\.parse/.test(err.message), michael@0: 'should thrown an error from JSON.parse a second time, not attempt to load .json.js'); michael@0: } michael@0: }; michael@0: michael@0: exports['test setting metadata for newly created sandboxes'] = function(assert) { michael@0: let addonID = 'random-addon-id'; michael@0: let uri = root + '/fixtures/loader/cycles/'; michael@0: let loader = Loader({ paths: { '': uri }, id: addonID }); michael@0: michael@0: let dbg = new Debugger(); michael@0: dbg.onNewGlobalObject = function(global) { michael@0: dbg.onNewGlobalObject = undefined; michael@0: michael@0: let metadata = Cu.getSandboxMetadata(global.unsafeDereference()); michael@0: assert.ok(metadata, 'this global has attached metadata'); michael@0: assert.equal(metadata.URI, uri + 'main.js', 'URI is set properly'); michael@0: assert.equal(metadata.addonID, addonID, 'addon ID is set'); michael@0: } michael@0: michael@0: let program = main(loader, 'main'); michael@0: }; michael@0: michael@0: exports['test require .json, .json.js'] = function (assert) { michael@0: let testjson = require('./fixtures/loader/json/test.json'); michael@0: assert.equal(testjson.filename, 'test.json', michael@0: 'require("./x.json") should load x.json, not x.json.js'); michael@0: michael@0: let nodotjson = require('./fixtures/loader/json/nodotjson.json'); michael@0: assert.equal(nodotjson.filename, 'nodotjson.json.js', michael@0: 'require("./x.json") should load x.json.js when x.json does not exist'); michael@0: nodotjson.data.prop = 'hydralisk'; michael@0: michael@0: // require('nodotjson.json') and require('nodotjson.json.js') michael@0: // should resolve to the same file michael@0: let nodotjsonjs = require('./fixtures/loader/json/nodotjson.json.js'); michael@0: assert.equal(nodotjsonjs.data.prop, 'hydralisk', michael@0: 'js modules are cached whether access via .json.js or .json'); michael@0: }; michael@0: michael@0: exports['test invisibleToDebugger: false'] = function (assert) { michael@0: let uri = root + '/fixtures/loader/cycles/'; michael@0: let loader = Loader({ paths: { '': uri } }); michael@0: main(loader, 'main'); michael@0: michael@0: let dbg = new Debugger(); michael@0: let sandbox = loader.sandboxes[uri + 'main.js']; michael@0: michael@0: try { michael@0: dbg.addDebuggee(sandbox); michael@0: assert.ok(true, 'debugger added visible value'); michael@0: } catch(e) { michael@0: assert.fail('debugger could not add visible value'); michael@0: } michael@0: }; michael@0: michael@0: exports['test invisibleToDebugger: true'] = function (assert) { michael@0: let uri = root + '/fixtures/loader/cycles/'; michael@0: let loader = Loader({ paths: { '': uri }, invisibleToDebugger: true }); michael@0: main(loader, 'main'); michael@0: michael@0: let dbg = new Debugger(); michael@0: let sandbox = loader.sandboxes[uri + 'main.js']; michael@0: michael@0: try { michael@0: dbg.addDebuggee(sandbox); michael@0: assert.fail('debugger added invisible value'); michael@0: } catch(e) { michael@0: assert.ok(true, 'debugger did not add invisible value'); michael@0: } michael@0: }; michael@0: michael@0: exports['test console global by default'] = function (assert) { michael@0: let uri = root + '/fixtures/loader/globals/'; michael@0: let loader = Loader({ paths: { '': uri }}); michael@0: let program = main(loader, 'main'); michael@0: michael@0: assert.ok(typeof program.console === 'object', 'global `console` exists'); michael@0: assert.ok(typeof program.console.log === 'function', 'global `console.log` exists'); michael@0: michael@0: let loader2 = Loader({ paths: { '': uri }, globals: { console: fakeConsole }}); michael@0: let program2 = main(loader2, 'main'); michael@0: michael@0: assert.equal(program2.console, fakeConsole, michael@0: 'global console can be overridden with Loader options'); michael@0: function fakeConsole () {}; michael@0: }; michael@0: michael@0: require('test').run(exports);