Hone logo
Hone
Problems

Asynchronous Utility Functions for Jest Testing

Testing asynchronous code in Jest can be tricky. This challenge asks you to create a set of utility functions that simplify testing asynchronous operations, particularly those involving Promises and async/await. These utilities will provide a more robust and readable way to handle asynchronous assertions within your Jest tests.

Problem Description

You are tasked with creating a TypeScript module containing several utility functions designed to improve the readability and reliability of asynchronous tests in Jest. These utilities should address common challenges like waiting for Promises to resolve or reject, handling timeouts, and asserting against asynchronous values.

Specifically, you need to implement the following functions:

  1. waitFor: This function takes an asynchronous function (returning a Promise) and a timeout in milliseconds as input. It should return a Promise that resolves with the value returned by the asynchronous function when it resolves, or rejects with an error if the timeout is reached.
  2. rejectAfter: This function takes a Promise and a timeout in milliseconds. It returns a new Promise that either resolves with the original Promise's value or rejects with a timeout error if the original Promise doesn't resolve within the specified timeout.
  3. asyncAssertion: This function takes an assertion function (that accepts a value) and an asynchronous value (a Promise). It awaits the asynchronous value and then passes it to the assertion function. This allows you to assert against the result of an asynchronous operation directly within your test.

Key Requirements:

  • All functions must be written in TypeScript.
  • The waitFor and rejectAfter functions should handle timeouts gracefully, rejecting with a clear error message.
  • The asyncAssertion function should handle potential rejections from the asynchronous value.
  • The code should be well-documented with JSDoc comments.
  • The module should be designed for easy import and use in Jest tests.

Expected Behavior:

  • waitFor should resolve with the value of the Promise if it resolves within the timeout.
  • waitFor should reject with a TimeoutError if the Promise doesn't resolve within the timeout.
  • rejectAfter should resolve with the value of the Promise if it resolves within the timeout.
  • rejectAfter should reject with a TimeoutError if the Promise doesn't resolve within the timeout.
  • asyncAssertion should call the assertion function with the resolved value of the Promise.
  • asyncAssertion should reject if the Promise rejects.

Edge Cases to Consider:

  • Promises that resolve immediately.
  • Promises that reject immediately.
  • Promises that take a long time to resolve (close to the timeout).
  • Promises that never resolve (infinite loops, etc.).
  • Invalid timeout values (e.g., negative numbers).

Examples

Example 1:

Input: waitFor(() => Promise.resolve(10), 500), timeout = 500ms
Output: 10
Explanation: The Promise resolves within the timeout, so waitFor resolves with the resolved value.

Example 2:

Input: waitFor(() => new Promise((resolve) => setTimeout(() => resolve(20), 1000)), timeout = 500ms
Output: Error: TimeoutError: Timeout after 500ms
Explanation: The Promise takes longer than the timeout to resolve, so waitFor rejects with a TimeoutError.

Example 3:

Input: asyncAssertion(expect, () => Promise.resolve("hello")),
Output: expect("hello").toBe("hello") (within a Jest test)
Explanation: asyncAssertion awaits the Promise and then passes the resolved value ("hello") to the assertion function.

Constraints

  • Timeout values must be non-negative numbers. If a negative timeout is provided, the function should reject with a TypeError.
  • The waitFor and rejectAfter functions should use setTimeout for timeout implementation.
  • The TimeoutError should be a custom error class with a message indicating the timeout duration.
  • The module should be exportable and importable using standard ES module syntax.
  • The functions should be reasonably performant; avoid unnecessary overhead.

Notes

  • Consider using Promise.race to implement the timeout functionality.
  • Think about how to handle errors gracefully and provide informative error messages.
  • Focus on creating reusable and well-documented utility functions.
  • This challenge is designed to test your understanding of asynchronous JavaScript, Promises, async/await, and error handling. Good luck!
Loading editor...
typescript