YuanHeDx 5f1a1cfa1c
fix: do not delete object key when shallowSetIn value is undefined, fix err msg and add a ut (#84)
* fix: do not delete key when shallowSetIn value is undefined

* fix: fix err msg in path, add ut for chained swap

* chore: comment the object test case not used in form

* chore: add fix comment

* fix: remove redundant clone
2025-03-24 08:30:59 +00:00

238 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from 'vitest';
import { getIn, isEmptyArray, isNaN, isPromise, shallowSetIn } from '../src/utils';
describe('object', () => {
describe('isEmptyArray', () => {
it('returns true when an empty array is passed in', () => {
expect(isEmptyArray([])).toBe(true);
});
it('returns false when anything other than empty array is passed in', () => {
expect(isEmptyArray()).toBe(false);
expect(isEmptyArray(null)).toBe(false);
expect(isEmptyArray(123)).toBe(false);
expect(isEmptyArray('abc')).toBe(false);
expect(isEmptyArray({})).toBe(false);
expect(isEmptyArray({ a: 1 })).toBe(false);
expect(isEmptyArray(['abc'])).toBe(false);
});
});
describe('getIn', () => {
const obj = {
a: {
b: 2,
c: false,
d: null,
},
t: true,
s: 'a random string',
};
it('gets a value by array path', () => {
expect(getIn(obj, ['a', 'b'])).toBe(2);
});
it('gets a value by string path', () => {
expect(getIn(obj, 'a.b')).toBe(2);
});
it('return "undefined" if value was not found using given path', () => {
expect(getIn(obj, 'a.z')).toBeUndefined();
});
it('return "undefined" if value was not found using given path and an intermediate value is "false"', () => {
expect(getIn(obj, 'a.c.z')).toBeUndefined();
});
it('return "undefined" if value was not found using given path and an intermediate value is "null"', () => {
expect(getIn(obj, 'a.d.z')).toBeUndefined();
});
it('return "undefined" if value was not found using given path and an intermediate value is "true"', () => {
expect(getIn(obj, 't.z')).toBeUndefined();
});
it('return "undefined" if value was not found using given path and an intermediate value is a string', () => {
expect(getIn(obj, 's.z')).toBeUndefined();
});
});
describe('shallowSetIn', () => {
it('sets flat value', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'flat', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', flat: 'value' });
});
it('keep the same object if nothing is changed', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'x', 'y');
expect(obj).toBe(newObj);
});
it('keep key shen set undefined', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'x', undefined);
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: undefined });
expect(Object.keys(newObj)).toEqual(['x']);
});
it('sets nested value', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'nested.value', 'nested value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: { value: 'nested value' } });
});
it('updates nested value', () => {
const obj = { x: 'y', nested: { value: 'a' } };
const newObj = shallowSetIn(obj, 'nested.value', 'b');
expect(obj).toEqual({ x: 'y', nested: { value: 'a' } });
expect(newObj).toEqual({ x: 'y', nested: { value: 'b' } });
});
it('updates deep nested value', () => {
const obj = { x: 'y', twofoldly: { nested: { value: 'a' } } };
const newObj = shallowSetIn(obj, 'twofoldly.nested.value', 'b');
expect(obj.twofoldly.nested === newObj.twofoldly.nested).toEqual(false); // fails, same object still
expect(obj).toEqual({ x: 'y', twofoldly: { nested: { value: 'a' } } }); // fails, it's b here, too
expect(newObj).toEqual({ x: 'y', twofoldly: { nested: { value: 'b' } } }); // works ofc
});
it('shallow clone data along the update path', () => {
const obj = {
x: 'y',
twofoldly: { nested: ['a', { c: 'd' }] },
other: { nestedOther: 'o' },
};
const newObj = shallowSetIn(obj, 'twofoldly.nested.0', 'b');
// All new objects/arrays created along the update path.
expect(obj).not.toBe(newObj);
expect(obj.twofoldly).not.toBe(newObj.twofoldly);
expect(obj.twofoldly.nested).not.toBe(newObj.twofoldly.nested);
// All other objects/arrays copied, not cloned (retain same memory
// location).
expect(obj.other).toBe(newObj.other);
expect(obj.twofoldly.nested[1]).toBe(newObj.twofoldly.nested[1]);
});
it('sets new array', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'nested.0', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: ['value'] });
});
it('sets new array when item is empty string', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'nested.0', '');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: [''] });
});
it('sets new array when item is empty string', () => {
const obj = {};
const newObj = shallowSetIn(obj, 'nested.0', '');
expect(obj).toEqual({});
expect(newObj).toEqual({ nested: [''] });
});
it('updates nested array value', () => {
const obj = { x: 'y', nested: ['a'] };
const newObj = shallowSetIn(obj, 'nested[0]', 'b');
expect(obj).toEqual({ x: 'y', nested: ['a'] });
expect(newObj).toEqual({ x: 'y', nested: ['b'] });
});
it('adds new item to nested array', () => {
const obj = { x: 'y', nested: ['a'] };
const newObj = shallowSetIn(obj, 'nested.1', 'b');
expect(obj).toEqual({ x: 'y', nested: ['a'] });
expect(newObj).toEqual({ x: 'y', nested: ['a', 'b'] });
});
it('sticks to object with int key when defined', () => {
const obj = { x: 'y', nested: { 0: 'a' } };
const newObj = shallowSetIn(obj, 'nested.0', 'b');
expect(obj).toEqual({ x: 'y', nested: { 0: 'a' } });
expect(newObj).toEqual({ x: 'y', nested: { 0: 'b' } });
});
it('supports bracket path', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'nested[0]', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: ['value'] });
});
it('supports path containing key of the object', () => {
const obj = { x: 'y' };
const newObj = shallowSetIn(obj, 'a.x.c', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', a: { x: { c: 'value' } } });
});
// This case is not used in form sdk for nowso we comment it.
// it('should keep class inheritance for the top level object', () => {
// class TestClass {
// constructor(public key: string, public setObj?: any) {}
// }
// const obj = new TestClass('value');
// const newObj = shallowSetIn(obj, 'setObj.nested', 'shallowSetInValue');
// expect(obj).toEqual(new TestClass('value'));
// expect(newObj).toEqual({
// key: 'value',
// setObj: { nested: 'shallowSetInValue' },
// });
// expect(obj instanceof TestClass).toEqual(true);
// expect(newObj instanceof TestClass).toEqual(true);
// });
it('can convert primitives to objects before setting', () => {
const obj = { x: [{ y: true }] };
const newObj = shallowSetIn(obj, 'x.0.y.z', true);
expect(obj).toEqual({ x: [{ y: true }] });
expect(newObj).toEqual({ x: [{ y: { z: true } }] });
});
});
describe('isPromise', () => {
it('verifies that a value is a promise', () => {
const alwaysResolve = (resolve: Function) => resolve();
const promise = new Promise(alwaysResolve);
expect(isPromise(promise)).toEqual(true);
});
it('verifies that a value is not a promise', () => {
const emptyObject = {};
const identity = (i: any) => i;
const foo = 'foo';
const answerToLife = 42;
expect(isPromise(emptyObject)).toEqual(false);
expect(isPromise(identity)).toEqual(false);
expect(isPromise(foo)).toEqual(false);
expect(isPromise(answerToLife)).toEqual(false);
expect(isPromise(undefined)).toEqual(false);
expect(isPromise(null)).toEqual(false);
});
});
describe('isNaN', () => {
it('correctly validate NaN', () => {
expect(isNaN(NaN)).toBe(true);
});
it('correctly validate not NaN', () => {
expect(isNaN(undefined)).toBe(false);
expect(isNaN(1)).toBe(false);
expect(isNaN('')).toBe(false);
expect(isNaN([])).toBe(false);
});
});
});