mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
* 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
238 lines
8.3 KiB
TypeScript
238 lines
8.3 KiB
TypeScript
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 now,so 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);
|
||
});
|
||
});
|
||
});
|