Hone logo
Hone
Problems

Implement a Jest waitFor Utility

Jest's waitFor function is a crucial tool for testing asynchronous operations. It allows tests to wait for a specific condition to become true before proceeding, making tests more robust and less prone to flaky failures. Your challenge is to implement a simplified version of this utility.

Problem Description

You need to create a TypeScript function named customWaitFor that mimics the core functionality of Jest's waitFor. This function should accept a callback that returns a promise, and it should repeatedly execute this callback until it resolves successfully or a timeout occurs.

Key Requirements:

  • The customWaitFor function should accept two arguments:
    • callback: A function that returns a Promise<T> for some type T.
    • options (optional): An object with the following properties:
      • timeout: The maximum time in milliseconds to wait for the callback to resolve. Defaults to 1000ms.
      • interval: The time in milliseconds to wait between retrying the callback. Defaults to 50ms.
  • The customWaitFor function should return a Promise<T> that resolves with the value returned by the callback when it successfully resolves.
  • If the callback throws an error or rejects its promise, customWaitFor should retry the callback after the specified interval, up to the timeout.
  • If the timeout is reached before the callback resolves successfully, customWaitFor should reject with an error indicating that the timeout occurred.
  • The callback should be executed at least once, even if the timeout is less than the interval.

Expected Behavior:

  • If the callback resolves within the timeout, the returned promise should resolve with the resolved value.
  • If the callback continuously rejects or throws errors, and the timeout is reached, the returned promise should reject with a timeout error.

Edge Cases:

  • What happens if timeout is 0?
  • What happens if interval is 0?
  • What happens if the callback resolves immediately?
  • What happens if the callback throws synchronously?

Examples

Example 1:

async function resolveAfter100ms() {
  return new Promise(resolve => setTimeout(() => resolve('Success!'), 100));
}

// Inside a test:
await customWaitFor(() => resolveAfter100ms(), { timeout: 500, interval: 50 });
// Expected output: Promise resolves with 'Success!'

Explanation: The resolveAfter100ms function resolves after 100ms. customWaitFor will retry it every 50ms. Since 100ms is well within the 500ms timeout, it will resolve with 'Success!'.

Example 2:

let counter = 0;
async function resolveAfter3Attempts() {
  counter++;
  if (counter === 3) {
    return new Promise(resolve => resolve('Resolved after 3 attempts'));
  }
  return new Promise((_, reject) => reject(new Error('Not ready yet')));
}

// Inside a test:
await customWaitFor(() => resolveAfter3Attempts(), { timeout: 500, interval: 50 });
// Expected output: Promise resolves with 'Resolved after 3 attempts'

Explanation: The resolveAfter3Attempts function will reject twice before resolving on the third attempt. With an interval of 50ms, the third attempt will occur around 100ms, well within the 500ms timeout.

Example 3:

async function neverResolve() {
  return new Promise(() => {}); // This promise never resolves
}

// Inside a test:
await customWaitFor(() => neverResolve(), { timeout: 200, interval: 50 });
// Expected output: Promise rejects with an error like "customWaitFor timed out in 200ms."

Explanation: The neverResolve function will never resolve or reject. customWaitFor will keep retrying until the 200ms timeout is reached, at which point it will reject.

Constraints

  • timeout: Must be a non-negative integer.
  • interval: Must be a non-negative integer.
  • The callback function is assumed to be a function that returns a Promise.

Notes

  • Consider how you will manage timers effectively. setTimeout and clearTimeout will be your primary tools.
  • Think about how to handle both promise rejections and synchronous errors thrown by the callback.
  • The default values for timeout and interval should be respected if not provided.
  • The error message for timeout should be informative.
Loading editor...
typescript