Skip to content

Commit c46b52e

Browse files
committed
Merge branch 'feature/jest-expect-type' into 'master'
refactor: jest matchers no longer depend on global expect See merge request html-validate/html-validate!872
2 parents b5003ef + 3cd2a7f commit c46b52e

15 files changed

+279
-193
lines changed

src/jest/internal-matchers/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default as toBeToken } from "./to-be-token";
1+
export { toBeToken } from "./to-be-token";
Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,45 @@
11
/* eslint-disable prefer-template -- technical debt, should be refactored*/
22

33
import { TokenType } from "../../lexer";
4-
import { type MatcherContext, type MatcherResult, diff, diverge } from "../utils";
4+
import {
5+
type MatcherContext,
6+
type MatcherExpect,
7+
type MatcherResult,
8+
type MaybeAsyncCallback,
9+
diff,
10+
diverge,
11+
} from "../utils";
512

6-
function toBeToken(this: MatcherContext, actual: any, expected: any): MatcherResult {
7-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- technical debt, this should be refactored and made typesafe */
8-
const token = actual.value;
13+
function createMatcher(expect: MatcherExpect): MaybeAsyncCallback<any, [any]> {
14+
function toBeToken(this: MatcherContext, actual: any, expected: any): MatcherResult {
15+
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- technical debt, this should be refactored and made typesafe */
16+
const token = actual.value;
917

10-
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
11-
if (token.type) {
12-
token.type = TokenType[token.type];
13-
}
18+
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
19+
if (token.type) {
20+
token.type = TokenType[token.type];
21+
}
1422

15-
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
16-
if (expected.type) {
17-
expected.type = TokenType[expected.type];
18-
}
23+
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
24+
if (expected.type) {
25+
expected.type = TokenType[expected.type];
26+
}
1927

20-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- outside our control, this is what jest gives us back and it gladly accepts it back */
21-
const matcher = expect.objectContaining(expected);
22-
const pass = this.equals(token, matcher);
23-
const diffString = diff(matcher, token, { expand: this.expand });
24-
const message = (): string =>
25-
this.utils.matcherHint(".toBeToken") +
26-
"\n\n" +
27-
"Expected token to equal:\n" +
28-
` ${this.utils.printExpected(matcher)}\n` +
29-
"Received:\n" +
30-
` ${this.utils.printReceived(token)}` +
31-
/* istanbul ignore next */ (diffString ? `\n\nDifference:\n\n${diffString}` : "");
28+
const matcher = expect.objectContaining(expected);
29+
const pass = this.equals(token, matcher);
30+
const diffString = diff(matcher, token, { expand: this.expand });
31+
const message = (): string =>
32+
this.utils.matcherHint(".toBeToken") +
33+
"\n\n" +
34+
"Expected token to equal:\n" +
35+
` ${this.utils.printExpected(matcher)}\n` +
36+
"Received:\n" +
37+
` ${this.utils.printReceived(token)}` +
38+
/* istanbul ignore next */ (diffString ? `\n\nDifference:\n\n${diffString}` : "");
3239

33-
return { pass, message };
40+
return { pass, message };
41+
}
42+
return diverge(toBeToken);
3443
}
3544

36-
export default diverge(toBeToken);
45+
export { createMatcher as toBeToken };

src/jest/jest-internal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ declare global {
2121
}
2222

2323
expect.extend({
24-
toBeToken,
24+
toBeToken: toBeToken(expect),
2525
});

src/jest/jest.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import {
1111
} from "./matchers";
1212

1313
expect.extend({
14-
toBeValid,
15-
toBeInvalid,
16-
toHaveError,
17-
toHaveErrors,
18-
toHTMLValidate,
19-
toMatchCodeframe,
20-
toMatchInlineCodeframe,
14+
toBeValid: toBeValid(),
15+
toBeInvalid: toBeInvalid(),
16+
toHTMLValidate: toHTMLValidate(expect),
17+
toHaveError: toHaveError(expect),
18+
toHaveErrors: toHaveErrors(expect),
19+
toMatchCodeframe: toMatchCodeframe(),
20+
toMatchInlineCodeframe: toMatchInlineCodeframe(),
2121
});

src/jest/matchers/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
export { default as toBeValid } from "./to-be-valid";
2-
export { default as toBeInvalid } from "./to-be-invalid";
3-
export { default as toHTMLValidate } from "./to-htmlvalidate";
4-
export { default as toHaveError } from "./to-have-error";
5-
export { default as toHaveErrors } from "./to-have-errors";
6-
export { default as toMatchCodeframe } from "./to-match-codeframe";
7-
export { default as toMatchInlineCodeframe } from "./to-match-inline-codeframe";
1+
export { toBeValid } from "./to-be-valid";
2+
export { toBeInvalid } from "./to-be-invalid";
3+
export { toHTMLValidate } from "./to-htmlvalidate";
4+
export { toHaveError } from "./to-have-error";
5+
export { toHaveErrors } from "./to-have-errors";
6+
export { toMatchCodeframe } from "./to-match-codeframe";
7+
export { toMatchInlineCodeframe } from "./to-match-inline-codeframe";

src/jest/matchers/to-be-invalid.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { type Report } from "../../reporter";
2-
import { type MatcherResult, diverge } from "../utils";
2+
import { type MatcherResult, type MaybeAsyncCallback, diverge } from "../utils";
33

4-
function toBeInvalid(report: Report): MatcherResult {
5-
if (report.valid) {
6-
return {
7-
pass: false,
8-
message: () => "Result should be invalid but had no errors",
9-
};
10-
} else {
11-
return {
12-
pass: true,
13-
message: /* istanbul ignore next */ () => "Result should not contain error",
14-
};
4+
function createMatcher(): MaybeAsyncCallback<Report, []> {
5+
function toBeInvalid(report: Report): MatcherResult {
6+
if (report.valid) {
7+
return {
8+
pass: false,
9+
message: () => "Result should be invalid but had no errors",
10+
};
11+
} else {
12+
return {
13+
pass: true,
14+
message: /* istanbul ignore next */ () => "Result should not contain error",
15+
};
16+
}
1517
}
18+
return diverge(toBeInvalid);
1619
}
1720

18-
export default diverge(toBeInvalid);
21+
export { createMatcher as toBeInvalid };

src/jest/matchers/to-be-valid.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { type Report } from "../../reporter";
2-
import { type MatcherResult, diverge } from "../utils";
2+
import { type MatcherResult, type MaybeAsyncCallback, diverge } from "../utils";
33

4-
function toBeValid(report: Report): MatcherResult {
5-
if (report.valid) {
6-
return {
7-
pass: true,
8-
message: /* istanbul ignore next */ () => "Result should not contain error",
9-
};
10-
} else {
11-
const firstError = report.results[0].messages[0];
12-
return {
13-
pass: false,
14-
message: () => `Result should be valid but had error "${firstError.message}"`,
15-
};
4+
function createMatcher(): MaybeAsyncCallback<Report, []> {
5+
function toBeValid(report: Report): MatcherResult {
6+
if (report.valid) {
7+
return {
8+
pass: true,
9+
message: /* istanbul ignore next */ () => "Result should not contain error",
10+
};
11+
} else {
12+
const firstError = report.results[0].messages[0];
13+
return {
14+
pass: false,
15+
message: () => `Result should be valid but had error "${firstError.message}"`,
16+
};
17+
}
1618
}
19+
return diverge(toBeValid);
1720
}
1821

19-
export default diverge(toBeValid);
22+
export { createMatcher as toBeValid };

src/jest/matchers/to-have-error.ts

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import { type Message, type Report } from "../../reporter";
2-
import { type MatcherContext, type MatcherResult, diff, diverge } from "../utils";
2+
import {
3+
type MatcherContext,
4+
type MatcherExpect,
5+
type MatcherResult,
6+
type MaybeAsyncCallback,
7+
diff,
8+
diverge,
9+
} from "../utils";
310
import { flattenMessages } from "../utils/flatten-messages";
411

512
function toHaveErrorImpl(
6-
jest: MatcherContext,
13+
context: MatcherContext,
14+
expect: MatcherExpect,
715
actual: Report,
816
expected: Partial<Message>
917
): MatcherResult {
1018
const flattened = flattenMessages(actual);
1119
const matcher = [expect.objectContaining(expected)];
12-
const pass = jest.equals(flattened, matcher);
13-
const diffString = diff(matcher, flattened, { expand: jest.expand });
14-
const hint = jest.utils.matcherHint(".toHaveError");
15-
const prettyExpected = jest.utils.printExpected(matcher);
16-
const prettyReceived = jest.utils.printReceived(flattened);
20+
const pass = context.equals(flattened, matcher);
21+
const diffString = diff(matcher, flattened, { expand: context.expand });
22+
const hint = context.utils.matcherHint(".toHaveError");
23+
const prettyExpected = context.utils.printExpected(matcher);
24+
const prettyReceived = context.utils.printReceived(flattened);
1725
const resultMessage = (): string => {
1826
return [
1927
hint,
@@ -28,34 +36,45 @@ function toHaveErrorImpl(
2836
return { pass, message: resultMessage };
2937
}
3038

31-
function toHaveError(this: MatcherContext, actual: Report, error: Partial<Message>): MatcherResult;
32-
function toHaveError(
33-
this: MatcherContext,
34-
actual: Report,
35-
ruleId: string,
36-
message: string,
37-
context?: any
38-
): MatcherResult;
39-
function toHaveError(
40-
this: MatcherContext,
41-
actual: Report,
42-
arg1: string | Partial<Message>,
43-
arg2?: string,
44-
arg3?: any
45-
): MatcherResult {
46-
if (typeof arg1 === "string") {
47-
const expected: Partial<Message> = {
48-
ruleId: arg1,
49-
message: arg2,
50-
};
51-
if (arg3) {
52-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- this is supposed to accept anything */
53-
expected.context = arg3;
39+
function createMatcher(
40+
expect: MatcherExpect
41+
):
42+
| MaybeAsyncCallback<Report, [Partial<Message>]>
43+
| MaybeAsyncCallback<Report, [string, string, any?]> {
44+
function toHaveError(
45+
this: MatcherContext,
46+
actual: Report,
47+
error: Partial<Message>
48+
): MatcherResult;
49+
function toHaveError(
50+
this: MatcherContext,
51+
actual: Report,
52+
ruleId: string,
53+
message: string,
54+
context?: any
55+
): MatcherResult;
56+
function toHaveError(
57+
this: MatcherContext,
58+
actual: Report,
59+
arg1: string | Partial<Message>,
60+
arg2?: string,
61+
arg3?: any
62+
): MatcherResult {
63+
if (typeof arg1 === "string") {
64+
const expected: Partial<Message> = {
65+
ruleId: arg1,
66+
message: arg2,
67+
};
68+
if (arg3) {
69+
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- this is supposed to accept anything */
70+
expected.context = arg3;
71+
}
72+
return toHaveErrorImpl(this, expect, actual, expected);
73+
} else {
74+
return toHaveErrorImpl(this, expect, actual, arg1);
5475
}
55-
return toHaveErrorImpl(this, actual, expected);
56-
} else {
57-
return toHaveErrorImpl(this, actual, arg1);
5876
}
77+
return diverge(toHaveError);
5978
}
6079

61-
export default diverge(toHaveError);
80+
export { createMatcher as toHaveError };

src/jest/matchers/to-have-errors.ts

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,47 @@
11
/* eslint-disable prefer-template -- technical debt, should be refactored */
22

33
import { type Report } from "../../reporter";
4-
import { type MatcherContext, type MatcherResult, diff, diverge, flattenMessages } from "../utils";
4+
import {
5+
type MatcherContext,
6+
type MatcherExpect,
7+
type MatcherResult,
8+
type MaybeAsyncCallback,
9+
diff,
10+
diverge,
11+
flattenMessages,
12+
} from "../utils";
513

6-
function toHaveErrors(
7-
this: MatcherContext,
8-
report: Report,
9-
errors: Array<[string, string] | Record<string, unknown>>
10-
): MatcherResult {
11-
const flattened = flattenMessages(report);
12-
const matcher = errors.map((entry) => {
13-
if (Array.isArray(entry)) {
14-
const [ruleId, message] = entry;
15-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-return -- jest type is declared like this */
16-
return expect.objectContaining({ ruleId, message });
17-
} else {
18-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-return -- jest type is declared like this */
19-
return expect.objectContaining(entry);
20-
}
21-
});
22-
const pass = this.equals(flattened, matcher);
23-
const diffString = diff(matcher, flattened, { expand: this.expand });
24-
const resultMessage = (): string =>
25-
this.utils.matcherHint(".toHaveErrors") +
26-
"\n\n" +
27-
"Expected error to equal:\n" +
28-
` ${this.utils.printExpected(matcher)}\n` +
29-
"Received:\n" +
30-
` ${this.utils.printReceived(flattened)}` +
31-
/* istanbul ignore next */ (diffString ? `\n\nDifference:\n\n${diffString}` : "");
14+
function createMatcher(
15+
expect: MatcherExpect
16+
): MaybeAsyncCallback<Report, [Array<[string, string] | Record<string, unknown>>]> {
17+
function toHaveErrors(
18+
this: MatcherContext,
19+
report: Report,
20+
errors: Array<[string, string] | Record<string, unknown>>
21+
): MatcherResult {
22+
const flattened = flattenMessages(report);
23+
const matcher = errors.map((entry) => {
24+
if (Array.isArray(entry)) {
25+
const [ruleId, message] = entry;
26+
return expect.objectContaining({ ruleId, message });
27+
} else {
28+
return expect.objectContaining(entry);
29+
}
30+
});
31+
const pass = this.equals(flattened, matcher);
32+
const diffString = diff(matcher, flattened, { expand: this.expand });
33+
const resultMessage = (): string =>
34+
this.utils.matcherHint(".toHaveErrors") +
35+
"\n\n" +
36+
"Expected error to equal:\n" +
37+
` ${this.utils.printExpected(matcher)}\n` +
38+
"Received:\n" +
39+
` ${this.utils.printReceived(flattened)}` +
40+
/* istanbul ignore next */ (diffString ? `\n\nDifference:\n\n${diffString}` : "");
3241

33-
return { pass, message: resultMessage };
42+
return { pass, message: resultMessage };
43+
}
44+
return diverge(toHaveErrors);
3445
}
3546

36-
export default diverge(toHaveErrors);
47+
export { createMatcher as toHaveErrors };

0 commit comments

Comments
 (0)