mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
fix(expect): improve expect
type to make it work better with expect.extend
(#5309)
This commit is contained in:
parent
63912921b3
commit
83fe1f754f
@ -1,7 +1,7 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { expect } from "./expect.ts";
|
||||
import type { MatcherContext, Tester } from "./_types.ts";
|
||||
import type { Async, Expected, MatcherContext, Tester } from "./_types.ts";
|
||||
import { AssertionError, assertThrows } from "@std/assert";
|
||||
|
||||
declare module "./_types.ts" {
|
||||
@ -110,3 +110,51 @@ Deno.test("expect.extend() api test case", () => {
|
||||
"Expected Book object: Book 1. Actual Book object: Book 1",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("expect.extend() example is valid", async () => {
|
||||
// Extends the `Expected` interface with your new matchers signatures
|
||||
interface ExtendedExpected<IsAsync = false> extends Expected<IsAsync> {
|
||||
// Matcher that asserts value is a dinosaur
|
||||
toBeDinosaur: (options?: { includeTrexs?: boolean }) => unknown;
|
||||
|
||||
// NOTE: You also need to overrides the following typings to allow modifiers to correctly infer typing
|
||||
not: IsAsync extends true ? Async<ExtendedExpected<true>>
|
||||
: ExtendedExpected<false>;
|
||||
resolves: Async<ExtendedExpected<true>>;
|
||||
rejects: Async<ExtendedExpected<true>>;
|
||||
}
|
||||
|
||||
// Call `expect.extend()` with your new matchers definitions
|
||||
expect.extend({
|
||||
toBeDinosaur(context, options) {
|
||||
const dino = `${context.value}`;
|
||||
const allowed = ["🦕"];
|
||||
if (options?.includeTrexs) {
|
||||
allowed.push("🦖");
|
||||
}
|
||||
const pass = allowed.includes(dino);
|
||||
if (context.isNot) {
|
||||
// Note: when `context.isNot` is set, the test is considered successful when `pass` is false
|
||||
return {
|
||||
message: () => `Expected "${dino}" to NOT be a dinosaur`,
|
||||
pass,
|
||||
};
|
||||
}
|
||||
return { message: () => `Expected "${dino}" to be a dinosaur`, pass };
|
||||
},
|
||||
});
|
||||
|
||||
// Alias expect to avoid having to pass the generic typing argument each time
|
||||
// This is probably what you want to export and reuse across your tests
|
||||
const myexpect = expect<ExtendedExpected>;
|
||||
|
||||
// Perform some tests
|
||||
myexpect("🦕").toBeDinosaur();
|
||||
myexpect("🦧").not.toBeDinosaur();
|
||||
await myexpect(Promise.resolve("🦕")).resolves.toBeDinosaur();
|
||||
await myexpect(Promise.resolve("🦧")).resolves.not.toBeDinosaur();
|
||||
|
||||
// Regular matchers will still be available
|
||||
myexpect("foo").not.toBeNull();
|
||||
myexpect.anything;
|
||||
});
|
||||
|
@ -137,12 +137,17 @@ const matchers: Record<MatcherKey, Matcher> = {
|
||||
* @param customMessage - An optional custom message to include in the assertion error.
|
||||
* @returns An expected object that can be used to chain matchers.
|
||||
*
|
||||
* @typeParam T The interface used for `expect`. This is usually needed only if you want to use `expect.extend` to create custom matchers.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export function expect(value: unknown, customMessage?: string): Expected {
|
||||
export function expect<T extends Expected = Expected>(
|
||||
value: unknown,
|
||||
customMessage?: string,
|
||||
): T {
|
||||
let isNot = false;
|
||||
let isPromised = false;
|
||||
const self: Expected = new Proxy<Expected>(<Expected> {}, {
|
||||
const self: T = new Proxy<T>(<T> {}, {
|
||||
get(_, name) {
|
||||
if (name === "not") {
|
||||
isNot = !isNot;
|
||||
@ -287,7 +292,60 @@ expect.addEqualityTesters = addCustomEqualityTesters as (
|
||||
newTesters: Tester[],
|
||||
) => void;
|
||||
/**
|
||||
* TODO: export appropriate types to define custom matchers.
|
||||
* Extend `expect()` with custom provided matchers.
|
||||
*
|
||||
* To do so, you will need to extend the interface `Expected` to define the new signature of the `expect`.
|
||||
*
|
||||
* ```ts
|
||||
* import type { Async, Expected } from "./expect.ts";
|
||||
* import { expect } from "./expect.ts";
|
||||
*
|
||||
* // Extends the `Expected` interface with your new matchers signatures
|
||||
* interface ExtendedExpected<IsAsync = false> extends Expected<IsAsync> {
|
||||
* // Matcher that asserts value is a dinosaur
|
||||
* toBeDinosaur: (options?: { includeTrexs?: boolean }) => unknown;
|
||||
*
|
||||
* // NOTE: You also need to overrides the following typings to allow modifiers to correctly infer typing
|
||||
* not: IsAsync extends true ? Async<ExtendedExpected<true>>
|
||||
* : ExtendedExpected<false>;
|
||||
* resolves: Async<ExtendedExpected<true>>;
|
||||
* rejects: Async<ExtendedExpected<true>>;
|
||||
* }
|
||||
*
|
||||
* // Call `expect.extend()` with your new matchers definitions
|
||||
* expect.extend({
|
||||
* toBeDinosaur(context, options) {
|
||||
* const dino = `${context.value}`;
|
||||
* const allowed = ["🦕"];
|
||||
* if (options?.includeTrexs) {
|
||||
* allowed.push("🦖");
|
||||
* }
|
||||
* const pass = allowed.includes(dino);
|
||||
* if (context.isNot) {
|
||||
* // Note: when `context.isNot` is set, the test is considered successful when `pass` is false
|
||||
* return {
|
||||
* message: () => `Expected "${dino}" to NOT be a dinosaur`,
|
||||
* pass,
|
||||
* };
|
||||
* }
|
||||
* return { message: () => `Expected "${dino}" to be a dinosaur`, pass };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* // Alias expect to avoid having to pass the generic typing argument each time
|
||||
* // This is probably what you want to export and reuse across your tests
|
||||
* const myexpect = expect<ExtendedExpected>;
|
||||
*
|
||||
* // Perform some tests
|
||||
* myexpect("🦕").toBeDinosaur();
|
||||
* myexpect("🦧").not.toBeDinosaur();
|
||||
* await myexpect(Promise.resolve("🦕")).resolves.toBeDinosaur();
|
||||
* await myexpect(Promise.resolve("🦧")).resolves.not.toBeDinosaur();
|
||||
*
|
||||
* // Regular matchers will still be available
|
||||
* myexpect("foo").not.toBeNull()
|
||||
* myexpect.anything
|
||||
* ```
|
||||
*/
|
||||
expect.extend = setExtendMatchers as (newExtendMatchers: Matchers) => void;
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user