Skip to content

Commit abed8c3

Browse files
committed
test(common): Fix isEmptyArray behavior and update tests
- Fix isEmptyArray to return false for non-array values and array-like objects - Update tests to align with the new behavior - Add comprehensive test coverage for edge cases
1 parent 4102122 commit abed8c3

23 files changed

+85
-59
lines changed

packages/common/pipes/file/parse-file.pipe.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable, Optional } from '../../decorators/core';
22
import { HttpStatus } from '../../enums';
33
import { PipeTransform } from '../../interfaces/features/pipe-transform.interface';
44
import { HttpErrorByCode } from '../../utils/http-error-by-code.util';
5-
import { isEmpty, isObject, isUndefined } from '../../utils/shared.utils';
5+
import { isEmptyArray, isObject, isUndefined } from '../../utils/shared.utils';
66
import { FileValidator } from './file-validator.interface';
77
import { ParseFileOptions } from './parse-file-options.interface';
88

@@ -60,9 +60,8 @@ export class ParseFilePipe implements PipeTransform<any> {
6060
}
6161

6262
private thereAreNoFilesIn(value: any): boolean {
63-
const isEmptyArray = Array.isArray(value) && isEmpty(value);
64-
const isEmptyObject = isObject(value) && isEmpty(Object.keys(value));
65-
return isUndefined(value) || isEmptyArray || isEmptyObject;
63+
const isEmptyObject = isObject(value) && isEmptyArray(Object.keys(value));
64+
return isUndefined(value) || isEmptyArray(value) || isEmptyObject;
6665
}
6766

6867
protected async validate(file: any): Promise<any> {

packages/common/test/utils/shared.utils.spec.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect } from 'chai';
22
import {
33
addLeadingSlash,
44
isConstructor,
5-
isEmpty,
5+
isEmptyArray,
66
isFunction,
77
isNil,
88
isNumber,
@@ -199,20 +199,43 @@ describe('Shared utils', () => {
199199
});
200200
});
201201

