Skip to content

Commit

Permalink
feat: Attach actual/expected args to error instance to enable IDE diffs
Browse files Browse the repository at this point in the history
  • Loading branch information
NiGhTTraX committed Sep 24, 2023
1 parent ba4f6b5 commit 7a04a43
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 10 deletions.
35 changes: 35 additions & 0 deletions src/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,41 @@ foobar`
// Yeah, funky way to do a negated ansiless contains.
expect(() => expectAnsilessContain(error.message, `bar`)).toThrow();
});

it("should contain actual and expected values when there's a single expectation remaining", () => {
const matcher = It.matches(() => false, {
getDiff: () => ({ actual: 'actual', expected: 'expected' }),
});

const expectation = new StrongExpectation('foo', [matcher, matcher], {
value: ':irrelevant:',
});

const error = new UnexpectedCall(
'foo',
['any arg', 'any arg'],
[expectation]
);

expect(error.matcherResult).toEqual({
actual: ['actual', 'actual'],
expected: ['expected', 'expected'],
});
});

it('should not contain actual and expected values when the expectation has no expected args', () => {
const expectation = new StrongExpectation('foo', [], {
value: ':irrelevant:',
});

const error = new UnexpectedCall(
'foo',
['any arg', 'any arg'],
[expectation]
);

expect(error.matcherResult).toBeUndefined();
});
});

describe('UnexpectedCalls', () => {
Expand Down
32 changes: 30 additions & 2 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EXPECTED_COLOR } from 'jest-matcher-utils';
import type { Expectation } from './expectation/expectation';
import { getMatcherDiffs } from './expectation/matcher';
import type { CallMap } from './expectation/repository/expectation-repository';
import {
printCall,
Expand Down Expand Up @@ -42,7 +43,16 @@ ${printRemainingExpectations(expectations)}`);
}
}

export class UnexpectedCall extends Error {
type MatcherResult = { expected: unknown; actual: unknown };

// This is taken from jest.
interface MatcherError {
matcherResult?: MatcherResult;
}

export class UnexpectedCall extends Error implements MatcherError {
public matcherResult?: MatcherResult;

constructor(
property: Property,
args: unknown[],
Expand All @@ -62,8 +72,26 @@ export class UnexpectedCall extends Error {
Remaining expectations:
${printDiffForAllExpectations(propertyExpectations, args)}`);

// If we have a single expectation we can attach the actual/expected args
// to the error instance, so that an IDE may show its own diff for them.
if (
propertyExpectations.length === 1 &&
propertyExpectations[0].args?.length
) {
const { actual, expected } = getMatcherDiffs(
propertyExpectations[0].args,
args
);
this.matcherResult = {
actual,
expected,
};
}
} else {
super(header);
super(`${header}
No remaining expectations.`);
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/expectation/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,14 @@ export type TypeMatcher<T> = T & Matcher;
export function isMatcher(f: unknown): f is Matcher {
return !!(f && (<Matcher>f)[MATCHER_SYMBOL]);
}

export const getMatcherDiffs = (
matchers: Matcher[],
args: any[]
): { actual?: unknown[]; expected?: unknown[] } => {
const matcherDiffs = matchers.map((matcher, i) => matcher.getDiff(args[i]));
const actual = matcherDiffs?.map((d) => d.actual);
const expected = matcherDiffs?.map((d) => d.expected);

return { actual, expected };
};
11 changes: 3 additions & 8 deletions src/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from 'jest-matcher-utils';
import type { Expectation } from './expectation/expectation';
import { ApplyProp } from './expectation/expectation';
import { isMatcher } from './expectation/matcher';
import { getMatcherDiffs, isMatcher } from './expectation/matcher';
import type { ReturnValue } from './expectation/repository/return-value';
import type { Property } from './proxy';

Expand Down Expand Up @@ -83,13 +83,8 @@ export const printExpectationDiff = (e: Expectation, args: any[]) => {
return '';
}

const matcherDiffs = e.args?.map((matcher, j) => matcher.getDiff(args[j]));

const diff = printDiff(
matcherDiffs?.map((d) => d.expected),
matcherDiffs?.map((d) => d.actual),
{ omitAnnotationLines: true }
);
const { actual, expected } = getMatcherDiffs(e.args, args);
const diff = printDiff(expected, actual, { omitAnnotationLines: true });

if (!diff) {
return '';
Expand Down

0 comments on commit 7a04a43

Please sign in to comment.