From d9b495ba74658259487701d9c6c7245f67c5b541 Mon Sep 17 00:00:00 2001 From: TomokiMiyauci Date: Tue, 30 Nov 2021 22:09:50 +0900 Subject: [PATCH] feat(expect): add `anything` function --- dev_deps.ts | 1 + expect/README.md | 21 ++++++++----- expect/anything.ts | 22 ++++++++++++++ expect/anything_test.ts | 25 +++++++++++++++ expect/mod.ts | 6 ++-- helper/equal.ts | 38 +++++++++++++++++++++++ helper/equal_test.ts | 67 +++++++++++++++++++++++++++++++++++++++++ mod.ts | 6 +++- 8 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 expect/anything.ts create mode 100644 expect/anything_test.ts create mode 100644 helper/equal.ts create mode 100644 helper/equal_test.ts diff --git a/dev_deps.ts b/dev_deps.ts index 9ab56a1..ea1f9ef 100644 --- a/dev_deps.ts +++ b/dev_deps.ts @@ -3,6 +3,7 @@ import { assertThrows } from "https://deno.land/std@0.115.1/testing/asserts.ts"; export { assert, + assertExists, assertRejects, assertThrows, } from "https://deno.land/std@0.115.1/testing/asserts.ts"; diff --git a/expect/README.md b/expect/README.md index 9cf50e8..d4d9cb4 100644 --- a/expect/README.md +++ b/expect/README.md @@ -85,13 +85,18 @@ expect(10).toBeGreaterThan(3); This allows TypeScript to do some of the assertions for you. -## Differences from jest +## anything -Unitest prefers to be loosely coupled, simple and low on code. +`anything()` matches anything but `null` or `undefined`. -It does not implement some of the things implemented in jest, and provides other -ways. - -| jest | unitest | -| ----------------------------------------------------------------- | -------------- | -| [expect.anything()](https://jestjs.io/docs/expect#expectanything) | toBeAnything() | +```ts +import { + anything, + expect, + test, +} from "https://deno.land/x/unitest@$VERSION/mod.ts"; + +test("should not be null or undefined", () => { + expect(actual).toEqual(anything()); +}); +``` diff --git a/expect/anything.ts b/expect/anything.ts new file mode 100644 index 0000000..af416ea --- /dev/null +++ b/expect/anything.ts @@ -0,0 +1,22 @@ +// Copyright 2021-Present the Unitest authors. All rights reserved. MIT license. +import { equality } from "../helper/equal.ts"; +import { isNil } from "../deps.ts"; +import type { Equality } from "../helper/equal.ts"; + +/** equal to anything */ +class Anything implements Equality { + [equality](actual: unknown): boolean { + return !isNil(actual); + } + + toString(): string { + return "except undefined or null"; + } +} + +/** make Anything */ +function anything(): Anything { + return new Anything(); +} + +export { Anything, anything }; diff --git a/expect/anything_test.ts b/expect/anything_test.ts new file mode 100644 index 0000000..9691c54 --- /dev/null +++ b/expect/anything_test.ts @@ -0,0 +1,25 @@ +// Copyright 2021-Present the Unitest authors. All rights reserved. MIT license. +import { Anything, anything } from "./anything.ts"; +import { equality } from "../helper/equal.ts"; +import { assertEquals, assertExists } from "../dev_deps.ts"; + +Deno.test({ + name: "Anything", + fn: () => { + const anything = new Anything(); + assertExists(anything.toString); + assertExists(anything[equality]); + + assertEquals(anything.toString(), "except undefined or null"); + assertEquals(anything[equality](undefined), false); + assertEquals(anything[equality](null), false); + assertEquals(anything[equality]({}), true); + }, +}); + +Deno.test({ + name: "anything", + fn: () => { + assertEquals(anything(), new Anything()); + }, +}); diff --git a/expect/mod.ts b/expect/mod.ts index 53bfa19..a72e7be 100644 --- a/expect/mod.ts +++ b/expect/mod.ts @@ -5,6 +5,7 @@ import { jestMatcherMap } from "../matcher/preset.ts"; import { jestModifierMap } from "../modifier/preset.ts"; import { AssertionError, isPromise } from "../deps.ts"; import { stringify, stringifyResult } from "../helper/format.ts"; +import { anything } from "./anything.ts"; import type { AnyFn, @@ -25,6 +26,7 @@ import { expectTo, promiseExpectTo, } from "./_utils.ts"; +import { stringifyEquality } from "../helper/equal.ts"; import type { ModifierMap } from "../modifier/types.ts"; import type { MatcherMap } from "../matcher/types.ts"; import type { StringifyResultArgs } from "../helper/format.ts"; @@ -108,7 +110,7 @@ function defineExpect< matcherArgs, matcher: String(name), actualValue, - expectedValue, + expectedValue: stringifyEquality(expectedValue), expectedHint, actualHint, preModifier: pre?.[0], @@ -159,5 +161,5 @@ function expect(actual: T) { })(actual); } -export { defineExpect, expect }; +export { anything, defineExpect, expect }; export type { Expected, MatcherMap }; diff --git a/helper/equal.ts b/helper/equal.ts new file mode 100644 index 0000000..8b1a043 --- /dev/null +++ b/helper/equal.ts @@ -0,0 +1,38 @@ +// Copyright 2021-Present the Unitest authors. All rights reserved. MIT license. +import { equal as eq, isObject } from "../deps.ts"; + +/** Symbol for equality */ +const equality = Symbol.for("equality"); + +interface Equality { + [equality]: (actual: unknown) => boolean; + toString: () => string; +} + +/** Whatever argument is `Equality` or not */ +function isEquality(value: object): value is Equality { + return equality in value; +} + +/** safe stringify for `Equality` */ +function stringifyEquality(value: T): T | string { + if (isObject(value) && isEquality(value)) { + return value.toString(); + } + + return value; +} + +/** Deep equality comparison used in assertions */ +function equal( + a: unknown, + b: unknown, +): boolean { + if (isObject(b) && isEquality(b)) { + return b[equality](a); + } + return eq(a, b); +} + +export { equal, equality, isEquality, stringifyEquality }; +export type { Equality }; diff --git a/helper/equal_test.ts b/helper/equal_test.ts new file mode 100644 index 0000000..81dccfa --- /dev/null +++ b/helper/equal_test.ts @@ -0,0 +1,67 @@ +// Copyright 2021-Present the Unitest authors. All rights reserved. MIT license. +import { equal, equality, isEquality, stringifyEquality } from "./equal.ts"; +import { anything } from "../expect/anything.ts"; +import { fn } from "../mock/fn.ts"; +import { assertEquals } from "../dev_deps.ts"; + +Deno.test("equality", () => assertEquals(equality, Symbol.for("equality"))); + +Deno.test("isEquality", () => { + const table: [ + ...Parameters, + ReturnType, + ][] = [ + [{}, false], + [[], false], + [{ + [equality]: true, + }, true], + [{ + [equality]: () => true, + }, true], + ]; + + table.forEach(([value, result]) => assertEquals(isEquality(value), result)); +}); + +Deno.test("equal", () => { + const table: [ + ...Parameters, + ReturnType, + ][] = [ + [null, anything(), false], + [{}, anything(), true], + [{}, {}, true], + ]; + + table.forEach(([a, b, result]) => assertEquals(equal(a, b), result)); + + const mock = fn(); + equal({}, { + [equality]: mock, + }); + + assertEquals(mock.mock.calls.length, 1); +}); + +Deno.test("stringifyEquality", () => { + const table: [ + ...Parameters, + ReturnType, + ][] = [ + [false, false], + [[], []], + [{ + [equality]: true, + toString: () => "equal", + }, "equal"], + [{ + [equality]: true, + toString: () => "any", + }, "any"], + ]; + + table.forEach(([value, result]) => + assertEquals(stringifyEquality(value), result) + ); +}); diff --git a/mod.ts b/mod.ts index 45d1e1a..19a1bc5 100644 --- a/mod.ts +++ b/mod.ts @@ -94,10 +94,14 @@ export * from "./matcher/utils.ts"; export * from "./mock/fn.ts"; export * from "./mock/types.ts"; -export * from "./expect/mod.ts"; +export { defineExpect, expect } from "./expect/mod.ts"; +export type { Expected, MatcherMap } from "./expect/mod.ts"; +export { anything } from "./expect/anything.ts"; export { not } from "./modifier/not.ts"; export { rejects } from "./modifier/rejects.ts"; export { resolves } from "./modifier/resolves.ts"; export * from "./modifier/preset.ts"; export * from "./test/mod.ts"; + +export { equal, equality } from "./helper/equal.ts";