202-
describe('isEmpty', () => {
203-
it('should return true when array is empty or not exists', () => {
204-
expect(isEmpty([])).to.be.true;
205-
expect(isEmpty(null)).to.be.true;
206-
expect(isEmpty(undefined)).to.be.true;
202+
describe('isEmptyArray', () => {
203+
it('should return true when array is empty', () => {
204+
expect(isEmptyArray([])).to.be.true;
207205
});
206+
208207
it('should return false when array is not empty', () => {
209-
expect(isEmpty([1, 2])).to.be.false;
208+
expect(isEmptyArray([1, 2])).to.be.false;
209+
expect(isEmptyArray(['a', 'b', 'c'])).to.be.false;
210+
expect(isEmptyArray([{}])).to.be.false;
211+
});
212+
213+
it('should return false for non-array values', () => {
214+
expect(isEmptyArray(null)).to.be.false;
215+
expect(isEmptyArray(undefined)).to.be.false;
216+
expect(isEmptyArray({})).to.be.false;
217+
expect(isEmptyArray('')).to.be.false;
218+
expect(isEmptyArray(0)).to.be.false;
219+
expect(isEmptyArray(false)).to.be.false;
220+
expect(isEmptyArray(Symbol())).to.be.false;
221+
expect(isEmptyArray(() => {})).to.be.false;
222+
});
223+
224+
it('should return false for array-like objects', () => {
225+
expect(isEmptyArray({ length: 0 })).to.be.false;
226+
expect(isEmptyArray({ length: 1 })).to.be.false;
227+
});
228+
229+
it('should return false for sparse arrays', () => {
230+
const sparseArray = new Array(3);
231+
expect(isEmptyArray(sparseArray)).to.be.false;
210232
});
211-
it('should return true for non-array values', () => {
212-
expect(isEmpty({})).to.be.true;
213-
expect(isEmpty('')).to.be.true;
214-
expect(isEmpty(0)).to.be.true;
215-
expect(isEmpty(false)).to.be.true;
233+
});
234+
235+
describe('stripEndSlash', () => {
236+
it('should strip end slash if present', () => {
237+
expect(stripEndSlash('/cats/')).to.equal('/cats');
238+
expect(stripEndSlash('/cats')).to.equal('/cats');
216239
});
217240
});
218241

packages/common/utils/shared.utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,12 @@ export const isConstructor = (val: unknown): boolean => val === 'constructor';
5454
export const isNil = (val: unknown): val is null | undefined =>
5555
isUndefined(val) || val === null;
5656

57-
export const isEmpty = (array: unknown): boolean =>
58-
!(Array.isArray(array) && array.length > 0);
57+
export const isEmptyArray = (array: unknown): boolean => {
58+
if (!Array.isArray(array)) {
59+
return false;
60+
}
61+
return array.length === 0;
62+
};
5963

6064
export const isSymbol = (val: unknown): val is symbol =>
6165
typeof val === 'symbol';

packages/core/exceptions/base-exception-filter-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { FILTER_CATCH_EXCEPTIONS } from '@nestjs/common/constants';
22
import { Type } from '@nestjs/common/interfaces';
33
import { ExceptionFilter } from '@nestjs/common/interfaces/exceptions/exception-filter.interface';
4-
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
4+
import { isEmptyArray, isFunction } from '@nestjs/common/utils/shared.utils';
55
import { iterate } from 'iterare';
66
import { ContextCreator } from '../helpers/context-creator';
77
import { STATIC_CONTEXT } from '../injector/constants';
@@ -20,7 +20,7 @@ export class BaseExceptionFilterContext extends ContextCreator {
2020
contextId = STATIC_CONTEXT,
2121
inquirerId?: string,
2222
): R {
23-
if (isEmpty(metadata)) {
23+
if (isEmptyArray(metadata)) {
2424
return [] as any[] as R;
2525
}
2626
return iterate(metadata)

packages/core/exceptions/exceptions-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { HttpException } from '@nestjs/common';
22
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface';
33
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
44
import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util';
5-
import { isEmpty } from '@nestjs/common/utils/shared.utils';
5+
import { isEmptyArray } from '@nestjs/common/utils/shared.utils';
66
import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception';
77
import { BaseExceptionFilter } from './base-exception-filter';
88

@@ -27,7 +27,7 @@ export class ExceptionsHandler extends BaseExceptionFilter {
2727
exception: T,
2828
ctx: ArgumentsHost,
2929
): boolean {
30-
if (isEmpty(this.filters)) {
30+
if (isEmptyArray(this.filters)) {
3131
return false;
3232
}
3333

packages/core/exceptions/external-exception-filter-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EXCEPTION_FILTERS_METADATA } from '@nestjs/common/constants';
22
import { Controller } from '@nestjs/common/interfaces';
33
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions';
4-
import { isEmpty } from '@nestjs/common/utils/shared.utils';
4+
import { isEmptyArray } from '@nestjs/common/utils/shared.utils';
55
import { iterate } from 'iterare';
66
import { ApplicationConfig } from '../application-config';
77
import { STATIC_CONTEXT } from '../injector/constants';
@@ -36,7 +36,7 @@ export class ExternalExceptionFilterContext extends BaseExceptionFilterContext {
3636
contextId,
3737
inquirerId,
3838
);
39-
if (isEmpty(filters)) {
39+
if (isEmptyArray(filters)) {
4040
return exceptionHandler;
4141
}
4242
exceptionHandler.setCustomFilters(filters.reverse());

packages/core/exceptions/external-exceptions-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions';
22
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
33
import { selectExceptionFilterMetadata } from '@nestjs/common/utils/select-exception-filter-metadata.util';
4-
import { isEmpty } from '@nestjs/common/utils/shared.utils';
4+
import { isEmptyArray } from '@nestjs/common/utils/shared.utils';
55
import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception';
66
import { ExternalExceptionFilter } from './external-exception-filter';
77

@@ -27,7 +27,7 @@ export class ExternalExceptionsHandler extends ExternalExceptionFilter {
2727
exception: T,
2828
host: ArgumentsHost,
2929
): Promise<any> | null {
30-
if (isEmpty(this.filters)) {
30+
if (isEmptyArray(this.filters)) {
3131
return null;
3232
}
3333

packages/core/guards/guards-consumer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CanActivate } from '@nestjs/common';
22
import { ContextType, Controller } from '@nestjs/common/interfaces';
3-
import { isEmpty } from '@nestjs/common/utils/shared.utils';
3+
import { isEmptyArray } from '@nestjs/common/utils/shared.utils';
44
import { lastValueFrom, Observable } from 'rxjs';
55
import { ExecutionContextHost } from '../helpers/execution-context-host';
66

@@ -12,7 +12,7 @@ export class GuardsConsumer {
1212
callback: (...args: unknown[]) => unknown,
1313
type?: TContext,
1414
): Promise<boolean> {
15-
if (!guards || isEmpty(guards)) {
15+
if (!guards || isEmptyArray(guards)) {
1616
return true;
1717
}
1818
const context = this.createContext(args, instance, callback);

packages/core/guards/guards-context-creator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CanActivate } from '@nestjs/common';
22
import { GUARDS_METADATA } from '@nestjs/common/constants';
33
import { Controller, Type } from '@nestjs/common/interfaces';
4-
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
4+
import { isEmptyArray, isFunction } from '@nestjs/common/utils/shared.utils';
55
import { iterate } from 'iterare';
66
import { ApplicationConfig } from '../application-config';
77
import { ContextCreator } from '../helpers/context-creator';
@@ -41,7 +41,7 @@ export class GuardsContextCreator extends ContextCreator {
4141
contextId = STATIC_CONTEXT,
4242
inquirerId?: string,
4343
): R {
44-
if (isEmpty(metadata)) {
44+
if (isEmptyArray(metadata)) {
4545
return [] as unknown[] as R;
4646
}
4747
return iterate(metadata)

packages/core/helpers/external-context-creator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Controller,
66
PipeTransform,
77
} from '@nestjs/common/interfaces';
8-
import { isEmpty } from '@nestjs/common/utils/shared.utils';
8+
import { isEmptyArray } from '@nestjs/common/utils/shared.utils';
99
import { isObservable, lastValueFrom } from 'rxjs';
1010
import { ExternalExceptionFilterContext } from '../exceptions/external-exception-filter-context';
1111
import { GuardsConsumer, GuardsContextCreator } from '../guards';
@@ -323,7 +323,7 @@ export class ExternalContextCreator {
323323
{ metatype, type, data }: { metatype: any; type: any; data: any },
324324
pipes: PipeTransform[],
325325
): Promise<any> {
326-
return isEmpty(pipes)
326+
return isEmptyArray(pipes)
327327
? value
328328
: this.pipesConsumer.apply(value, { metatype, type, data }, pipes);
329329
}

0 commit comments

Comments
 (0)