Commit 0ede9f74 authored by David Sveningsson's avatar David Sveningsson
Browse files

fix(jest): synchronous matchers as long as the passed value is synchronous

parent c85ce187
Pipeline #365493303 passed with stages
in 11 minutes and 24 seconds
......@@ -57,9 +57,9 @@ beforeEach(() => {
});
describe("toBeValid()", () => {
it("should pass if report is valid", async () => {
it("should pass if report is valid", () => {
expect.assertions(1);
await expect(reportOk).toBeValid();
expect(reportOk).toBeValid();
});
it("should pass if async report is valid", async () => {
......@@ -67,11 +67,11 @@ describe("toBeValid()", () => {
await expect(Promise.resolve(reportOk)).toBeValid();
});
it("should fail if report is invalid", async () => {
it("should fail if report is invalid", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect(reportError).toBeValid();
expect(reportError).toBeValid();
} catch (e) {
error = e;
}
......@@ -93,9 +93,9 @@ describe("toBeValid()", () => {
});
describe("toBeInvalid()", () => {
it("should pass if report is invalid", async () => {
it("should pass if report is invalid", () => {
expect.assertions(1);
await expect(reportError).toBeInvalid();
expect(reportError).toBeInvalid();
});
it("should pass if async report is invalid", async () => {
......@@ -103,11 +103,11 @@ describe("toBeInvalid()", () => {
await expect(Promise.resolve(reportError)).toBeInvalid();
});
it("should fail if report is valid", async () => {
it("should fail if report is valid", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect(reportOk).toBeInvalid();
expect(reportOk).toBeInvalid();
} catch (e) {
error = e;
}
......@@ -129,9 +129,9 @@ describe("toBeInvalid()", () => {
});
describe("toHaveError()", () => {
it("should pass if error is preset", async () => {
it("should pass if error is preset", () => {
expect.assertions(1);
await expect(reportError).toHaveError("my-rule", "mock message");
expect(reportError).toHaveError("my-rule", "mock message");
});
it("should pass if async error is preset", async () => {
......@@ -139,18 +139,18 @@ describe("toHaveError()", () => {
await expect(Promise.resolve(reportError)).toHaveError("my-rule", "mock message");
});
it("should pass if error have matching context", async () => {
it("should pass if error have matching context", () => {
expect.assertions(1);
await expect(reportError).toHaveError("my-rule", "mock message", {
expect(reportError).toHaveError("my-rule", "mock message", {
foo: "bar",
});
});
it("should fail if expected error is missing", async () => {
it("should fail if expected error is missing", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect(reportError).toHaveError("asdf", "asdf");
expect(reportError).toHaveError("asdf", "asdf");
} catch (e) {
error = e;
}
......@@ -186,9 +186,9 @@ describe("toHaveError()", () => {
});
describe("toHaveErrors()", () => {
it("should pass if error is preset", async () => {
it("should pass if error is preset", () => {
expect.assertions(1);
await expect(reportError).toHaveErrors([["my-rule", "mock message"]]);
expect(reportError).toHaveErrors([["my-rule", "mock message"]]);
});
it("should pass if async error is preset", async () => {
......@@ -196,26 +196,26 @@ describe("toHaveErrors()", () => {
await expect(Promise.resolve(reportError)).toHaveErrors([["my-rule", "mock message"]]);
});
it("should pass if error have matching context", async () => {
it("should pass if error have matching context", () => {
expect.assertions(1);
await expect(reportError).toHaveErrors([
expect(reportError).toHaveErrors([
{ ruleId: "my-rule", message: "mock message", context: { foo: "bar" } },
]);
});
it("should pass if all errors are preset", async () => {
it("should pass if all errors are preset", () => {
expect.assertions(1);
await expect(reportMultipleErrors).toHaveErrors([
expect(reportMultipleErrors).toHaveErrors([
["my-rule", "mock message"],
["another-rule", "another message"],
]);
});
it("should fail if error any missing", async () => {
it("should fail if error any missing", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect(reportMultipleErrors).toHaveErrors([
expect(reportMultipleErrors).toHaveErrors([
["my-rule", "mock message"],
["spam", "spam"],
]);
......@@ -240,18 +240,18 @@ describe("toBeToken()", () => {
},
};
it("should pass if token matches", async () => {
it("should pass if token matches", () => {
expect.assertions(1);
await expect({ value: token }).toBeToken({
expect({ value: token }).toBeToken({
type: TokenType.TAG_OPEN,
});
});
it("should fail if token doesn't match", async () => {
it("should fail if token doesn't match", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect({ value: token }).toBeToken({
expect({ value: token }).toBeToken({
type: TokenType.TAG_CLOSE,
});
} catch (e) {
......@@ -273,11 +273,11 @@ describe("toHTMLValidate()", () => {
expect("<p></i>").not.toHTMLValidate();
});
it("should fail if markup is invalid", async () => {
it("should fail if markup is invalid", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect("<a><button></i>").toHTMLValidate();
expect("<a><button></i>").toHTMLValidate();
} catch (e) {
error = e;
}
......@@ -294,11 +294,11 @@ describe("toHTMLValidate()", () => {
`);
});
it("should fail if markup is valid but negated", async () => {
it("should fail if markup is valid but negated", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect("<p></p>").not.toHTMLValidate();
expect("<p></p>").not.toHTMLValidate();
} catch (e) {
error = e;
}
......@@ -392,11 +392,11 @@ describe("toHTMLValidate()", () => {
});
});
it("should fail if markup has wrong error", async () => {
it("should fail if markup has wrong error", () => {
expect.assertions(3);
let error: Error | undefined = undefined;
try {
await expect("<u></i>").not.toHTMLValidate({
expect("<u></i>").not.toHTMLValidate({
ruleId: "wrong-error",
message: expect.stringContaining("Some other error"),
});
......
......@@ -2,12 +2,12 @@ import { ConfigData } from "../../config";
import { Location } from "../../context";
import { Message } from "../../reporter";
import { TokenType } from "../../lexer";
import { toBeValid } from "./to-be-valid";
import { toBeInvalid } from "./to-be-invalid";
import { toBeToken } from "./to-be-token";
import { toHTMLValidate } from "./to-htmlvalidate";
import { toHaveError } from "./to-have-error";
import { toHaveErrors } from "./to-have-errors";
import toBeValid from "./to-be-valid";
import toBeInvalid from "./to-be-invalid";
import toBeToken from "./to-be-token";
import toHTMLValidate from "./to-htmlvalidate";
import toHaveError from "./to-have-error";
import toHaveErrors from "./to-have-errors";
interface TokenMatcher {
type: TokenType;
......
import { Report } from "../../reporter";
import { diverge } from "../utils";
export async function toBeInvalid(
this: jest.MatcherUtils,
actual: Report | Promise<Report>
): Promise<jest.CustomMatcherResult> {
const report = await actual;
function toBeInvalid(report: Report): jest.CustomMatcherResult {
if (report.valid) {
return {
pass: false,
......@@ -17,3 +14,5 @@ export async function toBeInvalid(
};
}
}
export default diverge(toBeInvalid);
/* eslint-disable @typescript-eslint/explicit-module-boundary-types, prefer-template */
import { TokenType } from "../../lexer";
import { diff } from "../utils";
import { diff, diverge } from "../utils";
export function toBeToken(
this: jest.MatcherUtils,
actual: any,
expected: any
): jest.CustomMatcherResult {
function toBeToken(this: jest.MatcherUtils, actual: any, expected: any): jest.CustomMatcherResult {
const token = actual.value;
// istanbul ignore next: TokenMatcher requires "type" property to be set, this is just a failsafe
......@@ -34,3 +30,5 @@ export function toBeToken(
return { pass, message };
}
export default diverge(toBeToken);
import { Report } from "../../reporter";
import { diverge } from "../utils";
export async function toBeValid(
this: jest.MatcherUtils,
actual: Report | Promise<Report>
): Promise<jest.CustomMatcherResult> {
const report = await actual;
function toBeValid(report: Report): jest.CustomMatcherResult {
if (report.valid) {
return {
pass: true,
......@@ -18,3 +15,5 @@ export async function toBeValid(
};
}
}
export default diverge(toBeValid);
/* eslint-disable prefer-template */
import { Report } from "../../reporter";
import { diff } from "../utils/diff";
import { diff, diverge } from "../utils";
import { flattenMessages } from "../utils/flatten-messages";
export async function toHaveError(
function toHaveError(
this: jest.MatcherUtils,
actual: Report | Promise<Report>,
actual: Report,
ruleId: string,
message: string,
context?: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
): Promise<jest.CustomMatcherResult> {
const report = await actual;
const flattened = flattenMessages(report);
): jest.CustomMatcherResult {
const flattened = flattenMessages(actual);
const expected: any = { ruleId, message };
if (context) {
expected.context = context;
......@@ -32,3 +31,5 @@ export async function toHaveError(
return { pass, message: resultMessage };
}
export default diverge(toHaveError);
/* eslint-disable prefer-template */
import { Report } from "../../reporter";
import { diff, flattenMessages } from "../utils";
import { diff, diverge, flattenMessages } from "../utils";
export async function toHaveErrors(
function toHaveErrors(
this: jest.MatcherUtils,
actual: Report | Promise<Report>,
report: Report,
errors: Array<[string, string] | Record<string, unknown>>
): Promise<jest.CustomMatcherResult> {
const report = await actual;
): jest.CustomMatcherResult {
const flattened = flattenMessages(report);
const matcher = errors.map((entry) => {
if (Array.isArray(entry)) {
......@@ -31,3 +30,5 @@ export async function toHaveErrors(
return { pass, message: resultMessage };
}
export default diverge(toHaveErrors);
......@@ -4,7 +4,7 @@ import deepmerge from "deepmerge";
import { ConfigData } from "../../config";
import HtmlValidate from "../../htmlvalidate";
import { Message } from "../../reporter";
import { diff } from "../utils";
import { diff, diverge } from "../utils";
function isMessage(arg: any): arg is Partial<Message> {
return (
......@@ -44,7 +44,7 @@ function getMarkup(src: unknown): string {
}
}
export function toHTMLValidate(
function toHTMLValidate(
this: jest.MatcherUtils,
actual: unknown,
arg0?: Partial<Message> | ConfigData | string,
......@@ -113,3 +113,5 @@ function toHTMLValidateImpl(
};
}
}
export default diverge(toHTMLValidate);
import { isThenable } from "./is-thenable";
type Utils = jest.MatcherUtils;
type Result = jest.CustomMatcherResult;
type SyncCallback<T, TArgs extends any[]> = (this: Utils, actual: T, ...args: TArgs) => Result;
export interface MaybeAsyncCallback<T, TArgs extends any[]> {
(this: Utils, actual: T, ...args: TArgs): Result;
(this: Utils, actual: Promise<T>, ...args: TArgs): Promise<Result>;
}
/**
* Creates a wrapped function based on the passed function.
*
* The returned function takes either a `T` or `Promise<T>`. If `T` the result
* will be synchronous or if `Promise<T>` the result will be asynchronous.
*
* In practice this means that if you pass a synchronous object into it you will
* maintain synchronous code but if you pass an asynchronous object you must
* await the result.
*/
export function diverge<T, TArgs extends any[]>(
fn: SyncCallback<T, TArgs>
): MaybeAsyncCallback<T, TArgs> {
function diverged(this: Utils, actual: T, ...args: TArgs): Result;
function diverged(this: Utils, actual: Promise<T>, ...args: TArgs): Promise<Result>;
function diverged(this: Utils, actual: T | Promise<T>, ...args: TArgs): Result | Promise<Result> {
if (isThenable(actual)) {
return actual.then((resolved) => fn.call(this, resolved, ...args));
} else {
return fn.call(this, actual, ...args);
}
}
return diverged;
}
export { diff } from "./diff";
export { diverge } from "./diverge";
export { flattenMessages } from "./flatten-messages";
export { isThenable } from "./is-thenable";
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment