From 7ed5f93daec7de6f2475c6e52849daa786e65dea Mon Sep 17 00:00:00 2001 From: Dmytro Date: Sun, 23 Jun 2024 23:39:46 +0300 Subject: [PATCH] - Resolved [Issue 55](https://github.com/dmitriy-nz/nestjs-form-data/issues/55) - Added test cases for `Exposed` decorated fields --- CHANGELOG.md | 5 +- package.json | 2 +- .../validation/is-file.validator.ts | 8 ++- test/exposed-optional-upload.e2e-spec.ts | 56 +++++++++++++++++++ .../controllers/test.controller.ts | 18 ++++++ ...UploadOptionalExposedFieldArrayFile.dto.ts | 16 ++++++ ...ploadOptionalExposedFieldSingleFile.dto.ts | 16 ++++++ 7 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 test/exposed-optional-upload.e2e-spec.ts create mode 100644 test/test-module/dto/UploadOptionalExposedFieldArrayFile.dto.ts create mode 100644 test/test-module/dto/UploadOptionalExposedFieldSingleFile.dto.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ecee5f..7cd2812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ +### v1.9.91 +- Resolved [Issue 55](https://github.com/dmitriy-nz/nestjs-form-data/issues/55) +- Added test cases for `Exposed` decorated fields + ### v1.9.9 - Resolved [Issue 60](https://github.com/dmitriy-nz/nestjs-form-data/issues/60) - Added test cases for `enableImplicitConversion` field in the `class-validator` transform options - Modified `IsFile` validator to handle `enableImplicitConversion` param - Some other test cases were improved - ### v1.9.8 - Updated `README.md`, clarified `class-validator` pipe configuration diff --git a/package.json b/package.json index b8a1cfd..b7fc5f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nestjs-form-data", - "version": "1.9.9", + "version": "1.9.91", "description": "NestJS middleware for handling multipart/form-data, which is primarily used for uploading files", "main": "dist/index", "types": "dist/index", diff --git a/src/decorators/validation/is-file.validator.ts b/src/decorators/validation/is-file.validator.ts index 8f13a9f..c747460 100644 --- a/src/decorators/validation/is-file.validator.ts +++ b/src/decorators/validation/is-file.validator.ts @@ -33,10 +33,14 @@ export function IsFile(validationOptions?: ValidationOptions): PropertyDecorator if(validationOptions?.each){ return applyDecorators( + // Force transform to an array of files then we have each mode and only one file in the field Transform((params: TransformFnParams) => { - if(!Array.isArray(params.value)){ - return [ params.value ]; + // If value isn't array and isn't empty, arraying it + // We can get empty value, do nothing in this case + if(!Array.isArray(params.value) && params.value){ + return [ params.value ] } + return params.value; }), transformEnableImplicitConversion, diff --git a/test/exposed-optional-upload.e2e-spec.ts b/test/exposed-optional-upload.e2e-spec.ts new file mode 100644 index 0000000..4416104 --- /dev/null +++ b/test/exposed-optional-upload.e2e-spec.ts @@ -0,0 +1,56 @@ +import { INestApplication } from '@nestjs/common'; +import { NestFastifyApplication } from '@nestjs/platform-fastify'; +import * as request from 'supertest'; +import { createTestModule } from './helpers/create-test-module'; + + +// https://github.com/dmitriy-nz/nestjs-form-data/issues/55 +describe('Express - Exposed optional upload', () => { + let app: INestApplication; + + beforeEach(async () => { + app = await createTestModule(); + }); + + it('Upload optional exposed field single file', () => { + return request + .default(app.getHttpServer()) + .post('/optional-exposed-single') + .expect(200) + .expect({}); + }); + + it('Upload optional exposed field array files', () => { + return request + .default(app.getHttpServer()) + .post('/optional-exposed-array') + .expect(200) + .expect({}); + }); +}); + +describe('Fastify - Exposed optional upload', () => { + let app: NestFastifyApplication; + + beforeEach(async () => { + app = (await createTestModule({ + fastify: true, + })) as NestFastifyApplication; + }); + + it('Upload optional exposed field single file', () => { + return request + .default(app.getHttpServer()) + .post('/optional-exposed-single') + .expect(200) + .expect({}); + }); + + it('Upload optional exposed field array files', () => { + return request + .default(app.getHttpServer()) + .post('/optional-exposed-array') + .expect(200) + .expect({}); + }); +}); diff --git a/test/test-module/controllers/test.controller.ts b/test/test-module/controllers/test.controller.ts index b3e799d..12663e8 100644 --- a/test/test-module/controllers/test.controller.ts +++ b/test/test-module/controllers/test.controller.ts @@ -9,6 +9,8 @@ import { MimeTypeValidatorDto } from '../dto/MimeTypeValidator.dto'; import { UploadOptionalFileDto } from '../dto/UploadOptionalFile.dto'; import { CustomErrorSingleDto } from '../dto/CustomErrorSingle.dto'; import { CustomErrorArrayDto } from '../dto/CustomErrorArray.dto'; +import { UploadOptionalExposedFieldSingleFileDto } from '../dto/UploadOptionalExposedFieldSingleFile.dto'; +import { UploadOptionalExposedFieldArrayFileDto } from '../dto/UploadOptionalExposedFieldArrayFile.dto'; @Controller('') export class TestController { @@ -99,6 +101,22 @@ export class TestController { return dto; } + @Post('optional-exposed-single') + @UsePipes(new ValidationPipe({transform: true})) + @FormDataRequest() + @HttpCode(HttpStatus.OK) + optionalExposedFieldSingleFile(@Body() dto: UploadOptionalExposedFieldSingleFileDto) { + return dto; + } + + @Post('optional-exposed-array') + @UsePipes(new ValidationPipe({transform: true})) + @FormDataRequest() + @HttpCode(HttpStatus.OK) + optionalExposedFieldArrayFiles(@Body() dto: UploadOptionalExposedFieldArrayFileDto) { + return dto; + } + @Post('custom-error-single') @UsePipes(new ValidationPipe({transform: true})) @FormDataRequest() diff --git a/test/test-module/dto/UploadOptionalExposedFieldArrayFile.dto.ts b/test/test-module/dto/UploadOptionalExposedFieldArrayFile.dto.ts new file mode 100644 index 0000000..1f58b1a --- /dev/null +++ b/test/test-module/dto/UploadOptionalExposedFieldArrayFile.dto.ts @@ -0,0 +1,16 @@ +import { MemoryStoredFile } from '../../../src/classes/storage'; +import { HasMimeType, IsFiles, MaxFileSize, MinFileSize } from '../../../src/decorators'; +import { IsOptional } from 'class-validator'; +import { Expose } from 'class-transformer'; + +export class UploadOptionalExposedFieldArrayFileDto { + + @Expose() + @IsFiles() + @HasMimeType(['text/plain'], { each: true }) + @MaxFileSize(5, { each: true }) + @MinFileSize(3, { each: true }) + @IsOptional() + file?: MemoryStoredFile[]; + +} diff --git a/test/test-module/dto/UploadOptionalExposedFieldSingleFile.dto.ts b/test/test-module/dto/UploadOptionalExposedFieldSingleFile.dto.ts new file mode 100644 index 0000000..d82b951 --- /dev/null +++ b/test/test-module/dto/UploadOptionalExposedFieldSingleFile.dto.ts @@ -0,0 +1,16 @@ +import { MemoryStoredFile } from '../../../src/classes/storage'; +import { HasMimeType, IsFile, MaxFileSize, MinFileSize } from '../../../src/decorators'; +import { IsOptional } from 'class-validator'; +import { Expose } from 'class-transformer'; + +export class UploadOptionalExposedFieldSingleFileDto { + + @Expose() + @IsFile() + @HasMimeType(['text/plain']) + @MaxFileSize(5) + @MinFileSize(3) + @IsOptional() + file?: MemoryStoredFile; + +}