From 95daa2d1e14a11f51efe34889cc3d536f837bb87 Mon Sep 17 00:00:00 2001 From: ATeals Date: Mon, 2 Dec 2024 18:14:15 +0900 Subject: [PATCH 1/5] =?UTF-8?q?test(#194):=20=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20UserModel=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/frontend/src/feature/user/model/index.ts | 1 + .../src/feature/user/model/model.test.ts | 45 +++++++++++++++++++ .../src/feature/user/{ => model}/model.ts | 0 3 files changed, 46 insertions(+) create mode 100644 apps/frontend/src/feature/user/model/index.ts create mode 100644 apps/frontend/src/feature/user/model/model.test.ts rename apps/frontend/src/feature/user/{ => model}/model.ts (100%) diff --git a/apps/frontend/src/feature/user/model/index.ts b/apps/frontend/src/feature/user/model/index.ts new file mode 100644 index 00000000..9f8ccadd --- /dev/null +++ b/apps/frontend/src/feature/user/model/index.ts @@ -0,0 +1 @@ +export * from './model'; diff --git a/apps/frontend/src/feature/user/model/model.test.ts b/apps/frontend/src/feature/user/model/model.test.ts new file mode 100644 index 00000000..187e6dc9 --- /dev/null +++ b/apps/frontend/src/feature/user/model/model.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; +import { UserModel } from './model'; // 경로는 실제 파일 경로로 변경 + +describe('UserModel', () => { + it.each([ + { + description: '올바른 UserDto를 전달받으면 UserModel을 생성한다', + userDto: { + id: '123', + nickname: 'testUser', + profile: 'https://example.com/profile.png', + gistUrl: 'https://gist.github.com/testUser' + }, + expected: { + id: '123', + nickname: 'testUser', + profile: 'https://example.com/profile.png', + gistUrl: 'https://gist.github.com/testUser' + } + }, + { + description: 'profile이 비어있는 UserDto를 전달받으면 기본 이미지를 사용하는 UserModel을 생성한다', + userDto: { + id: '456', + nickname: 'defaultProfileUser', + profile: '', // 비어있는 값 + gistUrl: 'https://gist.github.com/defaultProfileUser' + }, + expected: { + id: '456', + nickname: 'defaultProfileUser', + profile: '/image/logoIcon.svg', // 기본값 + gistUrl: 'https://gist.github.com/defaultProfileUser' + } + } + ])('$description', ({ userDto, expected }) => { + //Given + const userModel = new UserModel(userDto); + + //When + + //Then + expect(userModel).toEqual(expected); + }); +}); diff --git a/apps/frontend/src/feature/user/model.ts b/apps/frontend/src/feature/user/model/model.ts similarity index 100% rename from apps/frontend/src/feature/user/model.ts rename to apps/frontend/src/feature/user/model/model.ts From 05a4ce2e8394f80326398b2ff31430bc351b19e2 Mon Sep 17 00:00:00 2001 From: ATeals Date: Mon, 2 Dec 2024 18:14:26 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor(#194):=20=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20pagination=20type=EC=97=90=EC=84=9C=20mode?= =?UTF-8?q?l=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/frontend/src/feature/pagination/index.ts | 2 +- apps/frontend/src/feature/pagination/{type.ts => model.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/frontend/src/feature/pagination/{type.ts => model.ts} (100%) diff --git a/apps/frontend/src/feature/pagination/index.ts b/apps/frontend/src/feature/pagination/index.ts index 601b0749..31566ded 100644 --- a/apps/frontend/src/feature/pagination/index.ts +++ b/apps/frontend/src/feature/pagination/index.ts @@ -1,3 +1,3 @@ export * from './usePagination'; export * from './Pagination'; -export * from './type'; +export * from './model'; diff --git a/apps/frontend/src/feature/pagination/type.ts b/apps/frontend/src/feature/pagination/model.ts similarity index 100% rename from apps/frontend/src/feature/pagination/type.ts rename to apps/frontend/src/feature/pagination/model.ts From fde680ab5bff69d57103172c9049300e9e7b4045 Mon Sep 17 00:00:00 2001 From: ATeals Date: Mon, 2 Dec 2024 18:14:35 +0900 Subject: [PATCH 3/5] =?UTF-8?q?test(#194):=20=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20LotusModel=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/feature/lotus/model/index.ts | 1 + .../src/feature/lotus/model/model.test.ts | 93 +++++++++++++++++++ .../src/feature/lotus/{ => model}/model.ts | 0 3 files changed, 94 insertions(+) create mode 100644 apps/frontend/src/feature/lotus/model/index.ts create mode 100644 apps/frontend/src/feature/lotus/model/model.test.ts rename apps/frontend/src/feature/lotus/{ => model}/model.ts (100%) diff --git a/apps/frontend/src/feature/lotus/model/index.ts b/apps/frontend/src/feature/lotus/model/index.ts new file mode 100644 index 00000000..9f8ccadd --- /dev/null +++ b/apps/frontend/src/feature/lotus/model/index.ts @@ -0,0 +1 @@ +export * from './model'; diff --git a/apps/frontend/src/feature/lotus/model/model.test.ts b/apps/frontend/src/feature/lotus/model/model.test.ts new file mode 100644 index 00000000..0a2a0e81 --- /dev/null +++ b/apps/frontend/src/feature/lotus/model/model.test.ts @@ -0,0 +1,93 @@ +import { describe, expect, it } from 'vitest'; +import { LotusDto, LotusModel } from './model'; + +describe('LotusModel', () => { + it.each([ + { + description: '올바른 LotusDto로 LotusModel 생성할 수 있다.', + lotusDto: { + id: '1', + link: 'https://example.com', + title: 'Example Title', + logo: 'https://example.com/logo.png', + date: '2024-01-01T00:00:00.000Z', + tags: ['tag1', 'tag2'], + isPublic: true, + gistUrl: 'https://gist.github.com/example' + }, + expected: { + id: '1', + link: 'https://example.com', + title: 'Example Title', + logo: 'https://example.com/logo.png', + date: new Date('2024-01-01T00:00:00.000Z'), + tags: ['tag1', 'tag2'], + isPublic: true, + gistUrl: 'https://gist.github.com/example', + isTagsEmpty: false + } + }, + { + description: '태그가 없는 LotusDto로 LotusModel 생성할 수 있다.', + lotusDto: { + id: '2', + link: 'https://example.com/2', + title: 'Another Title', + logo: 'https://example.com/logo2.png', + date: '2024-02-01T00:00:00.000Z', + tags: [], + gistUrl: 'https://gist.github.com/example2' + }, + expected: { + id: '2', + link: 'https://example.com/2', + title: 'Another Title', + logo: 'https://example.com/logo2.png', + date: new Date('2024-02-01T00:00:00.000Z'), + tags: [], + isPublic: undefined, + gistUrl: 'https://gist.github.com/example2', + isTagsEmpty: true + } + } + ])('$description', ({ lotusDto, expected }) => { + //Given + const lotusModel = new LotusModel(lotusDto); + + //When + + //Then + expect(lotusModel).toMatchObject(expected); + expect(lotusModel.isTagsEmpty).toBe(expected.isTagsEmpty); + }); + + it('clone메서드를 통해 새로운 LotusModel 객체를 만들 수 있다.', () => { + //Given + const originalDto: LotusDto = { + id: '3', + link: 'https://example.com/3', + title: 'Original Title', + logo: 'https://example.com/logo3.png', + date: '2024-03-01T00:00:00.000Z', + tags: ['original'], + isPublic: false, + gistUrl: 'https://gist.github.com/original' + }; + const originalModel = new LotusModel(originalDto); + const cloneDto = { + title: 'Updated Title', + tags: ['updated'], + date: new Date('2024-04-01T00:00:00.000Z') + }; + + // When + const clonedModel = originalModel.clone(cloneDto); + + // Then + expect(new LotusModel(originalDto)).toMatchObject(originalModel); + + expect(clonedModel).toMatchObject( + new LotusModel({ ...originalDto, ...cloneDto, date: cloneDto.date.toISOString() }) + ); + }); +}); diff --git a/apps/frontend/src/feature/lotus/model.ts b/apps/frontend/src/feature/lotus/model/model.ts similarity index 100% rename from apps/frontend/src/feature/lotus/model.ts rename to apps/frontend/src/feature/lotus/model/model.ts From 80defd9257b8c0dc8425947d0fb6da63d158e4c3 Mon Sep 17 00:00:00 2001 From: ATeals Date: Mon, 2 Dec 2024 18:14:46 +0900 Subject: [PATCH 4/5] =?UTF-8?q?test(#194):=20=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20HistoryModel=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/feature/history/model/index.ts | 1 + .../src/feature/history/model/model.test.ts | 125 ++++++++++++++++++ .../src/feature/history/{ => model}/model.ts | 4 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 apps/frontend/src/feature/history/model/index.ts create mode 100644 apps/frontend/src/feature/history/model/model.test.ts rename apps/frontend/src/feature/history/{ => model}/model.ts (89%) diff --git a/apps/frontend/src/feature/history/model/index.ts b/apps/frontend/src/feature/history/model/index.ts new file mode 100644 index 00000000..9f8ccadd --- /dev/null +++ b/apps/frontend/src/feature/history/model/index.ts @@ -0,0 +1 @@ +export * from './model'; diff --git a/apps/frontend/src/feature/history/model/model.test.ts b/apps/frontend/src/feature/history/model/model.test.ts new file mode 100644 index 00000000..49b1b50c --- /dev/null +++ b/apps/frontend/src/feature/history/model/model.test.ts @@ -0,0 +1,125 @@ +import { describe, expect, it } from 'vitest'; +import { HistoryModel } from './model'; +import { HISTORY_STATUS } from '@/feature/history/constant'; + +describe('HistoryModel', () => { + it.each([ + { + description: '올바른 DTO를 받아 HistoryModel을 생성합니다.', + dto: { + id: '1', + status: HISTORY_STATUS.SUCCESS, + date: '2024-01-01T00:00:00.000Z', + input: 'some input', + output: 'some output', + filename: 'file1.txt' + }, + expected: { + id: '1', + status: HISTORY_STATUS.SUCCESS, + date: new Date('2024-01-01T00:00:00.000Z'), + input: 'some input', + output: 'some output', + filename: 'file1.txt' + } + }, + { + description: '잘못된 status를 받아 HistoryModel을 생성하면 status는 ERROR로 설정됩니다.', + dto: { + id: '2', + status: 'INVALID_STATUS', + date: '2024-02-01T00:00:00.000Z', + input: 'input2', + output: 'output2', + filename: 'file2.txt' + }, + expected: { + id: '2', + status: HISTORY_STATUS.ERROR, + date: new Date('2024-02-01T00:00:00.000Z'), + input: 'input2', + output: 'output2', + filename: 'file2.txt' + } + } + ])('$description', ({ dto, expected }) => { + // Given + const historyModel = new HistoryModel(dto); + + // Then: 객체 속성 검증 + expect(historyModel).toMatchObject(expected); + }); + + it('getPendingHistoriesId는 HistoryModel[]을 인자로 받아 PENDING 상태인 HistoryId를 반환합니다.', () => { + // Given + const expected = ['1', '3']; + const histories = [ + new HistoryModel({ + id: '1', + status: HISTORY_STATUS.PENDING, + date: '2024-01-01T00:00:00.000Z', + input: 'input1', + output: 'output1', + filename: 'file1.txt' + }), + new HistoryModel({ + id: '2', + status: HISTORY_STATUS.SUCCESS, + date: '2024-02-01T00:00:00.000Z', + input: 'input2', + output: 'output2', + filename: 'file2.txt' + }), + new HistoryModel({ + id: '3', + status: HISTORY_STATUS.PENDING, + date: '2024-03-01T00:00:00.000Z', + input: 'input3', + output: 'output3', + filename: 'file3.txt' + }) + ]; + + // When + const pendingIds = HistoryModel.getPendingHistoriesId(histories); + + // Then + expect(pendingIds).toEqual(expected); + }); + + it.each([ + { + history: new HistoryModel({ + id: '1', + status: HISTORY_STATUS.SUCCESS, + date: '2024-01-01T00:00:00.000Z', + input: 'input1', + output: 'output1', + filename: 'file1.txt' + }), + status: HISTORY_STATUS.SUCCESS, + expected: true + }, + { + history: new HistoryModel({ + id: '2', + status: HISTORY_STATUS.ERROR, + date: '2024-02-01T00:00:00.000Z', + input: 'input2', + output: 'output2', + filename: 'file2.txt' + }), + status: HISTORY_STATUS.PENDING, + expected: false + } + ])( + 'isStatus를 통해 history의 status를 검증할 수 있습니다. ($history.status 일때 history.isStatus($status)는 $expected 입니다.)', + ({ history, status, expected }) => { + //Given + //When + + //Then + expect(history.isStatus(status)).toBe(expected); + } + ); +}); diff --git a/apps/frontend/src/feature/history/model.ts b/apps/frontend/src/feature/history/model/model.ts similarity index 89% rename from apps/frontend/src/feature/history/model.ts rename to apps/frontend/src/feature/history/model/model.ts index 14689f1a..f8b16081 100644 --- a/apps/frontend/src/feature/history/model.ts +++ b/apps/frontend/src/feature/history/model/model.ts @@ -1,5 +1,5 @@ -import { HISTORY_STATUS } from './constant'; -import { HistoryStatus } from './type'; +import { HISTORY_STATUS } from '@/feature/history/constant'; +import { HistoryStatus } from '@/feature/history/type'; export interface HistoryDto { id: string; From 26f2f3850fe921498ea8809bd451050f7da0299f Mon Sep 17 00:00:00 2001 From: ATeals Date: Mon, 2 Dec 2024 18:14:57 +0900 Subject: [PATCH 5/5] =?UTF-8?q?test(#194):=20=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20CodeViewModel=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/feature/codeView/model/index.ts | 1 + .../src/feature/codeView/model/model.test.ts | 193 ++++++++++++++++++ .../src/feature/codeView/{ => model}/model.ts | 2 +- 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/src/feature/codeView/model/index.ts create mode 100644 apps/frontend/src/feature/codeView/model/model.test.ts rename apps/frontend/src/feature/codeView/{ => model}/model.ts (93%) diff --git a/apps/frontend/src/feature/codeView/model/index.ts b/apps/frontend/src/feature/codeView/model/index.ts new file mode 100644 index 00000000..9f8ccadd --- /dev/null +++ b/apps/frontend/src/feature/codeView/model/index.ts @@ -0,0 +1 @@ +export * from './model'; diff --git a/apps/frontend/src/feature/codeView/model/model.test.ts b/apps/frontend/src/feature/codeView/model/model.test.ts new file mode 100644 index 00000000..b95995b9 --- /dev/null +++ b/apps/frontend/src/feature/codeView/model/model.test.ts @@ -0,0 +1,193 @@ +import { describe, expect, it } from 'vitest'; +import { CodeFileModel } from './model'; + +describe('CodeFileModel', () => { + it.each([ + { + description: '올바른 DTO를 받아 CodeFileModel을 생성합니다.', + dto: { + filename: 'example.js', + language: 'JavaScript', + content: 'console.log("Hello, world!");' + }, + expected: { + filename: 'example.js', + language: 'JavaScript', + content: 'console.log("Hello, world!");', + ext: 'js' + } + }, + { + description: '비어있는 값은 빈 값으로 처리합니다.', + dto: { + filename: '', + language: '', + content: '' + }, + expected: { + filename: '', + language: '', + content: '', + ext: '' + } + } + ])('$description', ({ dto, expected }) => { + // Given + const model = new CodeFileModel(dto); + + //When + + // Then: 필드 검증 + expect(model).toMatchObject(expected); + }); + + it.each([ + { + description: 'README 파일일 경우 isREADME가 true입니다.', + dto: { + filename: 'README.md', + language: 'Markdown', + content: '# Hello World' + }, + expected: true + }, + { + description: 'README 파일이 아닐 경우 isREADME가 false입니다.', + dto: { + filename: 'example.js', + language: 'JavaScript', + content: 'console.log("Hello, world!");' + }, + expected: false + } + ])('$description', ({ dto, expected }) => { + // Given + const model = new CodeFileModel(dto); + + //When + + // Then: isREADME getter 확인 + expect(model.isREADME).toBe(expected); + }); + + it.each([ + { + description: '마크다운 파일인 경우 isMarkdown가 true입니다.', + dto: { + filename: 'example.md', + language: 'Markdown', + content: '# Hello World' + }, + expected: true + }, + { + description: '마크다운 파일이 아닌 경우 isMarkdown가 false입니다.', + dto: { + filename: 'example.js', + language: 'JavaScript', + content: 'console.log("Hello, world!");' + }, + expected: false + } + ])('$description', ({ dto, expected }) => { + // Given + const model = new CodeFileModel(dto); + + //When + + // Then: isMarkdown getter 확인 + expect(model.isMarkdown).toBe(expected); + }); + + it.each([ + { + description: '지원하는 확장자 형식인 경우 canView가 true입니다.', + dto: { + filename: 'example.md', + language: 'Markdown', + content: '# Hello World' + }, + expected: true + }, + { + description: '지원하지 않는 확장자의 경우 canView가 false입니다.', + dto: { + filename: 'example.png', + language: 'Binary', + content: '' + }, + expected: false + } + ])('$description', ({ dto, expected }) => { + // Given + const model = new CodeFileModel(dto); + + //When + + // Then: canView getter 확인 + expect(model.canView).toBe(expected); + }); + + it.each([ + { + description: 'README 파일이 존재하는 경우 getDefaultFile은 README 파일을 반환합니다.', + files: [ + new CodeFileModel({ filename: 'README.md', language: 'Markdown', content: '# Hello' }), + new CodeFileModel({ filename: 'example.js', language: 'JavaScript', content: 'console.log("test");' }), + new CodeFileModel({ filename: 'example.md', language: 'Markdown', content: 'hola' }) + ], + expected: 'README.md' + }, + { + description: 'README 파일이 없고 마크다운 파일이 존재하는 경우 getDefaultFile은 마크다운 파일을 반환합니다.', + files: [ + new CodeFileModel({ filename: 'example.md', language: 'Markdown', content: '# Hello' }), + new CodeFileModel({ filename: 'example.js', language: 'JavaScript', content: 'console.log("test");' }) + ], + expected: 'example.md' + }, + { + description: 'README 파일과 마크다운 파일이 없는 경우 getDefaultFile은 undefined를 반환합니다.', + files: [ + new CodeFileModel({ filename: 'example.js', language: 'JavaScript', content: 'console.log("test");' }), + new CodeFileModel({ filename: 'example.py', language: 'Python', content: 'print("Hello")' }) + ], + expected: undefined + } + ])('$description', ({ files, expected }) => { + //Given + + //When + const defaultFile = CodeFileModel.getDefaultFile(files); + + //Then + expect(defaultFile?.filename).toBe(expected); + }); + + it.each([ + { + description: '지원하는 언어파일인 경우 언어에 맞는 content를 마크다운 문자열로 반환합니다', + dto: { + filename: 'example.js', + language: 'JavaScript', + content: 'console.log("Hello, world!");' + }, + expected: '```js\nconsole.log("Hello, world!");\n ```' + }, + { + description: '지원하지 않는 언어 파일이나, 언어 파일이 아닌 경우 그냥 content를 반환합니다', + dto: { + filename: 'example.txt', + language: 'Text', + content: 'Just some text' + }, + expected: 'Just some text' + } + ])('$description', ({ dto, expected }) => { + // Given + const model = new CodeFileModel(dto); + + // Then: toMarkdown 확인 + expect(model.toMarkdown()).toBe(expected); + }); +}); diff --git a/apps/frontend/src/feature/codeView/model.ts b/apps/frontend/src/feature/codeView/model/model.ts similarity index 93% rename from apps/frontend/src/feature/codeView/model.ts rename to apps/frontend/src/feature/codeView/model/model.ts index e15d6bd6..063dbea6 100644 --- a/apps/frontend/src/feature/codeView/model.ts +++ b/apps/frontend/src/feature/codeView/model/model.ts @@ -1,4 +1,4 @@ -import { CANT_VIEW_EXT, LANGUAGES_EXT } from './constant'; +import { CANT_VIEW_EXT, LANGUAGES_EXT } from '@/feature/codeView/constant'; export interface CodeFileDto { filename: string;