diff --git a/expect/_array_containing_test.ts b/expect/_array_containing_test.ts new file mode 100644 index 0000000000000..886e323bee254 --- /dev/null +++ b/expect/_array_containing_test.ts @@ -0,0 +1,14 @@ +import { expect } from "./expect.ts"; + +Deno.test("expect.arrayContaining() with array of numbers", () => { + const arr = [1, 2, 3]; + expect([1, 2, 3, 4]).toEqual(expect.arrayContaining(arr)); + expect([4, 5, 6]).not.toEqual(expect.arrayContaining(arr)); + expect([1, 2, 3]).toEqual(expect.arrayContaining(arr)); +}); + +Deno.test("expect.arrayContaining() with array of mixed types", () => { + const arr = [1, 2, "hello"]; + expect([1, 2, 3, "hello", "bye"]).toEqual(expect.arrayContaining(arr)); + expect([4, "bye"]).not.toEqual(expect.arrayContaining(arr)); +}); diff --git a/expect/_asymmetric_matchers.ts b/expect/_asymmetric_matchers.ts index ddac62c84ff5f..9f93aaffcaadb 100644 --- a/expect/_asymmetric_matchers.ts +++ b/expect/_asymmetric_matchers.ts @@ -1,3 +1,6 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + abstract class AsymmetricMatcher { constructor( protected value: T, @@ -61,3 +64,16 @@ export class Any extends AsymmetricMatcher { export function any(c: unknown): Any { return new Any(c); } + +export class ArrayContaining extends AsymmetricMatcher { + constructor(arr: any[]) { + super(arr); + } + equals(other: any[]): boolean { + return this.value.every((e) => other.includes(e)); + } +} + +export function arrayContaining(c: any[]): ArrayContaining { + return new ArrayContaining(c); +} diff --git a/expect/_equal.ts b/expect/_equal.ts index 58d1f9b70c6e8..e648a7d46151b 100644 --- a/expect/_equal.ts +++ b/expect/_equal.ts @@ -3,7 +3,7 @@ // This file is copied from `std/assert`. import { EqualOptions } from "./_types.ts"; -import { Any, Anything } from "./_asymmetric_matchers.ts"; +import { Any, Anything, ArrayContaining } from "./_asymmetric_matchers.ts"; function isKeyedCollection(x: unknown): x is Set { return [Symbol.iterator, "size"].every((k) => k in (x as Set)); @@ -59,6 +59,9 @@ export function equal(c: unknown, d: unknown, options?: EqualOptions): boolean { if (b instanceof Any) { return b.equals(a); } + if (b instanceof ArrayContaining && a instanceof Array) { + return b.equals(a); + } if (a instanceof Date && b instanceof Date) { const aTime = a.getTime(); const bTime = b.getTime(); diff --git a/expect/expect.ts b/expect/expect.ts index 5d3c4707b1225..c5bc4af677258 100644 --- a/expect/expect.ts +++ b/expect/expect.ts @@ -47,7 +47,7 @@ import { toThrow, } from "./_matchers.ts"; import { isPromiseLike } from "./_utils.ts"; -import { any, anything } from "./_asymmetric_matchers.ts"; +import { any, anything, arrayContaining } from "./_asymmetric_matchers.ts"; const matchers: Record = { lastCalledWith: toHaveBeenLastCalledWith, @@ -171,3 +171,4 @@ export function expect(value: unknown, customMessage?: string): Expected { expect.addEqualityTesters = addCustomEqualityTesters; expect.anything = anything; expect.any = any; +expect.arrayContaining = arrayContaining;