@qooxdoo/framework
Version:
The JS Framework for Coders
1,780 lines (1,627 loc) • 56 kB
JavaScript
qx.Class.define("qx.test.Promise", {
extend: qx.dev.unit.TestCase,
members: {
/**
* Tests the isPromise method
*/
testIsPromise() {
var p = new qx.Promise(function () {});
this.assertTrue(qx.Promise.isPromise(p));
this.assertFalse(qx.Promise.isPromise(null));
this.assertFalse(qx.Promise.isPromise({}));
this.assertTrue(qx.Promise.isPromise(qx.Promise.resolve()));
this.assertTrue(qx.Promise.isPromise(Promise.resolve()));
this.assertTrue(qx.Promise.isPromise({ then: function () {} }));
},
/**
* Tests a new promise that resolves with no errors
*/
testNewPromise() {
var self = this;
var p = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
resolve("ok");
});
}, this);
p.then(
function (value) {
this.assertIdentical(this, self);
this.assertEquals(value, "ok");
this.resume();
},
function (err) {
this.assertTrue(false);
this.resume();
}
);
this.wait(1000);
},
/**
* Tests a new promise that is rejected
*/
testReject() {
var self = this;
var p = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("oops"));
});
}, this);
p.then(
function (value) {
this.assertTrue(false);
this.resume();
},
function (err) {
this.assertIdentical(this, self);
this.assertEquals(err.message, "oops");
this.resume();
}
);
this.wait(1000);
},
/**
* Tests promise being rejected externally using `reject` method
* Also tests binding catch
*/
testExternalReject() {
var promise = new qx.Promise();
promise.catch(function (e) {
this.assertEquals("oops", e.message);
setTimeout(() => this.resume(), 1);
}, this);
promise.reject(new Error("oops"));
this.wait(1000);
},
/**
* Tests that cancelling promise will cause `then` and `catch` to not be called
* (finally must be called)
*/
testCancel1() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let promise = new qx.Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 500);
});
promise
.then(() => this.fail("Should not call!"))
.then(() => this.fail("Should not call!"));
promise.catch(() => {
this.fail("Should not call!");
});
let output = "";
promise
.then(
() => new qx.Promise((resolve, reject) => reject(new Error("oops")))
)
.catch(() => this.fail("Should not call!"))
.finally(() => {
output += "1";
})
.then(() => {
this.fail("Should not call!");
});
let f = promise.finally(() => {
setTimeout(() => {
output += "2";
this.assertEquals("12", output);
this.resume();
}, 100);
});
this.assertInstance(f, qx.Promise);
promise.cancel();
this.wait(1000);
}
},
/**
* Ensures that non of the handlers in the chain are called when a promise is cancelled immediately
*/
testCancel2() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let output = "";
let promise = qx.Promise.resolve()
.then(
() =>
new qx.Promise(resolve => {
output += "1";
setTimeout(resolve, 100);
})
)
.then(
() =>
new qx.Promise(resolve => {
output += "2";
setTimeout(resolve, 100);
})
)
.then(
() =>
new qx.Promise(resolve => {
output += "3";
setTimeout(resolve, 100);
})
)
.finally(() => {
this.assertEquals("", output);
this.resume();
});
promise.cancel();
this.wait(1000);
}
},
/**
* Ensures that the promise chain does not continue after a promise is cancelled, except ALL the `finally` calls
* which are both ancestors and descendants of the cancelled promise, which have not been called yet.
*/
testCancel3() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let output = "";
let promise = qx.Promise.resolve()
.then(
() =>
new qx.Promise(resolve => {
output += "1";
setTimeout(resolve, 100);
})
)
.then(
() =>
new qx.Promise(resolve => {
output += "2";
setTimeout(resolve, 100);
setTimeout(() => promise.cancel(), 150);
})
)
.finally(() => {
if (output.at(-1) !== "2") {
this.fail("finally called twice!");
}
output += "3";
})
.then(
() =>
new qx.Promise(resolve => {
output += "4";
setTimeout(resolve, 100);
})
)
.then(
() =>
new qx.Promise(resolve => {
this.fail("Should not call!");
})
)
.finally(() => {
output += "5";
})
.then(() => this.fail("should not call!"))
.finally(() => {
output += "6";
});
let promise2 = promise
.then(() => this.fail("should not call!"))
.finally(() => {
output += "7";
})
.then(() => this.fail("should not call!"))
.finally(() => {
output += "8";
this.assertEquals("12345678", output);
this.resume();
});
this.wait(1000);
}
},
/**
* Tests that cancelling a promise will not stop its chain from executing if there are promises which depend on some stages in the chain
*/
testCancel4() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let output = "";
let promise1 = qx.Promise.resolve().then(() => {
output += "1";
});
let branch1 = promise1.then(() => {
output += "2";
});
let branch2 = promise1
.then(() => (output += "3"))
.finally(() => {
setTimeout(() => {
this.assertEquals("13", output);
this.resume();
}, 100);
});
branch1.cancel();
this.wait(1000);
}
},
/**
* Ensures exception is thrown when trying to call `then` for a promise which has already been cancelled.
*/
testThenAfterCancel() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let promise = new qx.Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 300);
});
promise.cancel();
promise
.then(() => this.fail("Should not call!"))
.catch(e => {
this.assertEquals("late cancellation observer", e.message);
setTimeout(this.resume(), 1);
});
this.wait(1000);
}
},
/**
* Ensures exception is thrown when trying to call `catch` for a promise which has already been cancelled.
*/
testCatchAfterCancel() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let promise = new qx.Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 300);
});
promise.cancel();
promise
.then(() => {
this.fail("Should not call!");
})
.catch(e => {
this.assertEquals("late cancellation observer", e.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
}
},
/**
* Ensures exception is thrown when trying to call `finally` for a promise which has already been cancelled.
*/
testFinallyAfterCancel() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let promise = new qx.Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 300);
});
promise.cancel();
promise
.then(() => {
this.fail("Should not call!");
})
.catch(e => {
this.assertEquals("late cancellation observer", e.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
}
},
/**
* Ensures a promise can still be cancelled after it has been settled
*/
testCancelAfterSettled() {
if (qx.core.Environment.get("qx.Promise.useNativePromise")) {
console.warn(
"Skipping test because the native promise implementation of qx.Promise does not support cancellation"
);
return;
} else {
let promise = qx.Promise.resolve();
promise
.then(() => {
promise.cancel();
})
.then(() => {
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
}
},
/**
* Ensures that `finally` is run when the promise rejects
*/
testCatchFinally() {
var caughtException = null;
var t = this;
qx.Promise.resolve()
.then(function () {
throw new Error("oops");
})
.catch(function (ex) {
caughtException = ex;
})
.finally(function () {
this.assertNotNull(caughtException);
this.assertIdentical(this, t);
setTimeout(() => this.resume(), 1);
}, this);
this.wait(1000);
},
/**
* Tests that the `promisify` method works for operations functions.
* Also ensures the return value of `.then` is a qx.Promise
*/
testPromisifyResolve() {
function feedMe(fruit, callback) {
setTimeout(function () {
if (fruit == "raspberry") {
callback(null, "That's nice");
} else {
callback(new Error("No thank you!"), null);
}
}, 100);
}
let feedMeAsync = qx.Promise.promisify(feedMe);
feedMeAsync("raspberry").then(value => {
this.assertEquals("That's nice", value);
this.resume();
});
this.wait(1000);
},
/**
* Tests that the `promisify` method works for unsuccessful operations
*/
testPromisifyReject() {
function feedMe(fruit, callback) {
setTimeout(function () {
if (fruit == "raspberry") {
callback(null, "That's nice");
} else {
callback(new Error("No thank you!"), null);
}
}, 100);
}
let feedMeAsync = qx.Promise.promisify(feedMe);
feedMeAsync("ping pong balls").catch(err => {
this.assertEquals("No thank you!", err.message);
this.resume();
});
this.wait(1000);
},
/**
* Tests the qx.Promise.allOf method
*/
testAllOf() {
var t = this;
var dt = new Date();
var obj = {
a: new qx.Promise(),
b: new qx.Promise(),
c: new qx.Promise(),
d: "four",
e: dt
};
let promise = qx.Promise.allOf(obj);
this.assertInstance(promise, qx.Promise);
promise.then(function (obj2) {
t.assertTrue(obj === obj2);
t.assertEquals("one", obj.a);
t.assertEquals("two", obj.b);
t.assertEquals("three", obj.c);
t.assertEquals("four", obj.d);
t.assertTrue(obj.e === dt);
setTimeout(() => t.resume(), 1);
});
obj.a.then(function () {
obj.b.resolve("two");
});
obj.b.then(function () {
obj.c.resolve("three");
});
obj.a.resolve("one");
t.wait(1000);
},
/**
* Checks that if one of the promises in the allOf array rejects,
* the overall result will reject
*/
testAllOfReject() {
var t = this;
var obj = {
a: new qx.Promise(),
b: new qx.Promise()
};
qx.Promise.allOf(qx.Promise.resolve(obj)).catch(function (reason) {
t.assertEquals("two", reason.message);
setTimeout(() => t.resume(), 1);
});
obj.b.reject(new Error("two"));
obj.a.resolve("one");
t.wait(1000);
},
/**
* Tests the qx.Promise.allOf being passed a promise of an object instead of a straight value
*/
testAllOfPromiseObj() {
var t = this;
var obj = {
a: new qx.Promise(),
b: new qx.Promise()
};
qx.Promise.allOf(qx.Promise.resolve(obj)).then(function (obj2) {
t.assertTrue(obj === obj2);
t.assertEquals("one", obj.a);
t.assertEquals("two", obj.b);
setTimeout(() => t.resume(), 1);
});
obj.a.then(function () {
obj.b.resolve("two");
});
obj.a.resolve("one");
t.wait(1000);
},
/**
* Tests that setting a property value with a promise will delay setting the
* value until the promise is resolved. In this case, the property is *not*
* marked as async and the setXxx method is used
*/
testPropertySetValueAsPromise1() {
var t = this;
var Clazz = qx.Class.define("testPropertySetValueAsPromise1.Clazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true
}
}
});
this.assertTrue(!!Clazz.prototype.setAlpha);
this.assertFalse(!!Clazz.prototype.setAlphaAsync);
var obj = new Clazz();
var p = new qx.Promise(function (resolve) {
resolve(123);
});
obj.setAlpha(p);
p.then(function () {
t.assertEquals(123, obj.getAlpha());
qx.Class.undefine("testPropertySetValueAsPromise1.Clazz");
setTimeout(() => t.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that setting a property value with a promise will delay setting the
* value until the promise is resolved. In this case, the property *is*
* marked as async and the setXxxAsync method is used to test chaining
*/
testPropertySetValueAsPromise2() {
var t = this;
var Clazz = qx.Class.define("testPropertySetValueAsPromise2.Clazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true,
async: true
}
}
});
this.assertTrue(!!Clazz.prototype.setAlpha);
this.assertTrue(!!Clazz.prototype.setAlphaAsync);
var obj = new Clazz();
var p = new qx.Promise(function (resolve) {
resolve(123);
});
obj.setAlphaAsync(p).then(function () {
t.assertEquals(123, obj.getAlpha());
qx.Class.undefine("testPropertySetValueAsPromise2.Clazz");
setTimeout(() => t.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that a property apply method can return a promise; in this case, the
* property is not marked as async so the apply method is only able to delay
* the event handler
*/
testPropertySetValueAsyncApply1() {
var t = this;
var p;
var Clazz = qx.Class.define("testPropertySetValueAsyncApply1.Clazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true,
apply: "_applyAlpha",
event: "changeAlpha"
}
},
members: {
_applyAlpha(value, oldValue) {
return (p = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("xyz");
}, 250);
}));
}
}
});
var obj = new Clazz();
var eventFired = 0;
obj.addListener("changeAlpha", function (evt) {
eventFired++;
});
obj.setAlpha("abc");
this.assertTrue(!!p);
this.assertEquals(0, eventFired);
this.assertEquals("abc", obj.getAlpha());
p.then(function (value) {
this.assertEquals("xyz", value); // "xyz" because this is the internal promise
this.assertEquals("abc", obj.getAlpha());
this.assertEquals(1, eventFired);
qx.Class.undefine("testPropertySetValueAsyncApply1.Clazz");
t.resume();
}, this);
this.wait(1000);
},
/**
* Tests that a property apply method can return a promise; in this case, the
* property *is* marked as async, and we use the setAlphaAsync to test chaining
*/
testPropertySetValueAsyncApply2() {
var t = this;
var Clazz = qx.Class.define("testPropertySetValueAsyncApply2.Clazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true,
async: true,
apply: "_applyAlpha",
event: "changeAlpha"
}
},
members: {
_applyAlpha(value, oldValue) {
return new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("xyz");
}, 250);
});
}
}
});
var obj = new Clazz();
var eventFired = 0;
obj.addListener("changeAlpha", function (evt) {
eventFired++;
});
var p = obj.setAlphaAsync("abc");
this.assertEquals(0, eventFired);
p.then(function (value) {
this.assertEquals("abc", value);
this.assertEquals("abc", obj.getAlpha());
this.assertEquals(1, eventFired);
// Set the same value, should return a new promise but not fire an event
p = obj.setAlphaAsync("abc");
p.then(function (value) {
this.assertEquals("abc", value);
this.assertEquals("abc", obj.getAlpha());
this.assertEquals(1, eventFired);
qx.Class.undefine("testPropertySetValueAsyncApply2.Clazz");
t.resume();
}, this);
}, this);
this.wait(1000);
},
/**
* Tests that a property apply method can take a promise
*/
testPropertySetValueAsyncApply3() {
var t = this;
var Clazz = qx.Class.define("testPropertySetValueAsyncApply3.Clazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true,
check: "qx.Promise"
}
}
});
var obj = new Clazz();
var p = qx.Promise.resolve("hello");
obj.setAlpha(p);
this.assertEquals(p, obj.getAlpha());
qx.Class.undefine("testPropertySetValueAsyncApply3.Clazz");
},
testBinding() {
var t = this;
var AsyncClazz = qx.Class.define("testBinding.AsyncClazz", {
extend: qx.core.Object,
properties: {
alpha: {
init: null,
nullable: true,
async: true,
event: "changeAlpha"
}
},
members: {
_applyAlpha(value, oldValue) {
return new qx.Promise(function (resolve) {
setTimeout(resolve, 250);
});
}
}
});
var SyncClazz = qx.Class.define("testBinding.SyncClazz", {
extend: qx.core.Object,
properties: {
bravo: {
init: null,
nullable: true,
event: "changeBravo"
}
}
});
/*
* Test binding an async property to a "normal" sync property
*/
var asyncToSync = new qx.Promise(function (resolve) {
var asyncObj = new AsyncClazz();
var syncObj = new SyncClazz();
var p1 = new qx.Promise();
asyncObj.addListenerOnce("changeAlphaAsync", evt => {
var data = evt.getData();
this.assertTrue(data instanceof qx.Promise);
p1.resolve();
});
var p2 = new qx.Promise();
var bravoEvents = 0;
var id = syncObj.addListener("changeBravo", evt => {
bravoEvents++;
this.assertTrue(bravoEvents <= 2);
var data = evt.getData();
// First event is .bind() setting the initial value
if (bravoEvents == 1) {
this.assertNull(data);
// Second event was caused by asyncObj.setAlphaAsync()
} else if (bravoEvents == 2) {
this.assertEquals("zyx", data);
syncObj.removeListenerById(id);
p2.resolve();
}
});
asyncObj.getAlphaAsync();
asyncObj.bind("alphaAsync", syncObj, "bravo");
asyncObj.setAlphaAsync("zyx");
qx.Promise.all([p1, p2]).then(function () {
var p3 = new qx.Promise();
syncObj.addListenerOnce("changeBravo", evt => {
var data = evt.getData();
this.assertEquals("wvu", data);
p3.resolve();
});
asyncObj.setAlphaAsync("wvu");
p3.then(function () {
this.resume();
}, this);
}, this);
}, this);
/*
* Test binding a "normal" sync property to an async property
*/
asyncToSync.then(function () {
var asyncObj = new AsyncClazz();
var syncObj = new SyncClazz();
var p1 = new qx.Promise();
asyncObj.addListenerOnce("changeAlphaAsync", evt => {
var data = evt.getData();
this.assertEquals("def", data);
p1.resolve();
});
syncObj.bind("bravo", asyncObj, "alphaAsync");
syncObj.setBravo("def");
p1.then(function () {
var p2 = new qx.Promise();
asyncObj.addListenerOnce("changeAlphaAsync", evt => {
var data = evt.getData();
this.assertEquals("ghi", data);
p2.resolve();
});
syncObj.setBravo("ghi");
return p2.then(function () {
qx.Class.undefine("testBinding.AsyncClazz");
qx.Class.undefine("testBinding.SyncClazz");
this.resume();
}, this);
}, this);
}, this);
this.wait(1000);
},
/**
* Tests event handlers bound to the "changeXxxAsync" events, and which return
* a promise. Event handlers must be triggered in sequence and by returning
* a promise will defer subsequent event handlers from firing
*/
testAsyncEventHandlers() {
var Clazz = qx.Class.define("testAsyncEventHandlers.Clazz", {
extend: qx.core.Object,
properties: {
value: {},
alpha: {
init: null,
nullable: true,
async: true,
apply: "_applyAlpha",
event: "changeAlpha"
},
bravo: {
init: null,
nullable: true,
async: true,
apply: "_applyBravo",
event: "changeBravo"
}
},
members: {
_applyAlpha(value, oldValue) {
var p = new qx.Promise(function (resolve) {
console.log("in _applyAlpha qx.Promise, value=" + value);
setTimeout(function () {
console.log(
"in _applyAlpha resolving qx.Promise, value=" + value
);
resolve("xyz");
}, 50);
});
console.log("in _applyAlpha, value=" + value + ", p=" + p);
return p;
},
_applyBravo(value, oldValue) {
return new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("uvw");
}, 50);
});
}
}
});
function createObj(name) {
var obj = new Clazz().set({ value: name });
obj.addListener("changeAlphaAsync", function (evt) {
var value = evt.getData();
var p = new qx.Promise(function (resolve) {
console.log(
name + ": changeAlphaAsync 1 in qx.Promise, value=" + value
);
setTimeout(function () {
if (str.length) {
str += ",";
}
str += name;
console.log(
name +
": changeAlphaAsync 1 resolving qx.Promise, value=" +
value
);
resolve();
}, 200);
}).then(function () {
console.log(
name + ": changeAlphaAsync 1 resolved qx.Promise, value=" + value
);
});
console.log(
name + ": changeAlphaAsync 1, value=" + value + ", p=" + p
);
return p;
});
return obj;
}
var objOne = createObj("one");
var objTwo = createObj("two");
var str = "";
objOne.addListener("changeAlphaAsync", function (evt) {
var value = evt.getData();
console.log("objOne.alphaAsync setting, value=" + value);
return objTwo.setAlphaAsync("def").then(function () {
str += "xxx";
console.log("objOne.alphaAsync done, value=" + value);
});
});
console.log("objOne.alphaAsync going to set value=abc");
objOne.setAlphaAsync("abc").then(function () {
console.log("objOne.alphaAsync completed set value=abc");
this.assertEquals("one,twoxxx", str);
qx.Class.undefine("testAsyncEventHandlers.Clazz");
this.resume();
}, this);
this.wait(2500);
},
/**
* Tests using bind() on async properties (using the "changeXxxAsync" events) between
* a series of objects. The test must show that the property values are fired in
* order, and that if an async event handler returns a promise it defers bind from
* propagating onto other objects.
*/
testWaterfallBinding() {
var t = this;
var Clazz = qx.Class.define("testWaterfallBinding.Clazz", {
extend: qx.core.Object,
properties: {
value: {},
alpha: {
init: null,
nullable: true,
async: true,
apply: "_applyAlpha",
event: "changeAlpha"
}
},
members: {
_applyAlpha(value, oldValue) {
var t = this;
console.log("pre applyAlpha[" + t.getValue() + "] = " + value);
return new qx.Promise(function (resolve) {
setTimeout(function () {
console.log("applyAlpha[" + t.getValue() + "] = " + value);
resolve("xyz");
}, 50);
});
}
}
});
var objs = [];
var str = "";
function trap(i) {
var obj = new Clazz().set({ value: i });
var bindPromise;
if (i > 0) {
bindPromise = objs[i - 1].bindAsync("alphaAsync", obj, "alphaAsync");
} else {
bindPromise = qx.Promise.resolve(true);
}
return bindPromise.then(function () {
obj.addListener("changeAlpha", evt => {
var obj = evt.getTarget();
var data = evt.getData();
var delay = (5 - i + 1) * 100;
console.log(
"pre changeAlpha " +
obj.getValue() +
" = " +
data +
" after " +
delay
);
return new qx.Promise(function (resolve) {
setTimeout(function () {
if (str.length) {
str += ",";
}
str += obj.getValue() + ":" + data;
console.log(
"changeAlpha " +
obj.getValue() +
" = " +
data +
" after " +
delay
);
resolve();
}, delay);
});
});
objs[i] = obj;
});
}
qx.Promise.mapSeries([0, 1, 2, 3, 4], trap).then(function () {
var p = objs[0].setAlphaAsync("abc");
p.then(function () {
t.assertEquals("0:abc,1:abc,2:abc,3:abc,4:abc", str);
qx.Class.undefine("testWaterfallBinding.Clazz");
t.resume();
}, t);
});
this.wait(10000);
},
/**
* Tests the each method of promise, using qx.data.Array which the native Promise
* does not understand. The values are scalar values
*/
testEach1() {
var t = this;
var arr = new qx.data.Array();
arr.push("a");
arr.push("b");
arr.push("c");
var str = "";
var promise = qx.Promise.resolve(arr);
var forEachReturn = promise.forEach(function (item) {
str += item;
this.assertIdentical(t, this);
}, this);
forEachReturn.then(function () {
t.assertEquals("abc", str);
setTimeout(() => t.resume(), 1);
});
this.assertInstance(forEachReturn, qx.Promise);
t.wait(1000);
},
/**
* Tests the each method of promise, using qx.data.Array which the native Promise
* does not understand. The values are a mixture of promises and scalar values
*/
testEach2() {
var t = this;
var arr = new qx.data.Array();
arr.push(
new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("a");
}, 500);
})
);
arr.push(
new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 300);
})
);
arr.push(
new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("c");
}, 100);
})
);
arr.push("d");
arr.push("e");
var str = "";
var promise = qx.Promise.resolve(arr);
this.assertInstance(promise, qx.Promise);
var pEach = promise.forEach(function (item) {
str += item;
});
this.assertInstance(pEach, qx.Promise);
var pThen = pEach.then(function () {
t.assertEquals("abcde", str);
t.resume();
});
this.assertInstance(pThen, qx.Promise);
t.wait(1000);
},
/**
* Checks that the each method will reject if one of the promises in the array rejects
*/
testEachReject() {
let arr = [qx.Promise.resolve("a"), qx.Promise.reject(new Error("b"))];
var str = "";
var promiseArr = qx.Promise.resolve(arr);
var pEach = qx.Promise.forEach(promiseArr, function (item) {
str += item;
});
pEach.catch(reason => {
this.assertEquals("b", reason.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests the `each` method being passed with a promise of an array,
* not a straight array
*/
testEachPromiseArray() {
var promiseArr = qx.Promise.resolve([1, 2, 3]);
qx.Promise.forEach(promiseArr, (item, index) => {
this.assertEquals(index, item - 1);
}).then(result => {
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests unhandled rejections being passed to the global error handler
*/
testGlobalError() {
var t = this;
qx.event.GlobalError.setErrorHandler(function (ex) {
t.assertEquals(ex.message, "oops");
qx.event.GlobalError.setErrorHandler(null);
t.resume();
});
var self = this;
var p = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
resolve("ok");
});
}, this);
p.then(function (value) {
throw new Error("oops");
});
this.wait(1000);
},
/**
* Tests promisification of methods
*/
testMethod() {
var t = this;
var fn = qx.Promise.method(function (value) {
return value;
});
var promise = fn("yes");
this.assertInstance(promise, qx.Promise);
promise.then(function (value) {
t.assertEquals(value, "yes");
setTimeout(() => t.resume(), 1);
});
this.wait(1000);
},
/**
* Tests binding of all callbacks via .bind()
*/
testBinding1() {
var t = this;
var p = qx.Promise.resolve("hello").bind(this);
p.then(function (value) {
qx.core.Assert.assertIdentical(t, this);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests binding on a per-method basis
*/
testBinding2() {
var t = this;
var p = qx.Promise.forEach(
["a", "b", "c"],
function (item) {
qx.core.Assert.assertIdentical(t, this);
},
this
).then(function (value) {
qx.core.Assert.assertIdentical(t, this);
setTimeout(() => this.resume(), 1);
}, this);
this.wait(1000);
},
testMarshal() {
var marshal = new qx.data.marshal.Json();
marshal.toClass(qx.test.Promise.TEST_MODEL.children[0], true);
var model = marshal.toModel(qx.test.Promise.TEST_MODEL.children[0]);
},
/**
* Tests binding where the context is static class
*/
testBindingToStatic() {
var t = this;
qx.Promise.resolve(true).then(function () {
qx.core.Assert.assertIdentical(qx.Promise, this);
setTimeout(() => t.resume(), 1);
}, qx.Promise);
this.wait(1000);
},
/**
* Tests the context parameter for qx.Promise.resolve
*/
testBindingResolve() {
var t = this;
qx.Promise.resolve(true, this).then(function () {
qx.core.Assert.assertIdentical(t, this);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests the context parameter for qx.Promise.reject
*/
testBindingReject() {
var t = this;
qx.Promise.reject(new Error("Dummy Error"), this).catch(function () {
qx.core.Assert.assertIdentical(t, this);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests wrapping of parameters preserves the original values
*/
testWrapping() {
var t = this;
new qx.Promise(function (resolve) {
resolve();
})
.then(function () {
return qx.Promise.all(["foo", new qx.data.Array(["a", "b", "c"])]);
})
.spread(function (str, arr) {
t.assertEquals(str, "foo");
t.assertInstance(arr, qx.data.Array);
t.assertEquals(arr.join(""), "abc");
setTimeout(() => t.resume(), 1);
});
this.wait(1000);
},
/**
* Tests the `race` method when no promises in the array reject
*/
testRaceResolve() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var promiseC = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("c");
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("d");
}, 300);
});
var arr = [promiseB, promiseC, promiseD];
let promise = qx.Promise.resolve(arr).race();
promise.then(val => {
this.assertEquals("c", val);
this.resume();
});
this.assertInstance(promise, qx.Promise);
this.wait(1000);
},
/**
* Tests the `race` method when one promise in the array rejects
*/
testRaceReject() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var promiseC = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("c"));
}, 100);
});
var arr = new qx.data.Array([promiseB, promiseC]);
qx.Promise.resolve(arr)
.race()
.then(val => {
this.fail("Should not resolve");
})
.catch(err => {
this.assertEquals("c", err.message);
this.resume();
});
this.wait(1000);
},
/**
* Tests the `race` method when the array contains a straight (i.e. non-promise) value
*/
testRaceStraightValue() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var arr = ["a", promiseB];
qx.Promise.race(qx.Promise.resolve(arr)).then(val => {
this.assertEquals("a", val);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests the `any` method when all promises in the array resolve
*/
testAnyResolve() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var promiseC = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("c");
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("d");
}, 300);
});
var arr = [promiseB, promiseC, promiseD];
let promise = qx.Promise.resolve(arr).any();
promise.then(val => {
this.assertEquals("c", val);
this.resume();
});
this.assertInstance(promise, qx.Promise);
this.wait(1000);
},
/**
* Tests the `any` method when one promise in the array rejects.
* The overall result should the result of the first promise that resolves
*/
testAnyOneReject() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var promiseC = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("c"));
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("d");
}, 300);
});
var arr = new qx.data.Array([promiseB, promiseC, promiseD]);
qx.Promise.any(qx.Promise.resolve(arr)).then(val => {
this.assertEquals("b", val);
this.resume();
});
this.wait(1000);
},
/**
* Tests the `any` method when all promises in the array reject.
*/
testAnyAllReject() {
var promiseB = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("b"));
}, 200);
});
var promiseC = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("c"));
}, 100);
});
var promiseD = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("d"));
}, 300);
});
var arr = [promiseB, promiseC, promiseD];
qx.Promise.resolve(arr)
.any()
.catch(aggErr => {
this.resume();
});
this.wait(1000);
},
/**
* Tests the `any` method when an empty array is passed in.
*/
testAnyEmptyArray() {
qx.Promise.resolve([])
.any()
.catch(aggErr => {
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests the `any` method when a straight (i.e. non-promise value) is passed in.
* It should resolve to that value
*/
testAnyStraightValue() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve("b");
}, 200);
});
var arr = ["a", promiseB];
qx.Promise.resolve(arr)
.any()
.then(val => {
this.assertEquals("a", val);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that `reduce` succeeds when no promise rejects
*/
testReduceResolve() {
var promiseB = qx.Promise.resolve(1);
var promiseC = qx.Promise.resolve(2);
var promiseD = qx.Promise.resolve(3);
var arr = [promiseB, promiseC, promiseD];
let promise = qx.Promise.resolve(arr).reduce(
async (acc, item, index, length) => {
this.assertEquals(index, item - 1);
this.assertEquals(length, 3);
return acc + item;
},
0
);
promise.then(result => {
this.assertEquals(6, result);
setTimeout(() => this.resume(), 1);
});
this.assertInstance(promise, qx.Promise);
this.wait(1000);
},
/**
* Tests that `reduce` rejects when one promise in the array rejects
*/
testReduceOneReject() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(2);
}, 200);
});
var promiseC = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("oops"));
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(4);
}, 300);
});
var arr = new qx.data.Array([promiseB, promiseC, promiseD]);
qx.Promise.reduce(
qx.Promise.resolve(arr),
async (acc, item) => acc + item,
0
).catch(err => {
this.assertEquals("oops", err.message);
this.resume();
});
this.wait(1000);
},
/**
* Tests that `reduce` rejects when the reducer function throws an error
*/
testReduceMapperReject() {
var promiseB = qx.Promise.resolve(2);
var promiseC = qx.Promise.resolve(3);
var promiseD = qx.Promise.resolve(4);
var arr = [promiseB, promiseC, promiseD];
qx.Promise.resolve(arr)
.reduce(async (acc, item) => {
throw new Error("oops");
}, 0)
.catch(err => {
this.assertEquals("oops", err.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that `filter` succeeds when no promise rejects
*/
testFilterResolve() {
var promiseB = qx.Promise.resolve(2);
var promiseC = qx.Promise.resolve(3);
var promiseD = qx.Promise.resolve(4);
var promiseE = qx.Promise.resolve(5);
var arr = [promiseB, promiseC, promiseD, promiseE, 6];
let t = this;
let p = qx.Promise.resolve(arr).filter(async function (
item,
index,
length
) {
t.assertEquals(index, item - 2);
t.assertEquals(5, length);
t.assertIdentical(t, this);
return item % 2 === 0;
},
this);
p.then(evens => {
this.assertArrayEquals([2, 4, 6], evens);
//force resume to run on next tick so that we call resume after wait
setTimeout(() => this.resume(), 1);
});
this.assertInstance(p, qx.Promise);
this.wait(1000);
},
/**
* Tests `concurrency` option for method `filter`
*/
testFilterConcurrency() {
let arr = new qx.data.Array([qx.Promise.resolve(1), 2, 3, 4]);
let maxReached = false;
let concurrency = 2;
let running = 0;
let t = this;
qx.Promise.filter(
qx.Promise.resolve(arr),
async function (item) {
running++;
if (running > concurrency) {
t.fail("Too many running tasks");
} else if (running == concurrency) {
maxReached = true;
}
await new Promise(resolve => setTimeout(resolve, 200));
running--;
return item % 2 === 0;
},
{ concurrency }
).then(result => {
this.assertTrue(maxReached);
this.assertArrayEquals([2, 4], result);
this.resume();
});
this.wait(1000);
},
/**
* Tests that `filter` rejects when one promise in the array rejects
*/
testFilterRejectValue() {
var promiseB = qx.Promise.reject(new Error("oops"));
var promiseC = qx.Promise.resolve(3);
var promiseD = qx.Promise.resolve(4);
var arr = [promiseB, promiseC, promiseD, 6];
qx.Promise.resolve(arr)
.filter(async (item, index, length) => item % 2 === 0)
.catch(e => {
this.assertEquals("oops", e.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that `filter` rejects when the iterator function throws an error
*/
testFilterRejectFilterer() {
var promiseB = qx.Promise.resolve(2);
var promiseC = qx.Promise.resolve(3);
var promiseD = qx.Promise.resolve(4);
var arr = [promiseB, promiseC, promiseD, 6];
qx.Promise.resolve(arr)
.filter(async item => {
throw new Error("oops");
})
.catch(e => {
this.assertEquals("oops", e.message);
setTimeout(() => this.resume(), 1);
});
this.wait(1000);
},
/**
* Tests that the `some` method resolves when no promise rejects
*/
testSomeResolve() {
var promiseB = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(2);
}, 200);
});
var promiseC = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(3);
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(4);
}, 300);
});
var arr = [promiseB, promiseC, promiseD, 6];
let p = qx.Promise.resolve(arr).some(2);
p.then(result => {
this.assertArrayEquals([6, 3], result);
this.resume();
});
this.assertInstance(p, qx.Promise);
this.wait(1000);
},
/**
* Tests that the `some` method still resolves when one promise rejects
* such that enough promises still resolve.
* Also tests with a straight value in the array
*/
testSomeOneReject() {
var promiseB = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("oops"));
}, 200);
});
var promiseC = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(3);
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(4);
}, 300);
});
var arr = new qx.data.Array([promiseB, promiseC, promiseD, 6]);
qx.Promise.some(qx.Promise.resolve(arr), 3).then(result => {
this.assertArrayEquals([6, 3, 4], result);
this.resume();
});
this.wait(1000);
},
/**
* Ensures that the `some` method rejects
* when too many promises reject such that there aren't enough
* resolved promises to satisfy the count
*/
testSomeTooManyReject() {
var promiseB = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("oops"));
}, 200);
});
var promiseC = new qx.Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("oops1"));
}, 100);
});
var promiseD = new qx.Promise(function (resolve) {
setTimeout(function () {
resolve(4);
}, 300);
});
var arr = [promiseB, promiseC, promiseD, 6];
qx.Promise.resolve(arr)
.some(3)
.catch(error => {
let errors = qx.lang.Type.isArray(error.errors)
? error.errors
: error;
this.assertArrayEquals(
["oops1", "oops"],
errors.map(e => e.message)
);
this.resume();
});
this.wait(1000);
},
/**
* Ensures that the `map` method resolves when no promise rejects
*/
testMapResolve() {
var promiseB = qx.Promise.resolve(2);
var promiseC = qx.Promise.resolve(3);
var promiseD = qx.Promise.resolve(4);
var arr = [promiseB, promiseC, promiseD, 5];
var t = this;
let p = qx.Prom