neuronet
neuronet

Reputation: 1159

how to use jasmine clock (setTimeout) in nested promises when order matters? (jasmine.DEFAULT_TIMEOUT_INTERVAL error)

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

Answers (1)

neuronet
neuronet

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

Related Questions