зеркало из https://github.com/mozilla/gecko-dev.git
462 строки
14 KiB
JavaScript
462 строки
14 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
'use strict';
|
|
|
|
const { Cc, Cu, Ci } = require('chrome');
|
|
const { setTimeout } = require('sdk/timers');
|
|
const { prefixURI, name } = require('@loader/options');
|
|
const addonPromiseURI = prefixURI + name + '/lib/sdk/core/promise.js';
|
|
const builtPromiseURI = 'resource://gre/modules/commonjs/sdk/core/promise.js';
|
|
var { Promise, defer, resolve, reject, all, promised } = require('sdk/core/promise');
|
|
|
|
exports['test all observers are notified'] = function(assert, done) {
|
|
let expected = 'Taram pam param!';
|
|
let deferred = defer();
|
|
let pending = 10, i = 0;
|
|
|
|
function resolved(value) {
|
|
assert.equal(value, expected, 'value resolved as expected: #' + pending);
|
|
if (!--pending) done();
|
|
}
|
|
|
|
while (i++ < pending) deferred.promise.then(resolved);
|
|
|
|
deferred.resolve(expected);
|
|
};
|
|
|
|
exports['test exceptions dont stop notifications'] = function(assert, done) {
|
|
let threw = false, boom = Error('Boom!');
|
|
let deferred = defer();
|
|
|
|
let promise2 = deferred.promise.then(function() {
|
|
threw = true;
|
|
throw boom;
|
|
});
|
|
|
|
deferred.promise.then(function() {
|
|
assert.ok(threw, 'observer is called even though previos one threw');
|
|
promise2.then(function() {
|
|
assert.fail('should not resolve');
|
|
}, function(reason) {
|
|
assert.equal(reason, boom, 'rejects to thrown error');
|
|
done();
|
|
});
|
|
});
|
|
|
|
deferred.resolve('go!');
|
|
};
|
|
|
|
exports['test subsequent resolves are ignored'] = function(assert, done) {
|
|
let deferred = defer();
|
|
deferred.resolve(1);
|
|
deferred.resolve(2);
|
|
deferred.reject(3);
|
|
|
|
deferred.promise.then(function(actual) {
|
|
assert.equal(actual, 1, 'resolves to first value');
|
|
}, function() {
|
|
assert.fail('must not reject');
|
|
});
|
|
deferred.promise.then(function(actual) {
|
|
assert.equal(actual, 1, 'subsequent resolutions are ignored');
|
|
done();
|
|
}, function() {
|
|
assert.fail('must not reject');
|
|
});
|
|
};
|
|
|
|
exports['test subsequent rejections are ignored'] = function(assert, done) {
|
|
let deferred = defer();
|
|
deferred.reject(1);
|
|
deferred.resolve(2);
|
|
deferred.reject(3);
|
|
|
|
deferred.promise.then(function(actual) {
|
|
assert.fail('must not resolve');
|
|
}, function(actual) {
|
|
assert.equal(actual, 1, 'must reject to first');
|
|
});
|
|
deferred.promise.then(function(actual) {
|
|
assert.fail('must not resolve');
|
|
}, function(actual) {
|
|
assert.equal(actual, 1, 'must reject to first');
|
|
done();
|
|
});
|
|
};
|
|
|
|
exports['test error recovery'] = function(assert, done) {
|
|
let boom = Error('Boom!');
|
|
let deferred = defer();
|
|
|
|
deferred.promise.then(function() {
|
|
assert.fail('rejected promise should not resolve');
|
|
}, function(reason) {
|
|
assert.equal(reason, boom, 'rejection reason delivered');
|
|
return 'recovery';
|
|
}).then(function(value) {
|
|
assert.equal(value, 'recovery', 'error handled by a handler');
|
|
done();
|
|
});
|
|
|
|
deferred.reject(boom);
|
|
};
|
|
|
|
exports['test error recovery with promise'] = function(assert, done) {
|
|
let deferred = defer();
|
|
|
|
deferred.promise.then(function() {
|
|
assert.fail('must reject');
|
|
}, function(actual) {
|
|
assert.equal(actual, 'reason', 'rejected');
|
|
let deferred = defer();
|
|
deferred.resolve('recovery');
|
|
return deferred.promise;
|
|
}).then(function(actual) {
|
|
assert.equal(actual, 'recovery', 'recorvered via promise');
|
|
let deferred = defer();
|
|
deferred.reject('error');
|
|
return deferred.promise;
|
|
}).then(null, function(actual) {
|
|
assert.equal(actual, 'error', 'rejected via promise');
|
|
let deferred = defer();
|
|
deferred.reject('end');
|
|
return deferred.promise;
|
|
}).then(null, function(actual) {
|
|
assert.equal(actual, 'end', 'rejeced via promise');
|
|
done();
|
|
});
|
|
|
|
deferred.reject('reason');
|
|
};
|
|
|
|
exports['test propagation'] = function(assert, done) {
|
|
let d1 = defer(), d2 = defer(), d3 = defer();
|
|
|
|
d1.promise.then(function(actual) {
|
|
assert.equal(actual, 'expected', 'resolves to expected value');
|
|
done();
|
|
});
|
|
|
|
d1.resolve(d2.promise);
|
|
d2.resolve(d3.promise);
|
|
d3.resolve('expected');
|
|
};
|
|
|
|
exports['test chaining'] = function(assert, done) {
|
|
let boom = Error('boom'), brax = Error('braxXXx');
|
|
let deferred = defer();
|
|
|
|
deferred.promise.then().then().then(function(actual) {
|
|
assert.equal(actual, 2, 'value propagates unchanged');
|
|
return actual + 2;
|
|
}).then(null, function(reason) {
|
|
assert.fail('should not reject');
|
|
}).then(function(actual) {
|
|
assert.equal(actual, 4, 'value propagates through if not handled');
|
|
throw boom;
|
|
}).then(function(actual) {
|
|
assert.fail('exception must reject promise');
|
|
}).then().then(null, function(actual) {
|
|
assert.equal(actual, boom, 'reason propagates unchanged');
|
|
throw brax;
|
|
}).then().then(null, function(actual) {
|
|
assert.equal(actual, brax, 'reason changed becase of exception');
|
|
return 'recovery';
|
|
}).then(function(actual) {
|
|
assert.equal(actual, 'recovery', 'recovered from error');
|
|
done();
|
|
});
|
|
|
|
deferred.resolve(2);
|
|
};
|
|
|
|
exports['test reject'] = function(assert, done) {
|
|
let expected = Error('boom');
|
|
|
|
reject(expected).then(function() {
|
|
assert.fail('should reject');
|
|
}, function(actual) {
|
|
assert.equal(actual, expected, 'rejected with expected reason');
|
|
}).then(done, assert.fail);
|
|
};
|
|
|
|
exports['test resolve to rejected'] = function(assert, done) {
|
|
let expected = Error('boom');
|
|
let deferred = defer();
|
|
|
|
deferred.promise.then(function() {
|
|
assert.fail('should reject');
|
|
}, function(actual) {
|
|
assert.equal(actual, expected, 'rejected with expected failure');
|
|
}).then(done, assert.fail);
|
|
|
|
deferred.resolve(reject(expected));
|
|
};
|
|
|
|
exports['test resolve'] = function(assert, done) {
|
|
let expected = 'value';
|
|
resolve(expected).then(function(actual) {
|
|
assert.equal(actual, expected, 'resolved as expected');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
exports['test promised with normal args'] = function(assert, done) {
|
|
let sum = promised((x, y) => x + y );
|
|
|
|
sum(7, 8).then(function(actual) {
|
|
assert.equal(actual, 7 + 8, 'resolves as expected');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
exports['test promised with promise args'] = function(assert, done) {
|
|
let sum = promised((x, y) => x + y );
|
|
let deferred = defer();
|
|
|
|
sum(11, deferred.promise).then(function(actual) {
|
|
assert.equal(actual, 11 + 24, 'resolved as expected');
|
|
}).catch(assert.fail).then(done);
|
|
|
|
deferred.resolve(24);
|
|
};
|
|
|
|
exports['test promised error handling'] = function(assert, done) {
|
|
let expected = Error('boom');
|
|
let f = promised(function() {
|
|
throw expected;
|
|
});
|
|
|
|
f().then(function() {
|
|
assert.fail('should reject');
|
|
}, function(actual) {
|
|
assert.equal(actual, expected, 'rejected as expected');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
exports['test errors in promise resolution handlers are propagated'] = function(assert, done) {
|
|
var expected = Error('Boom');
|
|
var { promise, resolve } = defer();
|
|
|
|
promise.then(function() {
|
|
throw expected;
|
|
}).then(function() {
|
|
return undefined;
|
|
}).then(null, function(actual) {
|
|
assert.equal(actual, expected, 'rejected as expected');
|
|
}).then(done, assert.fail);
|
|
|
|
resolve({});
|
|
};
|
|
|
|
exports['test return promise form promised'] = function(assert, done) {
|
|
let f = promised(function() {
|
|
return resolve(17);
|
|
});
|
|
|
|
f().then(function(actual) {
|
|
assert.equal(actual, 17, 'resolves to a promise resolution');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
exports['test promised returning failure'] = function(assert, done) {
|
|
let expected = Error('boom');
|
|
let f = promised(function() {
|
|
return reject(expected);
|
|
});
|
|
|
|
f().then(function() {
|
|
assert.fail('must reject');
|
|
}, function(actual) {
|
|
assert.equal(actual, expected, 'rejects with expected reason');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
/*
|
|
* Changed for compliance in Bug 881047, promises are now always async
|
|
*/
|
|
exports['test promises are always async'] = function (assert, done) {
|
|
let runs = 0;
|
|
resolve(1)
|
|
.then(val => ++runs)
|
|
.catch(assert.fail).then(done);
|
|
assert.equal(runs, 0, 'resolutions are called in following tick');
|
|
};
|
|
|
|
/*
|
|
* Changed for compliance in Bug 881047, promised's are now non greedy
|
|
*/
|
|
exports['test promised are not greedy'] = function(assert, done) {
|
|
let runs = 0;
|
|
promised(() => ++runs)()
|
|
.catch(assert.fail).then(done);
|
|
assert.equal(runs, 0, 'promised does not run task right away');
|
|
};
|
|
|
|
exports['test promised does not flatten arrays'] = function(assert, done) {
|
|
let p = promised(function(empty, one, two, nested) {
|
|
assert.equal(empty.length, 0, "first argument is empty");
|
|
assert.deepEqual(one, ['one'], "second has one");
|
|
assert.deepEqual(two, ['two', 'more'], "third has two more");
|
|
assert.deepEqual(nested, [[]], "forth is properly nested");
|
|
done();
|
|
});
|
|
|
|
p([], ['one'], ['two', 'more'], [[]]);
|
|
};
|
|
|
|
exports['test arrays should not flatten'] = function(assert, done) {
|
|
let a = defer();
|
|
let b = defer();
|
|
|
|
let combine = promised(function(str, arr) {
|
|
assert.equal(str, 'Hello', 'Array was not flattened');
|
|
assert.deepEqual(arr, [ 'my', 'friend' ]);
|
|
});
|
|
|
|
combine(a.promise, b.promise).catch(assert.fail).then(done);
|
|
|
|
|
|
a.resolve('Hello');
|
|
b.resolve([ 'my', 'friend' ]);
|
|
};
|
|
|
|
exports['test `all` for all promises'] = function (assert, done) {
|
|
all([
|
|
resolve(5), resolve(7), resolve(10)
|
|
]).then(function (val) {
|
|
assert.equal(
|
|
val[0] === 5 &&
|
|
val[1] === 7 &&
|
|
val[2] === 10
|
|
, true, 'return value contains resolved promises values');
|
|
done();
|
|
}, function () {
|
|
assert.fail('should not call reject function');
|
|
});
|
|
};
|
|
|
|
exports['test `all` aborts upon first reject'] = function (assert, done) {
|
|
all([
|
|
resolve(5), reject('error'), delayedResolve()
|
|
]).then(function (val) {
|
|
assert.fail('Successful resolve function should not be called');
|
|
}, function (reason) {
|
|
assert.equal(reason, 'error', 'should reject the `all` promise');
|
|
done();
|
|
});
|
|
|
|
function delayedResolve () {
|
|
let deferred = defer();
|
|
setTimeout(deferred.resolve, 50);
|
|
return deferred.promise;
|
|
}
|
|
};
|
|
|
|
exports['test `all` with array containing non-promise'] = function (assert, done) {
|
|
all([
|
|
resolve(5), resolve(10), 925
|
|
]).then(function (val) {
|
|
assert.equal(val[2], 925, 'non-promises should pass-through value');
|
|
done();
|
|
}, function () {
|
|
assert.fail('should not be rejected');
|
|
});
|
|
};
|
|
|
|
exports['test `all` should resolve with an empty array'] = function (assert, done) {
|
|
all([]).then(function (val) {
|
|
assert.equal(Array.isArray(val), true, 'should return array in resolved');
|
|
assert.equal(val.length, 0, 'array should be empty in resolved');
|
|
done();
|
|
}, function () {
|
|
assert.fail('should not be rejected');
|
|
});
|
|
};
|
|
|
|
exports['test `all` with multiple rejected'] = function (assert, done) {
|
|
all([
|
|
reject('error1'), reject('error2'), reject('error3')
|
|
]).then(function (value) {
|
|
assert.fail('should not be successful');
|
|
}, function (reason) {
|
|
assert.equal(reason, 'error1', 'should reject on first promise reject');
|
|
done();
|
|
});
|
|
};
|
|
|
|
exports['test Promise constructor resolve'] = function (assert, done) {
|
|
var isAsync = true;
|
|
new Promise(function (resolve, reject) {
|
|
resolve(5);
|
|
}).then(x => {
|
|
isAsync = false;
|
|
assert.equal(x, 5, 'Promise constructor resolves correctly');
|
|
}).catch(assert.fail).then(done);
|
|
assert.ok(isAsync, 'Promise constructor runs async');
|
|
};
|
|
|
|
exports['test Promise constructor reject'] = function (assert, done) {
|
|
new Promise(function (resolve, reject) {
|
|
reject(new Error('deferred4life'));
|
|
}).then(assert.fail, (err) => {
|
|
assert.equal(err.message, 'deferred4life', 'Promise constructor rejects correctly');
|
|
}).catch(assert.fail).then(done);
|
|
};
|
|
|
|
exports['test JSM Load and API'] = function (assert, done) {
|
|
// Use addon URL when loading from cfx/local:
|
|
// resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
|
|
// Use built URL when testing on try, etc.
|
|
// resource://gre/modules/commonjs/sdk/core/promise.js
|
|
try {
|
|
var { Promise } = Cu.import(addonPromiseURI, {});
|
|
} catch (e) {
|
|
var { Promise } = Cu.import(builtPromiseURI, {});
|
|
}
|
|
testEnvironment(Promise, assert, done, 'JSM');
|
|
};
|
|
|
|
exports['test mozIJSSubScriptLoader exporting'] = function (assert, done) {
|
|
let { Services } = Cu.import('resource://gre/modules/Services.jsm', {});
|
|
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
|
let Promise = new Cu.Sandbox(systemPrincipal);
|
|
let loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
|
|
.getService(Ci.mozIJSSubScriptLoader);
|
|
|
|
// Use addon URL when loading from cfx/local:
|
|
// resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
|
|
// Use built URL when testing on try, etc.
|
|
// resource://gre/modules/commonjs/sdk/core/promise.js
|
|
try {
|
|
loader.loadSubScript(addonPromiseURI, Promise);
|
|
} catch (e) {
|
|
loader.loadSubScript(builtPromiseURI, Promise);
|
|
}
|
|
|
|
testEnvironment(Promise, assert, done, 'mozIJSSubScript');
|
|
};
|
|
|
|
function testEnvironment ({all, resolve, defer, reject, promised}, assert, done, type) {
|
|
all([resolve(5), resolve(10), 925]).then(val => {
|
|
assert.equal(val[0], 5, 'promise#all works ' + type);
|
|
assert.equal(val[1], 10, 'promise#all works ' + type);
|
|
assert.equal(val[2], 925, 'promise#all works ' + type);
|
|
return resolve(1000);
|
|
}).then(value => {
|
|
assert.equal(value, 1000, 'promise#resolve works ' + type);
|
|
return reject('testing reject');
|
|
}).then(null, reason => {
|
|
assert.equal(reason, 'testing reject', 'promise#reject works ' + type);
|
|
let deferred = defer();
|
|
setTimeout(() => deferred.resolve('\\m/'), 10);
|
|
return deferred.promise;
|
|
}).then(value => {
|
|
assert.equal(value, '\\m/', 'promise#defer works ' + type);
|
|
return promised(x => x * x)(5);
|
|
}).then(value => {
|
|
assert.equal(value, 25, 'promise#promised works ' + type);
|
|
}).then(done, assert.fail);
|
|
}
|
|
|
|
require("sdk/test").run(exports);
|