Jest Assertion Helper: expectAsync
Writing asynchronous tests in Jest can sometimes be verbose and repetitive, especially when dealing with Promises or async/await. This challenge asks you to create a reusable assertion helper function, expectAsync, that simplifies the process of asserting the resolution or rejection of asynchronous operations. This helper will improve test readability and reduce boilerplate code.
Problem Description
You need to implement a TypeScript function called expectAsync that takes an asynchronous operation (a Promise or a function returning a Promise) as input and returns an object with methods for asserting its resolution or rejection. The returned object should have the following methods:
resolves.with(value): Asserts that the asynchronous operation resolves with the givenvalue.rejects.with(error): Asserts that the asynchronous operation rejects with the givenerror. Theerrorcan be a value, a constructor function (e.g.,Error), or a regular expression. If it's a constructor function, the rejection must be an instance of that constructor. If it's a regular expression, the rejection must match that regex.resolves: Asserts that the asynchronous operation resolves (without specifying a value).rejects: Asserts that the asynchronous operation rejects (without specifying an error).
The expectAsync function should internally use Jest's expect and resolves / rejects matchers to perform the assertions. It should handle both Promises and functions that return Promises correctly.
Examples
Example 1:
Input: expectAsync(() => Promise.resolve(42))
Output: {
resolves: {
with: (value) => expect(42).resolves.with(value),
resolves: () => expect(42).resolves
},
rejects: {
with: (error) => expect(42).rejects.with(error),
rejects: () => expect(42).rejects
}
}
Explanation: The function returns an object with methods to assert resolution or rejection of a Promise that resolves to 42.
Example 2:
Input: expectAsync(async () => { throw new Error("Something went wrong"); })
Output: {
resolves: {
with: (value) => expect(undefined).resolves.with(value),
resolves: () => expect(undefined).resolves
},
rejects: {
with: (error) => expect(undefined).rejects.with(error),
rejects: () => expect(undefined).rejects
}
}
Explanation: The function returns an object with methods to assert resolution or rejection of a Promise that rejects with an Error.
Example 3:
Input: expectAsync(async () => Promise.reject("Network error"))
Output: {
resolves: {
with: (value) => expect(undefined).resolves.with(value),
resolves: () => expect(undefined).resolves
},
rejects: {
with: (error) => expect(undefined).rejects.with(error),
rejects: () => expect(undefined).rejects
}
}
Explanation: The function returns an object with methods to assert resolution or rejection of a Promise that rejects with the string "Network error".
Constraints
- The function must be written in TypeScript.
- The function must correctly handle both Promises and functions returning Promises.
- The
resolves.withandrejects.withmethods should use Jest'sresolves.withandrejects.withmatchers respectively. - The
rejects.withmethod should handle errors as values, constructor functions, and regular expressions as described above. - The function should not throw errors if the asynchronous operation completes successfully or fails. The assertions themselves will throw errors if they fail.
Notes
- Consider using a closure to capture the asynchronous operation within the
expectAsyncfunction. - Think about how to handle the case where the asynchronous operation never completes (e.g., an infinite loop). While not strictly required to handle this perfectly, consider how your solution might behave in such a scenario.
- The goal is to create a clean and reusable assertion helper that simplifies asynchronous testing in Jest. Focus on readability and maintainability.
- You can assume that Jest is already configured and available in your environment.