Search code examples
javascripttypescriptunit-testingjestjsrequestanimationframe

How to write a unit test for my raf util with jest?


I want to write a unit test for my code with jest, but I don't know how to deal with that.

This is my code:

type anyFunction = (...args: any[]) => any

let i = 0
const idMap = new Map<number, ReturnType<typeof requestAnimationFrame>>()

/**
 * Clean `id`.
 *
 * @param id `id` in idMap.
 */
function cleanup(id: number) {
  idMap.delete(id)
}

/**
 * Use RAF to do somthing. When there's no left frames,
 * exec callback function.
 *
 * @param callback Callback when animation is over.
 * @param delayFrames Frames before fn executing.
 * @returns Return id of this RAF.
 */
export function createRAF(callback: anyFunction, delayFrames = 1): number {
  const id = i++

  /**
   * Call raf when there's no left frame.
   *
   * @param leftFrames Left frames before executing.
   */
  function callRaf(leftFrames: number) {
    if (leftFrames === 0) {
      cleanup(id)
      callback()
      return
    }

    // Call raf and bind real raf id with `idMap`.
    const realId = requestAnimationFrame(() => callRaf(leftFrames - 1))
    idMap.set(id, realId)
  }

  callRaf(delayFrames)

  return id
}

/**
 * Cancel raf by id.
 *
 * @param id Raf id created by `raf`.
 */
export function cancelRAF(id: number): void {
  const realId = idMap.get(id)
  if (realId) {
    cleanup(id)
    cancelAnimationFrame(realId)
  }
}

Maybe I need to checkout out effects that cacllback made in every frame? But I don't know how to do.

I look forward to and welcome any suggestions and ideas.


Solution

  • You can use jest.spyOn(object, methodName) to mock window.requestAnimationFrame() method and its implementation.

    index.ts:

    type anyFunction = (...args: any[]) => any;
    
    let i = 0;
    const idMap = new Map<number, ReturnType<typeof requestAnimationFrame>>();
    
    function cleanup(id: number) {
      idMap.delete(id);
    }
    
    export function createRAF(callback: anyFunction, delayFrames = 1): number {
      const id = i++;
      function callRaf(leftFrames: number) {
        if (leftFrames === 0) {
          cleanup(id);
          callback();
          return;
        }
    
        // Call raf and bind real raf id with `idMap`.
        const realId = requestAnimationFrame(() => callRaf(leftFrames - 1));
        idMap.set(id, realId);
      }
      callRaf(delayFrames);
      return id;
    }
    

    index.test.ts:

    import { createRAF } from './';
    
    describe('69582282', () => {
      test('should pass', () => {
        const requestAnimationFrameSpy = jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
          cb(Date.now());
          return Math.random();
        });
        const callback = jest.fn();
        createRAF(callback);
        expect(callback).toBeCalledTimes(1);
        expect(requestAnimationFrameSpy).toBeCalledTimes(1);
        requestAnimationFrameSpy.mockRestore();
      });
    });
    

    test result:

     PASS  examples/69582282/index.test.ts (11.298 s)
      69582282
        ✓ should pass (6 ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |     100 |      100 |     100 |     100 |                   
     index.ts |     100 |      100 |     100 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        12.733 s
    

    package version: "jest": "^26.6.3"