Skip to content

Commit

Permalink
add more tests to CustomSet; fix issues
Browse files Browse the repository at this point in the history
  • Loading branch information
pascal-botpress committed Feb 21, 2025
1 parent faa1d5b commit 3e60a81
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 29 deletions.
152 changes: 133 additions & 19 deletions zui/src/z/types/utils/custom-set.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,142 @@
import { it, expect } from 'vitest'
import { it, expect, describe } from 'vitest'
import { CustomSet } from './custom-set'

describe('CustomSet', () => {
it('does not contain the same item twice', () => {
const set = new CustomSet([1, 1, 1])
expect(set.size).toBe(1)
})
describe.concurrent('CustomSet', () => {
describe.concurrent('sets of primitives', () => {
it('does not contain the same item twice', () => {
const set = new CustomSet([1, 1, 1])
expect(set.size).toBe(1)
})

it('uses the provided equality function', () => {
const set = new CustomSet([1, 2, 3], {
compare: (a, b) => Math.floor(a / 2) === Math.floor(b / 2),
it('uses the provided equality function', () => {
const set = new CustomSet([1, 2, 3], {
compare: (a, b) => Math.floor(a / 2) === Math.floor(b / 2),
})
expect(set.size).toBe(2)
})

it('is subset of another set with more items', () => {
const set = new CustomSet([1, 2])
const other = new CustomSet([1, 2, 3])
expect(set.isSubsetOf(other)).toBe(true)
})

it('is not subset of another set with less items', () => {
const set = new CustomSet([1, 2, 3])
const other = new CustomSet([1, 2])
expect(set.isSubsetOf(other)).toBe(false)
})
expect(set.size).toBe(2)
})

it('is subset of another set with more items', () => {
const set = new CustomSet([1, 2])
const other = new CustomSet([1, 2, 3])
expect(set.isSubsetOf(other)).toBe(true)
it('is subset of another identical set', () => {
const set = new CustomSet([1, 2, 3])
const other = new CustomSet([1, 2, 3])
expect(set.isSubsetOf(other)).toBe(true)
expect(other.isSubsetOf(set)).toBe(true)
})
})

it('is not subset of another set with less items', () => {
const set = new CustomSet([1, 2, 3])
const other = new CustomSet([1, 2])
expect(set.isSubsetOf(other)).toBe(false)
describe.concurrent('sets of reference types', () => {
describe.concurrent('class instances', () => {
class Foo {
constructor(public id: number) {}
}

it('does not contain the same item twice', () => {
const set = new CustomSet([new Foo(1), new Foo(1), new Foo(1)])
expect(set.size).toBe(1)
})

it('uses the provided equality function', () => {
const set = new CustomSet([new Foo(1), new Foo(2), new Foo(3)], {
compare: (a, b) => Math.floor(a.id / 2) === Math.floor(b.id / 2),
})
expect(set.size).toBe(2)
})

it('is subset of another set with more items', () => {
const set = new CustomSet([new Foo(1), new Foo(2)])
const other = new CustomSet([new Foo(1), new Foo(2), new Foo(3)])
expect(set.isSubsetOf(other)).toBe(true)
})

it('is not subset of another set with less items', () => {
const set = new CustomSet([new Foo(1), new Foo(2), new Foo(3)])
const other = new CustomSet([new Foo(1), new Foo(2)])
expect(set.isSubsetOf(other)).toBe(false)
})

it('is subset of another identical set', () => {
const set = new CustomSet([new Foo(1), new Foo(2), new Foo(3)])
const other = new CustomSet([new Foo(1), new Foo(2), new Foo(3)])
expect(set.isSubsetOf(other)).toBe(true)
expect(other.isSubsetOf(set)).toBe(true)
})
})

describe.concurrent('plain objects', () => {
it('does not contain the same item twice', () => {
const set = new CustomSet([{ id: 1 }, { id: 1 }, { id: 1 }])
expect(set.size).toBe(1)
})

it('uses the provided equality function', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }], {
compare: (a, b) => Math.floor(a.id / 2) === Math.floor(b.id / 2),
})
expect(set.size).toBe(2)
})

it('is subset of another set with more items', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }])
const other = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
expect(set.isSubsetOf(other)).toBe(true)
})

it('is not subset of another set with less items', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
const other = new CustomSet([{ id: 1 }, { id: 2 }])
expect(set.isSubsetOf(other)).toBe(false)
})

it('is subset of another identical set', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
const other = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
expect(set.isSubsetOf(other)).toBe(true)
expect(other.isSubsetOf(set)).toBe(true)
})
})

describe.concurrent('plain objects with some undefined values', () => {
it('does not contain the same item twice', () => {
const set = new CustomSet([{ id: 1 }, { id: 1, foo: undefined }, { id: 1 }])
expect(set.size).toBe(1)
})

it('uses the provided equality function', () => {
const set = new CustomSet([{ id: 1 }, { id: 2, foo: undefined }, { id: 3 }], {
compare: (a, b) => Math.floor(a.id / 2) === Math.floor(b.id / 2),
})
expect(set.size).toBe(2)
})

it('is subset of another set with more items', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }])
const other = new CustomSet([{ id: 1, foo: undefined }, { id: 2 }, { id: 3 }])
expect(set.isSubsetOf(other)).toBe(true)
})

it('is not subset of another set with less items', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
const other = new CustomSet([{ id: 1, foo: undefined }, { id: 2 }])
expect(set.isSubsetOf(other)).toBe(false)
})

it('is subset of another identical set', () => {
const set = new CustomSet([{ id: 1 }, { id: 2 }, { id: 3 }])
const other = new CustomSet([{ id: 1 }, { id: 2, foo: undefined }, { id: 3 }])
expect(set.isSubsetOf(other)).toBe(true)
expect(other.isSubsetOf(set)).toBe(true)
})
})
})
})
28 changes: 18 additions & 10 deletions zui/src/z/types/utils/is-equal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,26 @@ export const isEqual = (a: any, b: any): boolean => lodash.isEqualWith(a, b, _cu

const _alreadyChecked = Symbol('infinite recursion prevention')

const _customizer: IsEqualCustomizer = (value, other) => {
if (
lodash.isPlainObject(value) &&
!value[_alreadyChecked] &&
lodash.isPlainObject(other) &&
!other[_alreadyChecked]
) {
const _customizer: IsEqualCustomizer = (a, b) => {
if (lodash.isPlainObject(a) && !a[_alreadyChecked] && lodash.isPlainObject(b) && !b[_alreadyChecked]) {
const cleanedA = lodash.omitBy(a, lodash.isUndefined)
const cleanedB = lodash.omitBy(b, lodash.isUndefined)

// Prevent infinite recursion: mark objects as already checked:
value[_alreadyChecked] = true
other[_alreadyChecked] = true
Object.defineProperty(cleanedA, _alreadyChecked, {
value: true,
enumerable: false,
configurable: false,
writable: false,
})
Object.defineProperty(cleanedB, _alreadyChecked, {
value: true,
enumerable: false,
configurable: false,
writable: false,
})

return isEqual(lodash.omitBy(value, lodash.isUndefined), lodash.omitBy(other, lodash.isUndefined))
return isEqual(cleanedA, cleanedB)
}

return undefined // Offload to default lodash isEqual comparison
Expand Down

0 comments on commit 3e60a81

Please sign in to comment.