Skip to content

Commit

Permalink
Add optional options arg to control what is included in error message
Browse files Browse the repository at this point in the history
  • Loading branch information
mattphillips committed Sep 8, 2022
1 parent 049d705 commit 13aa927
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 20 deletions.
65 changes: 57 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ possible in Jest.
For example:

```js
test("returns 2 when adding 1 and 1", () => {
expect(1 + 1, "Woah this should be 2!").toBe(3);
test('returns 2 when adding 1 and 1', () => {
expect(1 + 1, 'Woah this should be 2!').toBe(3);
});
```

Expand All @@ -44,8 +44,8 @@ Expect takes at most one argument.
For example the same test as above:

```js
test("returns 2 when adding 1 and 1", () => {
expect(1 + 1, "Woah this should be 2!").toBe(3);
test('returns 2 when adding 1 and 1', () => {
expect(1 + 1, 'Woah this should be 2!').toBe(3);
});
```

Expand Down Expand Up @@ -101,7 +101,7 @@ Add `jest-expect-message` to your Jest `setupFilesAfterEnv` configuration.
If you have a custom setup file and want to use this library then add the following to your setup file.

```js
import "jest-expect-message";
import 'jest-expect-message';
```

### Configure Typescript
Expand Down Expand Up @@ -131,14 +131,63 @@ Custom message [example](/example) with typescript

## Usage

- `expect(actual, message)`
- `expect(actual, message, options?)`
- `actual`: The value you would normally pass into an `expect` to assert against with a given matcher.
- `message`: String, the custom message you want to be printed should the `expect` fail.
- `options`: An optional object that controls if the custom message prefix and/or if the original matcher message are shown.
- type: `{ showPrefix: boolean, showMatcherMessage: boolean }`
- Note: `options` is optional and so are both `showPrefix` and `showMatcherMessage` which will be defaulted to `true`.

```js
test("returns 2 when adding 1 and 1", () => {
expect(1 + 1, "Woah this should be 2!").toBe(3);
test('returns 2 when adding 1 and 1', () => {
expect(1 + 1, 'Woah this should be 2!').toBe(3);
});
// ↓ ↓ ↓ ↓ ↓ ↓
/*
● returns 2 when adding 1 and 1
Custom message:
Woah this should be 2!
expect(received).toBe(expected) // Object.is equality
Expected: 3
Received: 2
*/
```

### ShowPrefix false

```js
test('returns 2 when adding 1 and 1', () => {
expect(1 + 1, 'Woah this should be 2!', { showPrefix: false }).toBe(3);
});
// ↓ ↓ ↓ ↓ ↓ ↓
/*
● returns 2 when adding 1 and 1
Woah this should be 2!
expect(received).toBe(expected) // Object.is equality
Expected: 3
Received: 2
*/
```

### ShowMatcherMessage false

```js
test('returns 2 when adding 1 and 1', () => {
expect(1 + 1, 'Woah this should be 2!', { showMatcherMessage: false }).toBe(3);
});
// ↓ ↓ ↓ ↓ ↓ ↓
/*
● returns 2 when adding 1 and 1
Custom message:
Woah this should be 2!
*/
```

## LICENSE
Expand Down
24 changes: 15 additions & 9 deletions src/withMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class JestAssertionError extends Error {
}
}

