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