Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 161 additions & 1 deletion packages/form-core/tests/mergeForm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest'
import { mutateMergeDeep } from '../src/mergeForm'
import { FormApi } from '../src/FormApi'
import { mergeForm, mutateMergeDeep } from '../src/mergeForm'

type TestObject = Record<string, any>

Expand Down Expand Up @@ -97,4 +98,163 @@ describe('mutateMergeDeep', () => {
mutateMergeDeep(a, b)
expect(a).toStrictEqual({ a: { a: 1, b: 2 } })
})

it('should return empty object when target is null', () => {
const result = mutateMergeDeep(null, { a: 1 })
expect(result).toStrictEqual({})
})

it('should return empty object when target is undefined', () => {
const result = mutateMergeDeep(undefined, { a: 1 })
expect(result).toStrictEqual({})
})

it('should return empty object when target is not an object', () => {
const result = mutateMergeDeep('string' as any, { a: 1 })
expect(result).toStrictEqual({})
})

it('should return target unchanged when source is null', () => {
const target = { a: 1 }
const result = mutateMergeDeep(target, null)
expect(result).toBe(target)
expect(result).toStrictEqual({ a: 1 })
})

it('should return target unchanged when source is undefined', () => {
const target = { a: 1 }
const result = mutateMergeDeep(target, undefined)
expect(result).toBe(target)
expect(result).toStrictEqual({ a: 1 })
})

it('should return target unchanged when source is not an object', () => {
const target = { a: 1 }
const result = mutateMergeDeep(target, 'string' as any)
expect(result).toBe(target)
expect(result).toStrictEqual({ a: 1 })
})

it('should replace arrays completely without merging their elements', () => {
const target = {
users: [
{ id: 1, name: 'Old User', deleted: true },
{ id: 2, name: 'Another User' },
],
}
const source = {
users: [{ id: 1, name: 'New User' }],
}

mutateMergeDeep(target, source)

expect(target.users).toHaveLength(1)
expect(target.users[0]).toStrictEqual({ id: 1, name: 'New User' })
expect(target.users[0]).not.toHaveProperty('deleted')
})

it('should handle circular references without stack overflow', () => {
const target: any = { data: { value: 1 } }
target.data.self = target.data

const source = { data: { newValue: 2 } }

expect(() => mutateMergeDeep(target, source)).not.toThrow()
expect(target.data.newValue).toBe(2)
expect(target.data.value).toBe(1)
expect(target.data.self).toBe(target.data)
})

it('should not merge symbol keys (Object.keys limitation)', () => {
const sym = Symbol('test')
const target = { [sym]: 'original', regular: 'value' }
const source = { [sym]: 'new', regular: 'updated' }

mutateMergeDeep(target, source)

expect(target[sym]).toBe('original')

expect(target.regular).toBe('updated')
})
})

describe('mergeForm', () => {
// `as any` required: FormApi has 12 generic params with TSubmitMeta defaulting to `never`,
// incompatible with mergeForm's `any` constraint. Type limitation only - runtime is correct.
// Production usage via useTransform receives AnyFormApi, avoiding this.

it('should merge state into form using mutateMergeDeep', () => {
const form = new FormApi({
defaultValues: {
name: '',
age: 0,
},
})

form.mount()

const newState = {
values: {
name: 'John',
age: 30,
},
}

const result = mergeForm(form as any, newState)

expect(result).toBe(form)

expect(form.state.values).toStrictEqual({
name: 'John',
age: 30,
})
})

it('should handle partial state updates', () => {
const form = new FormApi({
defaultValues: {
firstName: '',
lastName: '',
},
})

form.mount()

mergeForm(form as any, {
values: {
firstName: 'Jane',
},
})

expect(form.state.values.firstName).toBe('Jane')
expect(form.state.values.lastName).toBe('')
})

it('should handle deeply nested state updates', () => {
const form = new FormApi({
defaultValues: {
user: {
profile: {
name: '',
age: 0,
},
},
},
})

form.mount()

mergeForm(form as any, {
values: {
user: {
profile: {
name: 'Alice',
},
},
},
})

expect(form.state.values.user.profile.name).toBe('Alice')
expect(form.state.values.user.profile.age).toBe(0)
})
})
Loading