test(common): improve test coverage for shared utils

- Add tests for edge cases in shared utility functions

- Fix issues with normalizePath and isEmpty
This commit is contained in:
Brahim Abdelli
2025-03-23 19:45:20 +01:00
parent 3ebb53bffb
commit 4fe18eb736
2 changed files with 75 additions and 12 deletions

View File

@@ -26,7 +26,13 @@ describe('Shared utils', () => {
it('should return false when object is not undefined', () => {
expect(isUndefined({})).toBe(false);
});
it('should return false for falsy values like false, 0, or empty string', () => {
expect(isUndefined(false)).to.be.false;
expect(isUndefined(0)).to.be.false;
expect(isUndefined('')).to.be.false;
});
});
describe('isFunction', () => {
it('should return true when obj is function', () => {
expect(isFunction(() => ({}))).toBe(true);
@@ -36,16 +42,21 @@ describe('Shared utils', () => {
expect(isFunction(undefined)).toBe(false);
});
});
describe('isObject', () => {
it('should return true when obj is object', () => {
expect(isObject({})).toBe(true);
});
it('should return true for arrays', () => {
expect(isObject([1, 2, 3])).to.be.true; // Arrays are objects
});
it('should return false when object is not object', () => {
expect(isObject(3)).toBe(false);
expect(isObject(null)).toBe(false);
expect(isObject(undefined)).toBe(false);
});
});
describe('isPlainObject', () => {
it('should return true when obj is plain object', () => {
expect(isPlainObject({})).toBe(true);
@@ -65,7 +76,12 @@ describe('Shared utils', () => {
expect(isPlainObject(new Date())).toBe(false);
expect(isPlainObject(new Foo(1))).toBe(false);
});
it('should return false for objects with custom prototypes', () => {
function CustomObject() {}
expect(isPlainObject(new CustomObject())).to.be.false;
});
});
describe('isString', () => {
it('should return true when val is a string', () => {
expect(isString('true')).toBe(true);
@@ -77,6 +93,7 @@ describe('Shared utils', () => {
expect(isString(undefined)).toBe(false);
});
});
describe('isSymbol', () => {
it('should return true when val is a Symbol', () => {
expect(isSymbol(Symbol())).toBe(true);
@@ -87,7 +104,11 @@ describe('Shared utils', () => {
expect(isSymbol(null)).toBe(false);
expect(isSymbol(undefined)).toBe(false);
});
it('should return false for invalid Symbol objects', () => {
expect(isSymbol(Object(Symbol()))).to.be.false;
});
});
describe('isNumber', () => {
it('should return true when val is a number or NaN', () => {
expect(isNumber(1)).toBe(true);
@@ -97,14 +118,18 @@ describe('Shared utils', () => {
expect(isNumber(0b1)).toBe(true); // binary notation
expect(isNumber(0x1)).toBe(true); // hexadecimal notation
expect(isNumber(NaN)).toBe(true);
expect(isNumber(Infinity)).toBe(true);
expect(isNumber(-Infinity)).toBe(true);
});
it('should return false when val is not a number', () => {
// expect(isNumber(1n)).toBe(false); // big int (available on ES2020)
expect(isNumber('1')).toBe(false); // string
expect(isNumber(undefined)).toBe(false); // nullish
expect(isNumber(null)).toBe(false); // nullish
expect(isNumber(new Number(123))).toBe(false); // number
});
});
describe('isConstructor', () => {
it('should return true when string is equal to constructor', () => {
expect(isConstructor('constructor')).toBe(true);
@@ -112,7 +137,13 @@ describe('Shared utils', () => {
it('should return false when string is not equal to constructor', () => {
expect(isConstructor('nope')).toBe(false);
});
it('should return false for non-string values', () => {
expect(isConstructor(null)).to.be.false;
expect(isConstructor(undefined)).to.be.false;
expect(isConstructor(123)).to.be.false;
});
});
describe('addLeadingSlash', () => {
it('should return the validated path ("add / if not exists")', () => {
expect(addLeadingSlash('nope')).toEqual('/nope');
@@ -127,7 +158,13 @@ describe('Shared utils', () => {
expect(addLeadingSlash(null!)).toEqual('');
expect(addLeadingSlash(undefined)).toEqual('');
});
it('should handle paths with special characters', () => {
expect(addLeadingSlash('path-with-special-chars!@#$%^&*()')).to.eql(
'/path-with-special-chars!@#$%^&*()',
);
});
});
describe('normalizePath', () => {
it('should remove all trailing slashes at the end of the path', () => {
expect(normalizePath('path/')).toEqual('/path');
@@ -145,6 +182,7 @@ describe('Shared utils', () => {
expect(normalizePath(undefined)).toEqual('/');
});
});
describe('isNil', () => {
it('should return true when obj is undefined or null', () => {
expect(isNil(undefined)).toBe(true);
@@ -153,7 +191,13 @@ describe('Shared utils', () => {
it('should return false when object is not undefined and null', () => {
expect(isNil('3')).toBe(false);
});
it('should return false for falsy values like false, 0, or empty string', () => {
expect(isNil(false)).to.be.false;
expect(isNil(0)).to.be.false;
expect(isNil('')).to.be.false;
});
});
describe('isEmpty', () => {
it('should return true when array is empty or not exists', () => {
expect(isEmpty([])).toBe(true);
@@ -163,10 +207,19 @@ describe('Shared utils', () => {
it('should return false when array is not empty', () => {
expect(isEmpty([1, 2])).toBe(false);
});
it('should return true for non-array values', () => {
expect(isEmpty({})).to.be.true;
expect(isEmpty('')).to.be.true;
expect(isEmpty(0)).to.be.true;
expect(isEmpty(false)).to.be.true;
});
});
describe('stripEndSlash', () => {
it('should strip end slash if present', () => {
expect(stripEndSlash('/cats/')).toBe('/cats');
});
it('should return the same path if no trailing slash exists', () => {
expect(stripEndSlash('/cats')).toBe('/cats');
});
});

View File

@@ -1,10 +1,10 @@
export const isUndefined = (obj: any): obj is undefined =>
export const isUndefined = (obj: unknown): obj is undefined =>
typeof obj === 'undefined';
export const isObject = (fn: any): fn is object =>
export const isObject = (fn: unknown): fn is object =>
!isNil(fn) && typeof fn === 'object';
export const isPlainObject = (fn: any): fn is object => {
export const isPlainObject = (fn: unknown): fn is object => {
if (!isObject(fn)) {
return false;
}
@@ -37,15 +37,25 @@ export const normalizePath = (path?: string): string =>
: '/' + path.replace(/\/+$/, '')
: '/';
export const stripEndSlash = (path: string) =>
path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
export const stripEndSlash = (path: string): string =>
path.endsWith('/') ? path.slice(0, -1) : path;
export const isFunction = (val: any): val is Function =>
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function';
export const isString = (val: any): val is string => typeof val === 'string';
export const isNumber = (val: any): val is number => typeof val === 'number';
export const isConstructor = (val: any): boolean => val === 'constructor';
export const isNil = (val: any): val is null | undefined =>
export const isString = (val: unknown): val is string =>
typeof val === 'string';
export const isNumber = (val: unknown): val is number =>
typeof val === 'number';
export const isConstructor = (val: unknown): boolean => val === 'constructor';
export const isNil = (val: unknown): val is null | undefined =>
isUndefined(val) || val === null;
export const isEmpty = (array: any): boolean => !(array && array.length > 0);
export const isSymbol = (val: any): val is symbol => typeof val === 'symbol';
export const isEmpty = (array: unknown): boolean =>
!(Array.isArray(array) && array.length > 0);
export const isSymbol = (val: unknown): val is symbol =>
typeof val === 'symbol';