How do you flush requestIdleCallback in an Angular service test?

536 views Asked by At

I'm writing an Angular service that uses requestIdleCallback to call another function, foo. In my test, I want to make sure that foo is being called eventually.

Currently I have a test that looks like this:

   it('test', fakeAsync(() => {
     // Triggers a requestIdleCallback call.
     service.doSomething();

     flush();
     expect(otherService.foo).toHaveBeenCalled();
   }));

But when I run this test, I get an error saying that otherService.foo was never called. I've tried replacing the flush with flushMicroTasks() and tick() with a long timer to no avail. The only thing that has worked so far was to use done() and add a setTimeout to the test itself like this:

   it('test', (done) => {
     // Triggers a requestIdleCallback call.
     service.doSomething();

     setTimeout(() => {
       expect(otherService.foo).toHaveBeenCalled();      
       done();
     }, 1);
   });

But I would like to avoid this because it could cause some flakiness. Is there anyway to guarantee that requestIdleCallback was flushed before I make my test assertion?

1

There are 1 answers

3
Kaiido On

According to this PR comment, you can define custom macroTasks to be tested by fakeAsync zone.

They say you have two options:

  1. pass the options from FakeAsyncTestSpec constructor

    const testZoneSpec = new FakeAsyncTestZoneSpec('name', false, [{source: 'HTMLCanvasElement.toBlob'}]);
    
  2. sometimes application test code can't access FakeAsyncTestSpec initialize process, so user can also define a global variable before load fakeAsyncTest.

    [...]

    window['__zone_symbol__FakeAsyncTestMacroTask'] = [{
      source: 'HTMLCanvasElement.toBlob',
      callbackArgs: ['testBlobData']  // the test blob data which will be passed back to callback
    }];
    

So in your case you'd replace source: HTMLCanvasElement.toBlob with source: window.requestIdleCallback.