const wrapMatcher = (matcher, customMessage) => {
const wrapMatcher = (matcher, customMessage, config) => {
const newMatcher = (...args) => {
try {
return matcher(...args);
Expand All @@ -26,44 +26,50 @@ const wrapMatcher = (matcher, customMessage) => {
const matcherMessage =
typeof error.matcherResult.message === 'function' ? error.matcherResult.message() : error.matcherResult.message;

const message = () => 'Custom message:\n ' + customMessage + '\n\n' + matcherMessage;
const messagePrefix = config.showPrefix ? 'Custom message:\n ' : '';

const message = () => messagePrefix + customMessage + (config.showMatcherMessage ? '\n\n' + matcherMessage : '');

throw new JestAssertionError({ ...matcherResult, message }, newMatcher);
}
};
return newMatcher;
};

const wrapMatchers = (matchers, customMessage) => {
const wrapMatchers = (matchers, customMessage, config) => {
return Object.keys(matchers).reduce((acc, name) => {
const matcher = matchers[name];

if (typeof matcher === 'function') {
acc[name] = wrapMatcher(matcher, customMessage);
acc[name] = wrapMatcher(matcher, customMessage, config);
} else {
acc[name] = wrapMatchers(matcher, customMessage); // recurse on .not/.resolves/.rejects
acc[name] = wrapMatchers(matcher, customMessage, config); // recurse on .not/.resolves/.rejects
}

return acc;
}, {});
};

export default expect => {
export default (expect) => {
// proxy the expect function
let expectProxy = Object.assign(
(actual, customMessage) => {
(actual, customMessage, options = {}) => {
const config = {
showMatcherMessage: typeof options.showMatcherMessage === 'boolean' ? options.showMatcherMessage : true,
showPrefix: typeof options.showPrefix === 'boolean' ? options.showPrefix : true
};
let matchers = expect(actual); // partially apply expect to get all matchers and chain them
if (customMessage) {
// only pay the cost of proxying matchers if we received a customMessage
matchers = wrapMatchers(matchers, customMessage);
matchers = wrapMatchers(matchers, customMessage, config);
}

return matchers;
},
expect // clone additional properties on expect
);

expectProxy.extend = o => {
expectProxy.extend = (o) => {
expect.extend(o); // add new matchers to expect
expectProxy = Object.assign(expectProxy, expect); // clone new asymmetric matchers
};
Expand Down
78 changes: 76 additions & 2 deletions src/withMessage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('withMessage()', () => {
expect(toBeMock).toHaveBeenCalledWith(1);
});

test.each([undefined, ''])('throws original error when given message: %s', message => {
test.each([undefined, ''])('throws original error when given message: %s', (message) => {
expect.assertions(3);
const originalError = new Error('Boo');
const toBeMock = jest.fn(() => {
Expand Down Expand Up @@ -141,7 +141,7 @@ describe('withMessage()', () => {

it('sets new asymmetric matchers when custom matcher is registered with expect.extend', () => {
const expectMock = () => {};
const extendMock = jest.fn(o => Object.assign(expectMock, o));
const extendMock = jest.fn((o) => Object.assign(expectMock, o));
expectMock.a = 'a';
expectMock.extend = extendMock;
const newMatcher = { newMatcher: 'woo' };
Expand All @@ -156,4 +156,78 @@ describe('withMessage()', () => {
expect(extendMock).toHaveBeenCalledWith(newMatcher);
expect(actual).toContainAllKeys(['a', 'extend', 'newMatcher']);
});

test('does not throw error with matcher message when `config.showMatcherMessage` is false', () => {
expect.assertions(2);
const originalError = new Error('Boo');
const matcherMessage = 'expected ACTUAL to not be ACTUAL';
originalError.matcherResult = {
actual: ACTUAL,
expected: 1,
message: matcherMessage,
pass: false
};
const toBeMock = jest.fn(() => {
throw originalError;
});
const expectMock = jest.fn(() => ({ toBe: toBeMock }));

const customMessage = 'should fail';
try {
withMessage(expectMock)(ACTUAL, customMessage, { showMatcherMessage: false }).toBe(1);
} catch (e) {
expect(e.message).toInclude(customMessage);
expect(e.message).not.toInclude(matcherMessage);
}
});

test('does not throw error with custom message prefix when `config.showPrefix` is false', () => {
expect.assertions(3);
const originalError = new Error('Boo');
const matcherMessage = 'expected ACTUAL to not be ACTUAL';
originalError.matcherResult = {
actual: ACTUAL,
expected: 1,
message: matcherMessage,
pass: false
};
const toBeMock = jest.fn(() => {
throw originalError;
});
const expectMock = jest.fn(() => ({ toBe: toBeMock }));

const customMessage = 'should fail';
try {
withMessage(expectMock)(ACTUAL, customMessage, { showPrefix: false }).toBe(1);
} catch (e) {
expect(e.message).not.toInclude('Custom message:\n');
expect(e.message).toInclude(customMessage);
expect(e.message).toInclude(matcherMessage);
}
});

test('throws error containing only custom message prefix when `config.showPrefix` and `config.showMatcherMessage` are false', () => {
expect.assertions(3);
const originalError = new Error('Boo');
const matcherMessage = 'expected ACTUAL to not be ACTUAL';
originalError.matcherResult = {
actual: ACTUAL,
expected: 1,
message: matcherMessage,
pass: false
};
const toBeMock = jest.fn(() => {
throw originalError;
});
const expectMock = jest.fn(() => ({ toBe: toBeMock }));

const customMessage = 'should fail';
try {
withMessage(expectMock)(ACTUAL, customMessage, { showPrefix: false, showMatcherMessage: false }).toBe(1);
} catch (e) {
expect(e.message).not.toInclude('Custom message:\n');
expect(e.message).not.toInclude(matcherMessage);
expect(e.message).toInclude(customMessage);
}
});
});
6 changes: 5 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
declare namespace jest {
interface Expect {
<T = any>(actual: T, message?: string): Matchers<T>;
<T = any>(
actual: T,
message?: string,
options?: { showMatcherMessage?: boolean; showPrefix?: boolean }
): Matchers<T>;
}
}

0 comments on commit 13aa927

Please sign in to comment.