feat: Add new toBeRejectedWithMatching async matcher

Addresses #1781
This commit is contained in:
Benoit CHOMEL
2026-02-26 00:01:45 +01:00
parent f7132d98cd
commit 0a5acd99cb
3 changed files with 144 additions and 1 deletions

View File

@@ -0,0 +1,78 @@
describe('#toBeRejectedWithMatching', function() {
it('passes if the promise is rejected with something matching the predicate', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = Promise.reject(new Error('nope')),
predicate = value => value.message === 'nope';
return matcher.compare(actual, predicate).then(function(result) {
expect(result).toEqual(jasmine.objectContaining({ pass: true }));
});
});
it('fails if the promise resolves', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = Promise.resolve(),
predicate = () => true;
return matcher.compare(actual, predicate).then(function(result) {
expect(result).toEqual(jasmine.objectContaining({ pass: false }));
});
});
it('fails if the promise is rejected with something not matching the predicate', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = Promise.reject('A Bad Apple'),
predicate = value => value === 'A Good Orange';
return matcher.compare(actual, predicate).then(function(result) {
expect(result).toEqual(
jasmine.objectContaining({
pass: false,
message:
'Expected a promise to be rejected matching a predicate, but the predicate returned "false".'
})
);
});
});
it('should build its error correctly when negated', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = Promise.reject(true),
predicate = () => true;
return matcher.compare(actual, predicate).then(function(result) {
expect(result).toEqual(
jasmine.objectContaining({
pass: true,
message:
'Expected a promise not to be rejected matching a predicate, but the predicate returned "true".'
})
);
});
});
it('fails if actual is not a promise', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = 'not a promise';
function f() {
return matcher.compare(actual);
}
expect(f).toThrowError(
'Expected toBeRejectedWithMatching to be called on a promise but was on a string.'
);
});
it('fails if predicate is not a function', function() {
const matcher = privateUnderTest.asyncMatchers.toBeRejectedWithMatching(),
actual = Promise.resolve(),
predicate = 'not a function';
function f() {
return matcher.compare(actual, predicate);
}
expect(f).toThrowError(/Predicate is not a Function/);
});
});

View File

@@ -0,0 +1,64 @@
getJasmineRequireObj().toBeRejectedWithMatching = function(j$) {
'use strict';
const usageError = j$.private.formatErrorMsg(
'<toBeRejectedWithMatching>',
'expect(function() {<expectation>}).toBeRejectedWithMatching(<Predicate>)'
);
/**
* Expect a promise to be rejected with a value matching a predicate.
* @function
* @async
* @name async-matchers#toBeRejectedWithMatching
* @since 6.2.0
* @param {Function} predicate - A function that takes the rejected promise value as its parameter and returns true if it matches.
* @example
* await expectAsync(aPromise).toBeRejectedWithMatching((error) => error.message === 'Some error');
* @example
* return expectAsync(aPromise).toBeRejectedWithMatching((error) => error.message === 'Some error');
*/
return function toBeRejectedWithMatching() {
return {
compare: function(actualPromise, predicate) {
if (!j$.private.isPromiseLike(actualPromise)) {
throw new Error(
`Expected toBeRejectedWithMatching to be called on a promise but was on a ${typeof actualPromise}.`
);
}
if (typeof predicate !== 'function') {
throw new Error(usageError('Predicate is not a Function'));
}
function prefix(passed) {
return (
'Expected a promise ' +
(passed ? 'not ' : '') +
'to be rejected matching a predicate'
);
}
return actualPromise.then(
function() {
return {
pass: false,
message: prefix(false) + ', but it was resolved.'
};
},
function(actualValue) {
const result = predicate(actualValue);
return {
pass: Boolean(result),
message:
prefix(result) +
', but the predicate returned "' +
result +
'".'
};
}
);
}
};
};
};

View File

@@ -7,7 +7,8 @@ getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) {
'toBeRejected',
'toBeResolvedTo',
'toBeRejectedWith',
'toBeRejectedWithError'
'toBeRejectedWithError',
'toBeRejectedWithMatching'
],
matchers = {};