addon-sdk/source/test/test-promise.js

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:5571ac997877
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 const { Cc, Cu, Ci } = require('chrome');
8 const { setTimeout } = require('sdk/timers');
9 const { prefixURI, name } = require('@loader/options');
10 const addonPromiseURI = prefixURI + name + '/lib/sdk/core/promise.js';
11 const builtPromiseURI = 'resource://gre/modules/commonjs/sdk/core/promise.js';
12 let { Promise, defer, resolve, reject, all, promised } = require('sdk/core/promise');
13
14 exports['test all observers are notified'] = function(assert, done) {
15 let expected = 'Taram pam param!';
16 let deferred = defer();
17 let pending = 10, i = 0;
18
19 function resolved(value) {
20 assert.equal(value, expected, 'value resolved as expected: #' + pending);
21 if (!--pending) done();
22 }
23
24 while (i++ < pending) deferred.promise.then(resolved);
25
26 deferred.resolve(expected);
27 };
28
29 exports['test exceptions dont stop notifications'] = function(assert, done) {
30 let threw = false, boom = Error('Boom!');
31 let deferred = defer();
32
33 let promise2 = deferred.promise.then(function() {
34 threw = true;
35 throw boom;
36 });
37
38 deferred.promise.then(function() {
39 assert.ok(threw, 'observer is called even though previos one threw');
40 promise2.then(function() {
41 assert.fail('should not resolve');
42 }, function(reason) {
43 assert.equal(reason, boom, 'rejects to thrown error');
44 done();
45 });
46 });
47
48 deferred.resolve('go!');
49 };
50
51 exports['test subsequent resolves are ignored'] = function(assert, done) {
52 let deferred = defer();
53 deferred.resolve(1);
54 deferred.resolve(2);
55 deferred.reject(3);
56
57 deferred.promise.then(function(actual) {
58 assert.equal(actual, 1, 'resolves to first value');
59 }, function() {
60 assert.fail('must not reject');
61 });
62 deferred.promise.then(function(actual) {
63 assert.equal(actual, 1, 'subsequent resolutions are ignored');
64 done();
65 }, function() {
66 assert.fail('must not reject');
67 });
68 };
69
70 exports['test subsequent rejections are ignored'] = function(assert, done) {
71 let deferred = defer();
72 deferred.reject(1);
73 deferred.resolve(2);
74 deferred.reject(3);
75
76 deferred.promise.then(function(actual) {
77 assert.fail('must not resolve');
78 }, function(actual) {
79 assert.equal(actual, 1, 'must reject to first');
80 });
81 deferred.promise.then(function(actual) {
82 assert.fail('must not resolve');
83 }, function(actual) {
84 assert.equal(actual, 1, 'must reject to first');
85 done();
86 });
87 };
88
89 exports['test error recovery'] = function(assert, done) {
90 let boom = Error('Boom!');
91 let deferred = defer();
92
93 deferred.promise.then(function() {
94 assert.fail('rejected promise should not resolve');
95 }, function(reason) {
96 assert.equal(reason, boom, 'rejection reason delivered');
97 return 'recovery';
98 }).then(function(value) {
99 assert.equal(value, 'recovery', 'error handled by a handler');
100 done();
101 });
102
103 deferred.reject(boom);
104 };
105
106 exports['test error recovery with promise'] = function(assert, done) {
107 let deferred = defer();
108
109 deferred.promise.then(function() {
110 assert.fail('must reject');
111 }, function(actual) {
112 assert.equal(actual, 'reason', 'rejected');
113 let deferred = defer();
114 deferred.resolve('recovery');
115 return deferred.promise;
116 }).then(function(actual) {
117 assert.equal(actual, 'recovery', 'recorvered via promise');
118 let deferred = defer();
119 deferred.reject('error');
120 return deferred.promise;
121 }).then(null, function(actual) {
122 assert.equal(actual, 'error', 'rejected via promise');
123 let deferred = defer();
124 deferred.reject('end');
125 return deferred.promise;
126 }).then(null, function(actual) {
127 assert.equal(actual, 'end', 'rejeced via promise');
128 done();
129 });
130
131 deferred.reject('reason');
132 };
133
134 exports['test propagation'] = function(assert, done) {
135 let d1 = defer(), d2 = defer(), d3 = defer();
136
137 d1.promise.then(function(actual) {
138 assert.equal(actual, 'expected', 'resolves to expected value');
139 done();
140 });
141
142 d1.resolve(d2.promise);
143 d2.resolve(d3.promise);
144 d3.resolve('expected');
145 };
146
147 exports['test chaining'] = function(assert, done) {
148 let boom = Error('boom'), brax = Error('braxXXx');
149 let deferred = defer();
150
151 deferred.promise.then().then().then(function(actual) {
152 assert.equal(actual, 2, 'value propagates unchanged');
153 return actual + 2;
154 }).then(null, function(reason) {
155 assert.fail('should not reject');
156 }).then(function(actual) {
157 assert.equal(actual, 4, 'value propagates through if not handled');
158 throw boom;
159 }).then(function(actual) {
160 assert.fail('exception must reject promise');
161 }).then().then(null, function(actual) {
162 assert.equal(actual, boom, 'reason propagates unchanged');
163 throw brax;
164 }).then().then(null, function(actual) {
165 assert.equal(actual, brax, 'reason changed becase of exception');
166 return 'recovery';
167 }).then(function(actual) {
168 assert.equal(actual, 'recovery', 'recovered from error');
169 done();
170 });
171
172 deferred.resolve(2);
173 };
174
175 exports['test reject'] = function(assert, done) {
176 let expected = Error('boom');
177
178 reject(expected).then(function() {
179 assert.fail('should reject');
180 }, function(actual) {
181 assert.equal(actual, expected, 'rejected with expected reason');
182 }).then(done, assert.fail);
183 };
184
185 exports['test resolve to rejected'] = function(assert, done) {
186 let expected = Error('boom');
187 let deferred = defer();
188
189 deferred.promise.then(function() {
190 assert.fail('should reject');
191 }, function(actual) {
192 assert.equal(actual, expected, 'rejected with expected failure');
193 }).then(done, assert.fail);
194
195 deferred.resolve(reject(expected));
196 };
197
198 exports['test resolve'] = function(assert, done) {
199 let expected = 'value';
200 resolve(expected).then(function(actual) {
201 assert.equal(actual, expected, 'resolved as expected');
202 }).catch(assert.fail).then(done);
203 };
204
205 exports['test promised with normal args'] = function(assert, done) {
206 let sum = promised((x, y) => x + y );
207
208 sum(7, 8).then(function(actual) {
209 assert.equal(actual, 7 + 8, 'resolves as expected');
210 }).catch(assert.fail).then(done);
211 };
212
213 exports['test promised with promise args'] = function(assert, done) {
214 let sum = promised((x, y) => x + y );
215 let deferred = defer();
216
217 sum(11, deferred.promise).then(function(actual) {
218 assert.equal(actual, 11 + 24, 'resolved as expected');
219 }).catch(assert.fail).then(done);
220
221 deferred.resolve(24);
222 };
223
224 exports['test promised error handleing'] = function(assert, done) {
225 let expected = Error('boom');
226 let f = promised(function() {
227 throw expected;
228 });
229
230 f().then(function() {
231 assert.fail('should reject');
232 }, function(actual) {
233 assert.equal(actual, expected, 'rejected as expected');
234 }).catch(assert.fail).then(done);
235 };
236
237 exports['test errors in promise resolution handlers are propagated'] = function(assert, done) {
238 var expected = Error('Boom');
239 var { promise, resolve } = defer();
240
241 promise.then(function() {
242 throw expected;
243 }).then(function() {
244 return undefined;
245 }).then(null, function(actual) {
246 assert.equal(actual, expected, 'rejected as expected');
247 }).then(done, assert.fail);
248
249 resolve({});
250 };
251
252 exports['test return promise form promised'] = function(assert, done) {
253 let f = promised(function() {
254 return resolve(17);
255 });
256
257 f().then(function(actual) {
258 assert.equal(actual, 17, 'resolves to a promise resolution');
259 }).catch(assert.fail).then(done);
260 };
261
262 exports['test promised returning failure'] = function(assert, done) {
263 let expected = Error('boom');
264 let f = promised(function() {
265 return reject(expected);
266 });
267
268 f().then(function() {
269 assert.fail('must reject');
270 }, function(actual) {
271 assert.equal(actual, expected, 'rejects with expected reason');
272 }).catch(assert.fail).then(done);
273 };
274
275 /*
276 * Changed for compliance in Bug 881047, promises are now always async
277 */
278 exports['test promises are always async'] = function (assert, done) {
279 let runs = 0;
280 resolve(1)
281 .then(val => ++runs)
282 .catch(assert.fail).then(done);
283 assert.equal(runs, 0, 'resolutions are called in following tick');
284 };
285
286 /*
287 * Changed for compliance in Bug 881047, promised's are now non greedy
288 */
289 exports['test promised are not greedy'] = function(assert, done) {
290 let runs = 0;
291 promised(() => ++runs)()
292 .catch(assert.fail).then(done);
293 assert.equal(runs, 0, 'promised does not run task right away');
294 };
295
296 exports['test arrays should not flatten'] = function(assert, done) {
297 let a = defer();
298 let b = defer();
299
300 let combine = promised(function(str, arr) {
301 assert.equal(str, 'Hello', 'Array was not flattened');
302 assert.deepEqual(arr, [ 'my', 'friend' ]);
303 });
304
305 combine(a.promise, b.promise).catch(assert.fail).then(done);
306
307
308 a.resolve('Hello');
309 b.resolve([ 'my', 'friend' ]);
310 };
311
312 exports['test `all` for all promises'] = function (assert, done) {
313 all([
314 resolve(5), resolve(7), resolve(10)
315 ]).then(function (val) {
316 assert.equal(
317 val[0] === 5 &&
318 val[1] === 7 &&
319 val[2] === 10
320 , true, 'return value contains resolved promises values');
321 done();
322 }, function () {
323 assert.fail('should not call reject function');
324 });
325 };
326
327 exports['test `all` aborts upon first reject'] = function (assert, done) {
328 all([
329 resolve(5), reject('error'), delayedResolve()
330 ]).then(function (val) {
331 assert.fail('Successful resolve function should not be called');
332 }, function (reason) {
333 assert.equal(reason, 'error', 'should reject the `all` promise');
334 done();
335 });
336
337 function delayedResolve () {
338 let deferred = defer();
339 setTimeout(deferred.resolve, 50);
340 return deferred.promise;
341 }
342 };
343
344 exports['test `all` with array containing non-promise'] = function (assert, done) {
345 all([
346 resolve(5), resolve(10), 925
347 ]).then(function (val) {
348 assert.equal(val[2], 925, 'non-promises should pass-through value');
349 done();
350 }, function () {
351 assert.fail('should not be rejected');
352 });
353 };
354
355 exports['test `all` should resolve with an empty array'] = function (assert, done) {
356 all([]).then(function (val) {
357 assert.equal(Array.isArray(val), true, 'should return array in resolved');
358 assert.equal(val.length, 0, 'array should be empty in resolved');
359 done();
360 }, function () {
361 assert.fail('should not be rejected');
362 });
363 };
364
365 exports['test `all` with multiple rejected'] = function (assert, done) {
366 all([
367 reject('error1'), reject('error2'), reject('error3')
368 ]).then(function (value) {
369 assert.fail('should not be successful');
370 }, function (reason) {
371 assert.equal(reason, 'error1', 'should reject on first promise reject');
372 done();
373 });
374 };
375
376 exports['test Promise constructor resolve'] = function (assert, done) {
377 var isAsync = true;
378 new Promise(function (resolve, reject) {
379 resolve(5);
380 }).then(x => {
381 isAsync = false;
382 assert.equal(x, 5, 'Promise constructor resolves correctly');
383 }).catch(assert.fail).then(done);
384 assert.ok(isAsync, 'Promise constructor runs async');
385 };
386
387 exports['test Promise constructor reject'] = function (assert, done) {
388 new Promise(function (resolve, reject) {
389 reject(new Error('deferred4life'));
390 }).then(assert.fail, (err) => {
391 assert.equal(err.message, 'deferred4life', 'Promise constructor rejects correctly');
392 }).catch(assert.fail).then(done);
393 };
394
395 exports['test JSM Load and API'] = function (assert, done) {
396 // Use addon URL when loading from cfx/local:
397 // resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
398 // Use built URL when testing on try, etc.
399 // resource://gre/modules/commonjs/sdk/core/promise.js
400 try {
401 var { Promise } = Cu.import(addonPromiseURI, {});
402 } catch (e) {
403 var { Promise } = Cu.import(builtPromiseURI, {});
404 }
405 testEnvironment(Promise, assert, done, 'JSM');
406 };
407
408 exports['test mozIJSSubScriptLoader exporting'] = function (assert, done) {
409 let { Services } = Cu.import('resource://gre/modules/Services.jsm', {});
410 let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
411 let Promise = new Cu.Sandbox(systemPrincipal);
412 let loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
413 .getService(Ci.mozIJSSubScriptLoader);
414
415 // Use addon URL when loading from cfx/local:
416 // resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
417 // Use built URL when testing on try, etc.
418 // resource://gre/modules/commonjs/sdk/core/promise.js
419 try {
420 loader.loadSubScript(addonPromiseURI, Promise);
421 } catch (e) {
422 loader.loadSubScript(builtPromiseURI, Promise);
423 }
424
425 testEnvironment(Promise, assert, done, 'mozIJSSubScript');
426 };
427
428 function testEnvironment ({all, resolve, defer, reject, promised}, assert, done, type) {
429 all([resolve(5), resolve(10), 925]).then(val => {
430 assert.equal(val[0], 5, 'promise#all works ' + type);
431 assert.equal(val[1], 10, 'promise#all works ' + type);
432 assert.equal(val[2], 925, 'promise#all works ' + type);
433 return resolve(1000);
434 }).then(value => {
435 assert.equal(value, 1000, 'promise#resolve works ' + type);
436 return reject('testing reject');
437 }).then(null, reason => {
438 assert.equal(reason, 'testing reject', 'promise#reject works ' + type);
439 let deferred = defer();
440 setTimeout(() => deferred.resolve('\\m/'), 10);
441 return deferred.promise;
442 }).then(value => {
443 assert.equal(value, '\\m/', 'promise#defer works ' + type);
444 return promised(x => x * x)(5);
445 }).then(value => {
446 assert.equal(value, 25, 'promise#promised works ' + type);
447 }).then(done, assert.fail);
448 }
449
450 require("sdk/test").run(exports);

mercurial