165 lines
4.8 KiB
TypeScript
165 lines
4.8 KiB
TypeScript
import {
|
|
clean, deepCopy, deepMerge, dotNotation, encodeQuery, flattenObj, formData, includes, isEqual, mixin,
|
|
JSONAttemptParse, JSONSerialize, JSONSanitize
|
|
} from '../src';
|
|
|
|
describe('Object utilities', () => {
|
|
describe('clean', () => {
|
|
it('removes null values', () => {
|
|
const obj = { a: 1, b: null, c: undefined, d: 0 };
|
|
expect(clean({ ...obj })).toEqual({ a: 1, c: undefined, d: 0 });
|
|
});
|
|
it('throws on null input', () => {
|
|
expect(() => clean(null as any)).toThrow();
|
|
});
|
|
it('removes undefined only when specified', () => {
|
|
const obj = { a: 1, b: undefined, c: null };
|
|
expect(clean({ ...obj }, true)).toEqual({ a: 1, c: null });
|
|
});
|
|
it('works for arrays', () => {
|
|
expect(clean([1, null, 2, undefined, 3] as any)).toEqual([1, 2, 3]);
|
|
});
|
|
});
|
|
|
|
describe('deepCopy', () => {
|
|
it('creates a deep copy', () => {
|
|
const obj = { a: { b: 2 } };
|
|
const copy = deepCopy(obj);
|
|
expect(copy).toEqual(obj);
|
|
expect(copy).not.toBe(obj);
|
|
expect(copy.a).not.toBe(obj.a);
|
|
});
|
|
});
|
|
|
|
describe('deepMerge', () => {
|
|
it('merges deeply nested objects', () => {
|
|
const tgt = { a: { b: 1 }, d: 7 };
|
|
const src = { a: { c: 2 }, d: 8 };
|
|
expect(deepMerge({ ...tgt }, src)).toEqual({ a: { b: 1, c: 2 }, d: 8 });
|
|
});
|
|
it('merges multiple sources', () => {
|
|
const t = { a: 1 };
|
|
const s1 = { b: 2 };
|
|
const s2 = { c: 3 };
|
|
expect(deepMerge({ ...t }, s1, s2)).toEqual({ a: 1, b: 2, c: 3 });
|
|
});
|
|
});
|
|
|
|
describe('dotNotation', () => {
|
|
it('gets nested value', () => {
|
|
const obj = { a: { b: { c: 3 } } };
|
|
expect(dotNotation(obj, 'a.b.c')).toBe(3);
|
|
});
|
|
it('sets nested value', () => {
|
|
const obj = { a: { b: { c: 3 } } };
|
|
dotNotation(obj, 'a.b.c', 10);
|
|
expect(obj.a.b.c).toBe(10);
|
|
});
|
|
it('returns undefined for non-existent path', () => {
|
|
expect(dotNotation({ a: 1 }, 'a.b.c')).toBeUndefined();
|
|
});
|
|
it('creates nested object when setting', () => {
|
|
const obj: any = {};
|
|
dotNotation(obj, 'd.e.f', 5);
|
|
expect(obj.d.e.f).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('encodeQuery', () => {
|
|
it('encodes simple objects', () => {
|
|
expect(encodeQuery({ a: 1, b: 'test' })).toBe('a=1&b=test');
|
|
});
|
|
it('handles special characters', () => {
|
|
expect(encodeQuery({ a: 'hello world' })).toBe('a=hello%20world');
|
|
});
|
|
});
|
|
|
|
describe('flattenObj', () => {
|
|
it('flattens nested objects', () => {
|
|
const obj = { a: { b: 2 }, c: 3 };
|
|
expect(flattenObj(obj)).toEqual({ 'a.b': 2, c: 3 });
|
|
});
|
|
it('handles multiple nesting', () => {
|
|
const obj = { a: { b: { c: 4 } } };
|
|
expect(flattenObj(obj)).toEqual({ 'a.b.c': 4 });
|
|
});
|
|
});
|
|
|
|
describe('formData', () => {
|
|
it('converts object to FormData', () => {
|
|
const obj = { a: '1', b: 'foo' };
|
|
const fd = formData(obj);
|
|
expect(fd.get('a')).toBe('1');
|
|
expect(fd.get('b')).toBe('foo');
|
|
});
|
|
});
|
|
|
|
describe('includes', () => {
|
|
it('checks if all values included', () => {
|
|
expect(includes({ a: 2, b: 3 }, { a: 2 })).toBeTruthy();
|
|
expect(includes({ a: 2, b: 3 }, { c: 1 })).toBeFalsy();
|
|
});
|
|
it('handles arrays of values', () => {
|
|
expect(includes([{ a: 1 }], [{ a: 1 }])).toBeTruthy();
|
|
expect(includes([{ a: 1 }], [{ a: 2 }])).toBeFalsy();
|
|
});
|
|
it('allows missing when specified', () => {
|
|
expect(includes(undefined, { a: 2 }, true)).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('isEqual', () => {
|
|
it('returns true for deeply equal objects', () => {
|
|
expect(isEqual({ a: 1, b: { c: 3 } }, { a: 1, b: { c: 3 } })).toBe(true);
|
|
});
|
|
it('returns false for non-equal objects', () => {
|
|
expect(isEqual({ a: 1 }, { a: 2 })).toBe(false);
|
|
});
|
|
it('compares functions by string', () => {
|
|
expect(isEqual(() => 1, () => 1)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('mixin', () => {
|
|
it('merges prototypes', () => {
|
|
class A { foo() { return 1; } }
|
|
class B { bar() { return 2; } }
|
|
class C {}
|
|
mixin(C, [A, B]);
|
|
const c = new (C as any)();
|
|
expect(c.foo()).toBe(1);
|
|
expect(c.bar()).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('JSONAttemptParse', () => {
|
|
it('parses valid JSON', () => {
|
|
expect(JSONAttemptParse('{"a":1}')).toEqual({ a: 1 });
|
|
});
|
|
it('returns original string on error', () => {
|
|
expect(JSONAttemptParse('not json')).toBe('not json');
|
|
});
|
|
});
|
|
|
|
describe('JSONSerialize', () => {
|
|
it('serializes objects', () => {
|
|
expect(JSONSerialize({ a: 1 })).toBe(JSON.stringify({ a: 1 }));
|
|
});
|
|
it('leaves primitives as is', () => {
|
|
expect(JSONSerialize('test')).toBe('test');
|
|
expect(JSONSerialize(123)).toBe(123);
|
|
});
|
|
});
|
|
|
|
describe('JSONSanitize', () => {
|
|
it('stringifies objects', () => {
|
|
expect(JSONSanitize({ a: 1 })).toBe(JSON.stringify({ a: 1 }));
|
|
});
|
|
it('does not throw on circular refs', () => {
|
|
const obj: any = {};
|
|
obj.self = obj;
|
|
expect(() => JSONSanitize(obj)).not.toThrow();
|
|
});
|
|
});
|
|
});
|