diff --git a/docs/api/expect.md b/docs/api/expect.md
index 4edf66659d50f..268a27609c1f4 100644
--- a/docs/api/expect.md
+++ b/docs/api/expect.md
@@ -1405,3 +1405,58 @@ Don't forget to include the ambient declaration file in your `tsconfig.json`.
:::tip
If you want to know more, checkout [guide on extending matchers](/guide/extending-matchers).
:::
+
+## expect.addEqualityTesters 1.2.0+
+
+- **Type:** `(tester: Array) => void`
+
+You can use this method to define custom testers, which are methods used by matchers, to test if two objects are equal. It is compatible with Jest's `expect.addEqualityTesters`.
+
+```ts
+import { expect, test } from 'vitest'
+
+class AnagramComparator {
+ public word: string
+
+ constructor(word: string) {
+ this.word = word
+ }
+
+ equals(other: AnagramComparator): boolean {
+ const cleanStr1 = this.word.replace(/ /g, '').toLowerCase()
+ const cleanStr2 = other.word.replace(/ /g, '').toLowerCase()
+
+ const sortedStr1 = cleanStr1.split('').sort().join('')
+ const sortedStr2 = cleanStr2.split('').sort().join('')
+
+ return sortedStr1 === sortedStr2
+ }
+}
+
+function isAnagramComparator(a: unknown): a is AnagramComparator {
+ return a instanceof AnagramComparator
+}
+
+const areAnagramsEqual: Tester = (
+ a: unknown,
+ b: unknown,
+): boolean | undefined => {
+ const isAAnagramComparator = isAnagramComparator(a)
+ const isBAnagramComparator = isAnagramComparator(b)
+
+ if (isAAnagramComparator && isBAnagramComparator)
+ return a.equals(b)
+
+ else if (isAAnagramComparator === isBAnagramComparator)
+ return undefined
+
+ else
+ return false
+}
+
+expect.addEqualityTesters([areAnagramsEqual])
+
+test('custom equality tester', () => {
+ expect(new AnagramComparator('listen')).toEqual(new AnagramComparator('silent'))
+})
+```
\ No newline at end of file
diff --git a/examples/vitesse/src/auto-import.d.ts b/examples/vitesse/src/auto-import.d.ts
index 9959df651b1b9..f1bf97ee9e432 100644
--- a/examples/vitesse/src/auto-import.d.ts
+++ b/examples/vitesse/src/auto-import.d.ts
@@ -62,4 +62,5 @@ declare global {
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
-}
+ export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+}
\ No newline at end of file
diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts
index b0f3466f831d8..f9d311afb51b3 100644
--- a/packages/expect/src/index.ts
+++ b/packages/expect/src/index.ts
@@ -4,5 +4,6 @@ export * from './constants'
export * from './types'
export { getState, setState } from './state'
export { JestChaiExpect } from './jest-expect'
+export { addCustomEqualityTesters } from './jest-matcher-utils'
export { JestExtend } from './jest-extend'
export { setupColors } from '@vitest/utils'
diff --git a/packages/expect/src/jest-asymmetric-matchers.ts b/packages/expect/src/jest-asymmetric-matchers.ts
index e36b06e5defb6..3879b02af992c 100644
--- a/packages/expect/src/jest-asymmetric-matchers.ts
+++ b/packages/expect/src/jest-asymmetric-matchers.ts
@@ -1,7 +1,7 @@
import type { ChaiPlugin, MatcherState } from './types'
import { GLOBAL_EXPECT } from './constants'
import { getState } from './state'
-import { diff, getMatcherUtils, stringify } from './jest-matcher-utils'
+import { diff, getCustomEqualityTesters, getMatcherUtils, stringify } from './jest-matcher-utils'
import { equals, isA, iterableEquality, pluralize, subsetEquality } from './jest-utils'
@@ -26,7 +26,7 @@ export abstract class AsymmetricMatcher<
...getState(expect || (globalThis as any)[GLOBAL_EXPECT]),
equals,
isNot: this.inverse,
- customTesters: [],
+ customTesters: getCustomEqualityTesters(),
utils: {
...getMatcherUtils(),
diff,
@@ -116,8 +116,9 @@ export class ObjectContaining extends AsymmetricMatcher>
let result = true
+ const matcherContext = this.getMatcherContext()
for (const property in this.sample) {
- if (!this.hasProperty(other, property) || !equals(this.sample[property], other[property])) {
+ if (!this.hasProperty(other, property) || !equals(this.sample[property], other[property], matcherContext.customTesters)) {
result = false
break
}
@@ -149,11 +150,12 @@ export class ArrayContaining extends AsymmetricMatcher> {
)
}
+ const matcherContext = this.getMatcherContext()
const result
= this.sample.length === 0
|| (Array.isArray(other)
&& this.sample.every(item =>
- other.some(another => equals(item, another)),
+ other.some(another => equals(item, another, matcherContext.customTesters)),
))
return this.inverse ? !result : result
diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts
index 0434c37052331..07aedc318e346 100644
--- a/packages/expect/src/jest-expect.ts
+++ b/packages/expect/src/jest-expect.ts
@@ -6,7 +6,7 @@ import type { Test } from '@vitest/runner'
import type { Assertion, ChaiPlugin } from './types'
import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils'
import type { AsymmetricMatcher } from './jest-asymmetric-matchers'
-import { diff, stringify } from './jest-matcher-utils'
+import { diff, getCustomEqualityTesters, stringify } from './jest-matcher-utils'
import { JEST_MATCHERS_OBJECT } from './constants'
import { recordAsyncExpect, wrapSoft } from './utils'
@@ -23,6 +23,7 @@ declare class DOMTokenList {
export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const { AssertionError } = chai
const c = () => getColors()
+ const customTesters = getCustomEqualityTesters()
function def(name: keyof Assertion | (keyof Assertion)[], fn: ((this: Chai.AssertionStatic & Assertion, ...args: any[]) => any)) {
const addMethod = (n: keyof Assertion) => {
@@ -80,7 +81,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const equal = jestEquals(
actual,
expected,
- [iterableEquality],
+ [...customTesters, iterableEquality],
)
return this.assert(
@@ -98,6 +99,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
obj,
expected,
[
+ ...customTesters,
iterableEquality,
typeEquality,
sparseArrayEquality,
@@ -125,6 +127,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
actual,
expected,
[
+ ...customTesters,
iterableEquality,
typeEquality,
sparseArrayEquality,
@@ -140,7 +143,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const toEqualPass = jestEquals(
actual,
expected,
- [iterableEquality],
+ [...customTesters, iterableEquality],
)
if (toEqualPass)
@@ -159,7 +162,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
def('toMatchObject', function (expected) {
const actual = this._obj
return this.assert(
- jestEquals(actual, expected, [iterableEquality, subsetEquality]),
+ jestEquals(actual, expected, [...customTesters, iterableEquality, subsetEquality]),
'expected #{this} to match object #{exp}',
'expected #{this} to not match object #{exp}',
expected,
@@ -208,7 +211,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
def('toContainEqual', function (expected) {
const obj = utils.flag(this, 'object')
const index = Array.from(obj).findIndex((item) => {
- return jestEquals(item, expected)
+ return jestEquals(item, expected, customTesters)
})
this.assert(
@@ -339,7 +342,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
return utils.getPathInfo(actual, propertyName)
}
const { value, exists } = getValue()
- const pass = exists && (args.length === 1 || jestEquals(expected, value))
+ const pass = exists && (args.length === 1 || jestEquals(expected, value, customTesters))
const valueString = args.length === 1 ? '' : ` with value ${utils.objDisplay(expected)}`
diff --git a/packages/expect/src/jest-extend.ts b/packages/expect/src/jest-extend.ts
index fee75069895ea..27c53f843e71a 100644
--- a/packages/expect/src/jest-extend.ts
+++ b/packages/expect/src/jest-extend.ts
@@ -10,7 +10,7 @@ import { ASYMMETRIC_MATCHERS_OBJECT, JEST_MATCHERS_OBJECT } from './constants'
import { AsymmetricMatcher } from './jest-asymmetric-matchers'
import { getState } from './state'
-import { diff, getMatcherUtils, stringify } from './jest-matcher-utils'
+import { diff, getCustomEqualityTesters, getMatcherUtils, stringify } from './jest-matcher-utils'
import {
equals,
@@ -33,8 +33,7 @@ function getMatcherState(assertion: Chai.AssertionStatic & Chai.Assertion, expec
const matcherState: MatcherState = {
...getState(expect),
- // TODO: implement via expect.addEqualityTesters
- customTesters: [],
+ customTesters: getCustomEqualityTesters(),
isNot,
utils: jestUtils,
promise,
diff --git a/packages/expect/src/jest-matcher-utils.ts b/packages/expect/src/jest-matcher-utils.ts
index 32dcc2c0319e7..ad2267d9688b9 100644
--- a/packages/expect/src/jest-matcher-utils.ts
+++ b/packages/expect/src/jest-matcher-utils.ts
@@ -1,5 +1,6 @@
-import { getColors, stringify } from '@vitest/utils'
-import type { MatcherHintOptions } from './types'
+import { getColors, getType, stringify } from '@vitest/utils'
+import type { MatcherHintOptions, Tester } from './types'
+import { JEST_MATCHERS_OBJECT } from './constants'
export { diff } from '@vitest/utils/diff'
export { stringify }
@@ -101,3 +102,21 @@ export function getMatcherUtils() {
printExpected,
}
}
+
+export function addCustomEqualityTesters(newTesters: Array): void {
+ if (!Array.isArray(newTesters)) {
+ throw new TypeError(
+ `expect.customEqualityTesters: Must be set to an array of Testers. Was given "${getType(
+ newTesters,
+ )}"`,
+ )
+ }
+
+ (globalThis as any)[JEST_MATCHERS_OBJECT].customEqualityTesters.push(
+ ...newTesters,
+ )
+}
+
+export function getCustomEqualityTesters(): Array {
+ return (globalThis as any)[JEST_MATCHERS_OBJECT].customEqualityTesters
+}
diff --git a/packages/expect/src/jest-utils.ts b/packages/expect/src/jest-utils.ts
index a6d842043ae10..a71cee767af63 100644
--- a/packages/expect/src/jest-utils.ts
+++ b/packages/expect/src/jest-utils.ts
@@ -23,7 +23,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { isObject } from '@vitest/utils'
-import type { Tester } from './types'
+import type { Tester, TesterContext } from './types'
// Extracted out of jasmine 2.5.2
export function equals(
@@ -87,8 +87,9 @@ function eq(
if (asymmetricResult !== undefined)
return asymmetricResult
+ const testerContext: TesterContext = { equals }
for (let i = 0; i < customTesters.length; i++) {
- const customTesterResult = customTesters[i](a, b)
+ const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters)
if (customTesterResult !== undefined)
return customTesterResult
}
@@ -298,7 +299,7 @@ function hasIterator(object: any) {
return !!(object != null && object[IteratorSymbol])
}
-export function iterableEquality(a: any, b: any, aStack: Array = [], bStack: Array = []): boolean | undefined {
+export function iterableEquality(a: any, b: any, customTesters: Array = [], aStack: Array = [], bStack: Array = []): boolean | undefined {
if (
typeof a !== 'object'
|| typeof b !== 'object'
@@ -324,7 +325,20 @@ export function iterableEquality(a: any, b: any, aStack: Array = [], bStack
aStack.push(a)
bStack.push(b)
- const iterableEqualityWithStack = (a: any, b: any) => iterableEquality(a, b, [...aStack], [...bStack])
+ const filteredCustomTesters: Array = [
+ ...customTesters.filter(t => t !== iterableEquality),
+ iterableEqualityWithStack,
+ ]
+
+ function iterableEqualityWithStack(a: any, b: any) {
+ return iterableEquality(
+ a,
+ b,
+ [...filteredCustomTesters],
+ [...aStack],
+ [...bStack],
+ )
+ }
if (a.size !== undefined) {
if (a.size !== b.size) {
@@ -336,7 +350,7 @@ export function iterableEquality(a: any, b: any, aStack: Array = [], bStack
if (!b.has(aValue)) {
let has = false
for (const bValue of b) {
- const isEqual = equals(aValue, bValue, [iterableEqualityWithStack])
+ const isEqual = equals(aValue, bValue, filteredCustomTesters)
if (isEqual === true)
has = true
}
@@ -357,20 +371,16 @@ export function iterableEquality(a: any, b: any, aStack: Array = [], bStack
for (const aEntry of a) {
if (
!b.has(aEntry[0])
- || !equals(aEntry[1], b.get(aEntry[0]), [iterableEqualityWithStack])
+ || !equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)
) {
let has = false
for (const bEntry of b) {
- const matchedKey = equals(aEntry[0], bEntry[0], [
- iterableEqualityWithStack,
- ])
+ const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters)
let matchedValue = false
- if (matchedKey === true) {
- matchedValue = equals(aEntry[1], bEntry[1], [
- iterableEqualityWithStack,
- ])
- }
+ if (matchedKey === true)
+ matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters)
+
if (matchedValue === true)
has = true
}
@@ -394,7 +404,7 @@ export function iterableEquality(a: any, b: any, aStack: Array = [], bStack
const nextB = bIterator.next()
if (
nextB.done
- || !equals(aValue, nextB.value, [iterableEqualityWithStack])
+ || !equals(aValue, nextB.value, filteredCustomTesters)
)
return false
}
@@ -430,7 +440,8 @@ function isObjectWithKeys(a: any) {
&& !(a instanceof Date)
}
-export function subsetEquality(object: unknown, subset: unknown): boolean | undefined {
+export function subsetEquality(object: unknown, subset: unknown, customTesters: Array = []): boolean | undefined {
+ const filteredCustomTesters = customTesters.filter(t => t !== subsetEquality)
// subsetEquality needs to keep track of the references
// it has already visited to avoid infinite loops in case
// there are circular references in the subset passed to it.
@@ -443,7 +454,7 @@ export function subsetEquality(object: unknown, subset: unknown): boolean | unde
return Object.keys(subset).every((key) => {
if (isObjectWithKeys(subset[key])) {
if (seenReferences.has(subset[key]))
- return equals(object[key], subset[key], [iterableEquality])
+ return equals(object[key], subset[key], filteredCustomTesters)
seenReferences.set(subset[key], true)
}
@@ -451,7 +462,7 @@ export function subsetEquality(object: unknown, subset: unknown): boolean | unde
= object != null
&& hasPropertyInObject(object, key)
&& equals(object[key], subset[key], [
- iterableEquality,
+ ...filteredCustomTesters,
subsetEqualityWithContext(seenReferences),
])
// The main goal of using seenReference is to avoid circular node on tree.
@@ -504,15 +515,16 @@ export function arrayBufferEquality(a: unknown, b: unknown): boolean | undefined
return true
}
-export function sparseArrayEquality(a: unknown, b: unknown): boolean | undefined {
+export function sparseArrayEquality(a: unknown, b: unknown, customTesters: Array = []): boolean | undefined {
if (!Array.isArray(a) || !Array.isArray(b))
return undefined
// A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"]
const aKeys = Object.keys(a)
const bKeys = Object.keys(b)
+ const filteredCustomTesters = customTesters.filter(t => t !== sparseArrayEquality)
return (
- equals(a, b, [iterableEquality, typeEquality], true) && equals(aKeys, bKeys)
+ equals(a, b, filteredCustomTesters, true) && equals(aKeys, bKeys)
)
}
diff --git a/packages/expect/src/state.ts b/packages/expect/src/state.ts
index 7600a6d8dda0d..ee116f5b4e7d3 100644
--- a/packages/expect/src/state.ts
+++ b/packages/expect/src/state.ts
@@ -1,9 +1,10 @@
-import type { ExpectStatic, MatcherState } from './types'
+import type { ExpectStatic, MatcherState, Tester } from './types'
import { ASYMMETRIC_MATCHERS_OBJECT, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, MATCHERS_OBJECT } from './constants'
if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
const globalState = new WeakMap()
const matchers = Object.create(null)
+ const customEqualityTesters: Array = []
const assymetricMatchers = Object.create(null)
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
get: () => globalState,
@@ -13,6 +14,7 @@ if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
get: () => ({
state: globalState.get((globalThis as any)[GLOBAL_EXPECT]),
matchers,
+ customEqualityTesters,
}),
})
Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts
index 4a05a086454a0..44df324761fc5 100644
--- a/packages/expect/src/types.ts
+++ b/packages/expect/src/types.ts
@@ -12,8 +12,21 @@ import type { diff, getMatcherUtils, stringify } from './jest-matcher-utils'
export type ChaiPlugin = Chai.ChaiPlugin
-export type Tester = (a: any, b: any) => boolean | undefined
-
+export type Tester = (
+ this: TesterContext,
+ a: any,
+ b: any,
+ customTesters: Array,
+) => boolean | undefined
+
+export interface TesterContext {
+ equals: (
+ a: unknown,
+ b: unknown,
+ customTesters?: Array,
+ strictCheck?: boolean,
+ ) => boolean
+}
export type { DiffOptions } from '@vitest/utils/diff'
export interface MatcherHintOptions {
@@ -81,6 +94,7 @@ export interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersConta
unreachable(message?: string): never
soft(actual: T, message?: string): Assertion
extend(expects: MatchersObject): void
+ addEqualityTesters(testers: Array): void
assertions(expected: number): void
hasAssertions(): void
anything(): any
diff --git a/packages/vitest/src/integrations/chai/index.ts b/packages/vitest/src/integrations/chai/index.ts
index 3422094956d5a..fb0f6d0bca1f1 100644
--- a/packages/vitest/src/integrations/chai/index.ts
+++ b/packages/vitest/src/integrations/chai/index.ts
@@ -4,7 +4,7 @@ import * as chai from 'chai'
import './setup'
import type { TaskPopulated, Test } from '@vitest/runner'
import { getCurrentTest } from '@vitest/runner'
-import { ASYMMETRIC_MATCHERS_OBJECT, GLOBAL_EXPECT, getState, setState } from '@vitest/expect'
+import { ASYMMETRIC_MATCHERS_OBJECT, GLOBAL_EXPECT, addCustomEqualityTesters, getState, setState } from '@vitest/expect'
import type { Assertion, ExpectStatic } from '@vitest/expect'
import type { MatcherState } from '../../types/chai'
import { getFullName } from '../../utils/tasks'
@@ -46,6 +46,8 @@ export function createExpect(test?: TaskPopulated) {
// @ts-expect-error untyped
expect.extend = matchers => chai.expect.extend(expect, matchers)
+ expect.addEqualityTesters = customTesters =>
+ addCustomEqualityTesters(customTesters)
expect.soft = (...args) => {
const assert = expect(...args)
diff --git a/test/core/test/expect.test.ts b/test/core/test/expect.test.ts
index 8d98e2cb55c62..90c62250995ca 100644
--- a/test/core/test/expect.test.ts
+++ b/test/core/test/expect.test.ts
@@ -1,3 +1,4 @@
+import type { Tester } from '@vitest/expect'
import { getCurrentTest } from '@vitest/runner'
import { describe, expect, expectTypeOf, test } from 'vitest'
@@ -40,3 +41,112 @@ describe('expect.soft', () => {
expect.soft('test3').toBe('test res')
})
})
+
+describe('expect.addEqualityTesters', () => {
+ class AnagramComparator {
+ public word: string
+
+ constructor(word: string) {
+ this.word = word
+ }
+
+ equals(other: AnagramComparator): boolean {
+ const cleanStr1 = this.word.replace(/ /g, '').toLowerCase()
+ const cleanStr2 = other.word.replace(/ /g, '').toLowerCase()
+
+ const sortedStr1 = cleanStr1.split('').sort().join('')
+ const sortedStr2 = cleanStr2.split('').sort().join('')
+
+ return sortedStr1 === sortedStr2
+ }
+ }
+
+ function createAnagramComparator(word: string) {
+ return new AnagramComparator(word)
+ }
+
+ function isAnagramComparator(a: unknown): a is AnagramComparator {
+ return a instanceof AnagramComparator
+ }
+
+ const areObjectsEqual: Tester = (
+ a: unknown,
+ b: unknown,
+ ): boolean | undefined => {
+ const isAAnagramComparator = isAnagramComparator(a)
+ const isBAnagramComparator = isAnagramComparator(b)
+
+ if (isAAnagramComparator && isBAnagramComparator)
+ return a.equals(b)
+
+ else if (isAAnagramComparator === isBAnagramComparator)
+ return undefined
+
+ else
+ return false
+ }
+
+ function* toIterator(array: Array): Iterator {
+ for (const obj of array)
+ yield obj
+ }
+
+ const customObject1 = createAnagramComparator('listen')
+ const customObject2 = createAnagramComparator('silent')
+
+ expect.addEqualityTesters([areObjectsEqual])
+
+ test('AnagramComparator objects are unique and not contained within arrays of AnagramComparator objects', () => {
+ expect(customObject1).not.toBe(customObject2)
+ expect([customObject1]).not.toContain(customObject2)
+ })
+
+ test('basic matchers pass different AnagramComparator objects', () => {
+ expect(customObject1).toEqual(customObject2)
+ expect([customObject1, customObject2]).toEqual([customObject2, customObject1])
+ expect(new Map([['key', customObject1]])).toEqual(new Map([['key', customObject2]]))
+ expect(new Set([customObject1])).toEqual(new Set([customObject2]))
+ expect(toIterator([customObject1, customObject2])).toEqual(
+ toIterator([customObject2, customObject1]),
+ )
+ expect([customObject1]).toContainEqual(customObject2)
+ expect({ a: customObject1 }).toHaveProperty('a', customObject2)
+ expect({ a: customObject2, b: undefined }).toStrictEqual({
+ a: customObject1,
+ b: undefined,
+ })
+ expect({ a: 1, b: { c: customObject1 } }).toMatchObject({
+ a: 1,
+ b: { c: customObject2 },
+ })
+ })
+
+ test('asymmetric matchers pass different AnagramComparator objects', () => {
+ expect([customObject1]).toEqual(expect.arrayContaining([customObject1]))
+ expect({ a: 1, b: { c: customObject1 } }).toEqual(
+ expect.objectContaining({ b: { c: customObject2 } }),
+ )
+ })
+
+ test('toBe recommends toStrictEqual even with different objects', () => {
+ expect(() => expect(customObject1).toBe(customObject2)).toThrow('toStrictEqual')
+ })
+
+ test('toBe recommends toEqual even with different AnagramComparator objects', () => {
+ expect(() => expect({ a: undefined, b: customObject1 }).toBe({ b: customObject2 })).toThrow(
+ 'toEqual',
+ )
+ })
+
+ test('iterableEquality still properly detects cycles', () => {
+ const a = new Set()
+ a.add(customObject1)
+ a.add(a)
+
+ const b = new Set()
+ b.add(customObject2)
+ b.add(b)
+
+ expect(a).toEqual(b)
+ })
+})