test-loader.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. let {
  6. Loader, main, unload, parseStack, generateMap, resolve, join
  7. } = require('toolkit/loader');
  8. let { readURI } = require('sdk/net/url');
  9. let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
  10. // The following adds Debugger constructor to the global namespace.
  11. const { Cu } = require('chrome');
  12. const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {});
  13. addDebuggerToGlobal(this);
  14. exports['test resolve'] = function (assert) {
  15. let cuddlefish_id = 'sdk/loader/cuddlefish';
  16. assert.equal(resolve('../index.js', './dir/c.js'), './index.js');
  17. assert.equal(resolve('./index.js', './dir/c.js'), './dir/index.js');
  18. assert.equal(resolve('./dir/c.js', './index.js'), './dir/c.js');
  19. assert.equal(resolve('../utils/file.js', './dir/b.js'), './utils/file.js');
  20. assert.equal(resolve('../utils/./file.js', './dir/b.js'), './utils/file.js');
  21. assert.equal(resolve('../utils/file.js', './'), './../utils/file.js');
  22. assert.equal(resolve('./utils/file.js', './'), './utils/file.js');
  23. assert.equal(resolve('./utils/file.js', './index.js'), './utils/file.js');
  24. assert.equal(resolve('../utils/./file.js', cuddlefish_id), 'sdk/utils/file.js');
  25. assert.equal(resolve('../utils/file.js', cuddlefish_id), 'sdk/utils/file.js');
  26. assert.equal(resolve('./utils/file.js', cuddlefish_id), 'sdk/loader/utils/file.js');
  27. assert.equal(resolve('..//index.js', './dir/c.js'), './index.js');
  28. assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'resource://thing/utils/file.js'), 'resource://gre/modules/XPCOMUtils.jsm');
  29. assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'chrome://thing/utils/file.js'), 'chrome://gre/modules/XPCOMUtils.jsm');
  30. assert.equal(resolve('../../a/b/c.json', 'file:///thing/utils/file.js'), 'file:///a/b/c.json');
  31. // Does not change absolute paths
  32. assert.equal(resolve('resource://gre/modules/file.js', './dir/b.js'),
  33. 'resource://gre/modules/file.js');
  34. assert.equal(resolve('file:///gre/modules/file.js', './dir/b.js'),
  35. 'file:///gre/modules/file.js');
  36. assert.equal(resolve('/root.js', './dir/b.js'),
  37. '/root.js');
  38. };
  39. exports['test join'] = function (assert) {
  40. assert.equal(join('a/path', '../../../module'), '../module');
  41. assert.equal(join('a/path/to', '../module'), 'a/path/module');
  42. assert.equal(join('a/path/to', './module'), 'a/path/to/module');
  43. assert.equal(join('a/path/to', '././../module'), 'a/path/module');
  44. assert.equal(join('resource://my/path/yeah/yuh', '../whoa'),
  45. 'resource://my/path/yeah/whoa');
  46. assert.equal(join('resource://my/path/yeah/yuh', './whoa'),
  47. 'resource://my/path/yeah/yuh/whoa');
  48. assert.equal(join('file:///my/path/yeah/yuh', '../whoa'),
  49. 'file:///my/path/yeah/whoa');
  50. assert.equal(join('file:///my/path/yeah/yuh', './whoa'),
  51. 'file:///my/path/yeah/yuh/whoa');
  52. assert.equal(join('a/path/to', '..//module'), 'a/path/module');
  53. };
  54. exports['test dependency cycles'] = function(assert) {
  55. let uri = root + '/fixtures/loader/cycles/';
  56. let loader = Loader({ paths: { '': uri } });
  57. let program = main(loader, 'main');
  58. assert.equal(program.a.b, program.b, 'module `a` gets correct `b`');
  59. assert.equal(program.b.a, program.a, 'module `b` gets correct `a`');
  60. assert.equal(program.c.main, program, 'module `c` gets correct `main`');
  61. unload(loader);
  62. }
  63. exports['test syntax errors'] = function(assert) {
  64. let uri = root + '/fixtures/loader/syntax-error/';
  65. let loader = Loader({ paths: { '': uri } });
  66. try {
  67. let program = main(loader, 'main');
  68. } catch (error) {
  69. assert.equal(error.name, "SyntaxError", "throws syntax error");
  70. assert.equal(error.fileName.split("/").pop(), "error.js",
  71. "Error contains filename");
  72. assert.equal(error.lineNumber, 11, "error is on line 11");
  73. let stack = parseStack(error.stack);
  74. assert.equal(stack.pop().fileName, uri + "error.js",
  75. "last frame file containing syntax error");
  76. assert.equal(stack.pop().fileName, uri + "main.js",
  77. "previous frame is a requirer module");
  78. assert.equal(stack.pop().fileName, module.uri,
  79. "previous to it is a test module");
  80. } finally {
  81. unload(loader);
  82. }
  83. }
  84. exports['test sandboxes are not added if error'] = function (assert) {
  85. let uri = root + '/fixtures/loader/missing-twice/';
  86. let loader = Loader({ paths: { '': uri } });
  87. let program = main(loader, 'main');
  88. assert.ok(!(uri + 'not-found.js' in loader.sandboxes), 'not-found.js not in loader.sandboxes');
  89. }
  90. exports['test missing module'] = function(assert) {
  91. let uri = root + '/fixtures/loader/missing/'
  92. let loader = Loader({ paths: { '': uri } });
  93. try {
  94. let program = main(loader, 'main')
  95. } catch (error) {
  96. assert.equal(error.message, "Module `not-found` is not found at " +
  97. uri + "not-found.js", "throws if error not found");
  98. assert.equal(error.fileName.split("/").pop(), "main.js",
  99. "Error fileName is requirer module");
  100. assert.equal(error.lineNumber, 7, "error is on line 7");
  101. let stack = parseStack(error.stack);
  102. assert.equal(stack.pop().fileName, uri + "main.js",
  103. "loader stack is omitted");
  104. assert.equal(stack.pop().fileName, module.uri,
  105. "previous in the stack is test module");
  106. } finally {
  107. unload(loader);
  108. }
  109. }
  110. exports["test invalid module not cached and throws everytime"] = function(assert) {
  111. let uri = root + "/fixtures/loader/missing-twice/";
  112. let loader = Loader({ paths: { "": uri } });
  113. let { firstError, secondError, invalidJSON1, invalidJSON2 } = main(loader, "main");
  114. assert.equal(firstError.message, "Module `not-found` is not found at " +
  115. uri + "not-found.js", "throws on first invalid require");
  116. assert.equal(firstError.lineNumber, 8, "first error is on line 7");
  117. assert.equal(secondError.message, "Module `not-found` is not found at " +
  118. uri + "not-found.js", "throws on second invalid require");
  119. assert.equal(secondError.lineNumber, 14, "second error is on line 14");
  120. assert.equal(invalidJSON1.message,
  121. "JSON.parse: unexpected character at line 1 column 1 of the JSON data",
  122. "throws on invalid JSON");
  123. assert.equal(invalidJSON2.message,
  124. "JSON.parse: unexpected character at line 1 column 1 of the JSON data",
  125. "throws on invalid JSON second time");
  126. };
  127. exports['test exceptions in modules'] = function(assert) {
  128. let uri = root + '/fixtures/loader/exceptions/'
  129. let loader = Loader({ paths: { '': uri } });
  130. try {
  131. let program = main(loader, 'main')
  132. } catch (error) {
  133. assert.equal(error.message, "Boom!", "thrown errors propagate");
  134. assert.equal(error.fileName.split("/").pop(), "boomer.js",
  135. "Error comes from the module that threw it");
  136. assert.equal(error.lineNumber, 8, "error is on line 8");
  137. let stack = parseStack(error.stack);
  138. let frame = stack.pop()
  139. assert.equal(frame.fileName, uri + "boomer.js",
  140. "module that threw is first in the stack");
  141. assert.equal(frame.name, "exports.boom",
  142. "name is in the stack");
  143. frame = stack.pop()
  144. assert.equal(frame.fileName, uri + "main.js",
  145. "module that called it is next in the stack");
  146. assert.equal(frame.lineNumber, 9, "caller line is in the stack");
  147. assert.equal(stack.pop().fileName, module.uri,
  148. "this test module is next in the stack");
  149. } finally {
  150. unload(loader);
  151. }
  152. }
  153. exports['test early errors in module'] = function(assert) {
  154. let uri = root + '/fixtures/loader/errors/';
  155. let loader = Loader({ paths: { '': uri } });
  156. try {
  157. let program = main(loader, 'main')
  158. } catch (error) {
  159. assert.equal(String(error),
  160. "Error: opening input stream (invalid filename?)",
  161. "thrown errors propagate");
  162. assert.equal(error.fileName.split("/").pop(), "boomer.js",
  163. "Error comes from the module that threw it");
  164. assert.equal(error.lineNumber, 7, "error is on line 7");
  165. let stack = parseStack(error.stack);
  166. let frame = stack.pop()
  167. assert.equal(frame.fileName, uri + "boomer.js",
  168. "module that threw is first in the stack");
  169. frame = stack.pop()
  170. assert.equal(frame.fileName, uri + "main.js",
  171. "module that called it is next in the stack");
  172. assert.equal(frame.lineNumber, 7, "caller line is in the stack");
  173. assert.equal(stack.pop().fileName, module.uri,
  174. "this test module is next in the stack");
  175. } finally {
  176. unload(loader);
  177. }
  178. };
  179. exports['test require json'] = function (assert) {
  180. let data = require('./fixtures/loader/json/manifest.json');
  181. assert.equal(data.name, 'Jetpack Loader Test', 'loads json with strings');
  182. assert.equal(data.version, '1.0.1', 'loads json with strings');
  183. assert.equal(data.dependencies.async, '*', 'loads json with objects');
  184. assert.equal(data.dependencies.underscore, '*', 'loads json with objects');
  185. assert.equal(data.contributors.length, 4, 'loads json with arrays');
  186. assert.ok(Array.isArray(data.contributors), 'loads json with arrays');
  187. data.version = '2.0.0';
  188. let newdata = require('./fixtures/loader/json/manifest.json');
  189. assert.equal(newdata.version, '2.0.0',
  190. 'JSON objects returned should be cached and the same instance');
  191. try {
  192. require('./fixtures/loader/json/invalid.json');
  193. assert.fail('Error not thrown when loading invalid json');
  194. } catch (err) {
  195. assert.ok(err, 'error thrown when loading invalid json');
  196. assert.ok(/JSON\.parse/.test(err.message),
  197. 'should thrown an error from JSON.parse, not attempt to load .json.js');
  198. }
  199. // Try again to ensure an empty module isn't loaded from cache
  200. try {
  201. require('./fixtures/loader/json/invalid.json');
  202. assert.fail('Error not thrown when loading invalid json a second time');
  203. } catch (err) {
  204. assert.ok(err,
  205. 'error thrown when loading invalid json a second time');
  206. assert.ok(/JSON\.parse/.test(err.message),
  207. 'should thrown an error from JSON.parse a second time, not attempt to load .json.js');
  208. }
  209. };
  210. exports['test setting metadata for newly created sandboxes'] = function(assert) {
  211. let addonID = 'random-addon-id';
  212. let uri = root + '/fixtures/loader/cycles/';
  213. let loader = Loader({ paths: { '': uri }, id: addonID });
  214. let dbg = new Debugger();
  215. dbg.onNewGlobalObject = function(global) {
  216. dbg.onNewGlobalObject = undefined;
  217. let metadata = Cu.getSandboxMetadata(global.unsafeDereference());
  218. assert.ok(metadata, 'this global has attached metadata');
  219. assert.equal(metadata.URI, uri + 'main.js', 'URI is set properly');
  220. assert.equal(metadata.addonID, addonID, 'addon ID is set');
  221. }
  222. let program = main(loader, 'main');
  223. };
  224. exports['test require .json, .json.js'] = function (assert) {
  225. let testjson = require('./fixtures/loader/json/test.json');
  226. assert.equal(testjson.filename, 'test.json',
  227. 'require("./x.json") should load x.json, not x.json.js');
  228. let nodotjson = require('./fixtures/loader/json/nodotjson.json');
  229. assert.equal(nodotjson.filename, 'nodotjson.json.js',
  230. 'require("./x.json") should load x.json.js when x.json does not exist');
  231. nodotjson.data.prop = 'hydralisk';
  232. // require('nodotjson.json') and require('nodotjson.json.js')
  233. // should resolve to the same file
  234. let nodotjsonjs = require('./fixtures/loader/json/nodotjson.json.js');
  235. assert.equal(nodotjsonjs.data.prop, 'hydralisk',
  236. 'js modules are cached whether access via .json.js or .json');
  237. };
  238. exports['test invisibleToDebugger: false'] = function (assert) {
  239. let uri = root + '/fixtures/loader/cycles/';
  240. let loader = Loader({ paths: { '': uri } });
  241. main(loader, 'main');
  242. let dbg = new Debugger();
  243. let sandbox = loader.sandboxes[uri + 'main.js'];
  244. try {
  245. dbg.addDebuggee(sandbox);
  246. assert.ok(true, 'debugger added visible value');
  247. } catch(e) {
  248. assert.fail('debugger could not add visible value');
  249. }
  250. };
  251. exports['test invisibleToDebugger: true'] = function (assert) {
  252. let uri = root + '/fixtures/loader/cycles/';
  253. let loader = Loader({ paths: { '': uri }, invisibleToDebugger: true });
  254. main(loader, 'main');
  255. let dbg = new Debugger();
  256. let sandbox = loader.sandboxes[uri + 'main.js'];
  257. try {
  258. dbg.addDebuggee(sandbox);
  259. assert.fail('debugger added invisible value');
  260. } catch(e) {
  261. assert.ok(true, 'debugger did not add invisible value');
  262. }
  263. };
  264. exports['test console global by default'] = function (assert) {
  265. let uri = root + '/fixtures/loader/globals/';
  266. let loader = Loader({ paths: { '': uri }});
  267. let program = main(loader, 'main');
  268. assert.ok(typeof program.console === 'object', 'global `console` exists');
  269. assert.ok(typeof program.console.log === 'function', 'global `console.log` exists');
  270. let loader2 = Loader({ paths: { '': uri }, globals: { console: fakeConsole }});
  271. let program2 = main(loader2, 'main');
  272. assert.equal(program2.console, fakeConsole,
  273. 'global console can be overridden with Loader options');
  274. function fakeConsole () {};
  275. };
  276. require('test').run(exports);