Reputation: 1159
How to use jasmine.clock setTimeout mock inside nested promises? (result: Error: jasmine.DEFAULT_TIMEOUT_INTERVAL
)
Order is crucial here.
I need to test nested promises that must have setTimeout inside - order matters.
I know that then
is kind of process.nextTick
(or setImmediate
) and it goes beyond current event loop (which is the core problem here) but this knowledge
does not solve the problem :)
I know that I can put jasmine.clock().tick()
inside nested promise but this is pointless because I'm testing proper order in some events related lib.
How to test something like this in jasmine? any ideas?
it("should run setTimeout mock inside chained promises",(done)=>{
jasmine.clock().install();
let realOrder = [];
let ok1=new Promise((resolve,reject)=>{
resolve("ok");
}).then((ok)=>{// this is future - second level
let p=new Promise((resolve,reject)=>{
setTimeout(()=>{ // not fired up due to 'then' method
realOrder.push("1");
resolve("1");
},100);
});
//jasmine.clock().tick(101); //<- order is crucial here so I can't do that
return p;
});
let ok2=new Promise((resolve,reject)=>{
resolve("ok");
}).then((ok)=>{ // second level
let p=new Promise((resolve,reject)=>{
setTimeout(()=>{ // not fired up due to 'then' method
realOrder.push("2");
resolve("2");
},50);
});
//jasmine.clock().tick(51); //<- order is crucial here so I can't do that
return p;
});
jasmine.clock().tick(151);// we must go outside nested promise - we dont need to run tick inplace because order will change
Promise.all([ok1,ok2]).then((results)=>{
expect(results).toEqual(["1","2"]);
expect(realOrder).toEqual(["2","1"]);
done();
});
});
Upvotes: 1
Views: 1356
Reputation: 1159
Ok for those who came with same problem:
[UPDATE]: I created a npm package with that fn https://www.npmjs.com/package/promiseloop
I created a function that will iterate through then
levels of promise execution and on each level run some function like jasmine.clock().tick()
so on each level we will have setTimeout
executed and catched.
function promiseLoop(times,iterationFn,finalFn){
let current=0;
let p=Promise.resolve("ok");
function _loop(fn){
current++;
if(current<times){
p=p.then(()=>{
iterationFn(current);
_loop(finalFn);
});
}else{
p.then(()=>{
setImmediate(()=>{
finalFn();
});
});
}
}
_loop(finalFn);
}
times
is a level - how far we want to go through then
levels?
iterationFn
is function that will be run on each then
level
finalFn
is a final function after last level
here is a full example according to question:
it("should run setTimeout mock inside chained promises",(done)=>{
jasmine.clock().install();
let realOrder = [];
let ok1=new Promise((resolve,reject)=>{
resolve("ok");
}).then((ok)=>{ // second level
let p=new Promise((resolve,reject)=>{
setTimeout(()=>{ // not fired up due to 'then' method
realOrder.push("1");
resolve("1");
},100);
});
//jasmine.clock().tick(101); //<- order is crucial here so I can't do that
return p;
});
let ok2=new Promise((resolve,reject)=>{
resolve("ok");
}).then((ok)=>{ // second level
let p=new Promise((resolve,reject)=>{
setTimeout(()=>{ // not fired up due to 'then' method
realOrder.push("2");
resolve("2");
},50);
});
//jasmine.clock().tick(51); //<- order is crucial here so I can't do that
return p;
});
function iterationTick(){
jasmine.clock().tick(301);
// this will be executed on each promise level in our case second level
// that normally would not be catched by
// jasmine.clock().tick() because 'then' will be
// executed in the future and jasmine.clock().tick() is synchronous
// and will not see future setTimeouts
}
function finalTick(){
Promise.all([ok1,ok2]).then((results)=>{
expect(results).toEqual(["1","2"]);
expect(realOrder).toEqual(["2","1"]);
jasmine.clock().uninstall();
done();
});
//jasmine.clock().tick(301);
}
promiseLoop(2,iterationTick,finalTick);
});
other example
let testNr=0;
new Promise((resolve,reject)=>{
resolve("ok");
}).then(()=>{
// level 1
testNr++; // 1
}).then(()=>{
// level 2
testNr++; // 3
});
new Promise((resolve,reject)=>{
resolve("ok");
}).then(()=>{
// level 1
testNr++; // 2
}).then(()=>{
// level 2
testNr++; // 4
});
function each(currentLevel){
if(currentLevel==1){
expect(testNr).toEqual(2);
}else if(currentLevel==2){
expect(testNr).toEqual(4);
}
}
function final(){
expect(testNr).toEqual(4);
done();
}
promiseLoop(3,each,final);
Upvotes: 1