Implementing a Custom Rejects Matcher in Jest
Jest's built-in matchers are powerful, but sometimes you need more specific assertions for asynchronous code that's expected to reject. This challenge asks you to implement a custom Jest matcher that specifically checks if a promise rejects with a particular error or error message. This is useful for ensuring your error handling is working correctly and that your code throws the expected errors in specific scenarios.
Problem Description
You need to create a custom Jest matcher called rejectsWithMessage. This matcher will take a promise and a message (string) as input. It should assert that the promise rejects and that the rejection reason is a string that strictly equals the provided message. The matcher should work similarly to Jest's built-in rejects matcher, but with the added constraint of verifying the rejection message.
Key Requirements:
- The matcher must accept a promise and a message string as arguments.
- It must use
expect(promise).rejectsinternally to verify the promise rejects. - It must then assert that the rejection reason is strictly equal to the provided message string.
- The matcher should return a boolean indicating whether the assertion passed or failed.
- The matcher should provide a clear error message if the assertion fails, indicating both that the promise didn't reject and/or that the rejection message didn't match.
Expected Behavior:
- If the promise rejects with the expected message, the matcher should pass.
- If the promise rejects with a different message, the matcher should fail with a descriptive error.
- If the promise resolves instead of rejecting, the matcher should fail with a descriptive error.
Edge Cases to Consider:
- Promises that resolve instead of rejecting.
- Rejections with non-string error messages (e.g., objects, numbers).
- Empty or null message strings.
- Promises that throw synchronously instead of asynchronously rejecting.
Examples
Example 1:
Input: promise that rejects with "Network error", "Network error"
Output: true
Explanation: The promise rejects with the expected message.
Example 2:
Input: promise that rejects with "Database error", "Network error"
Output: false
Explanation: The promise rejects, but with a different message. The error message should indicate both that the promise rejected and that the message didn't match.
Example 3:
Input: promise that resolves with "Success", "Network error"
Output: false
Explanation: The promise resolves, not rejects. The error message should indicate that the promise did not reject.
Example 4:
Input: promise that rejects with { message: "Network error" }, "Network error"
Output: false
Explanation: The promise rejects with an object, not a string. The error message should indicate that the rejection reason was not a string.
Constraints
- The matcher must be implemented in TypeScript.
- The matcher should be compatible with Jest versions 25 or higher.
- The matcher should not introduce any external dependencies.
- The matcher should be reasonably performant; avoid unnecessary computations.
Notes
- You'll need to use Jest's
expectandrejectsfunctions to implement this matcher. - Consider using
toStrictlyEqualfor the message comparison to ensure strict equality. - Think about how to provide informative error messages to the user when the assertion fails. The error message should clearly indicate why the assertion failed (e.g., "Expected promise to reject with 'Network error', but it resolved", or "Expected promise to reject with 'Network error', but it rejected with 'Database error'").
- Remember that
rejectsalready handles the asynchronous nature of promises. You don't need to manually await the promise within your matcher.