Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ dist
.yarnrc.yml.temp

# rollup
.rollup.cache
.rollup.cache

# test
coverage
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# shared

[![Test Coverage](https://img.shields.io/badge/coverage-38.11%25-yellow.svg)](https://github.com/ummgoban/shared)

ummgoban 공통 패키지입니다.

- ummgoban 도메인에서 공용으로 사용하는 type, utils, http client, hook 등을 관리합니다.
Expand All @@ -14,12 +16,9 @@ yarn add @ummgoban/shared
# exports

- `@ummgoban/shared`
- `@ummgoban/shared/lib`
- `@ummgoban/shared/network`
- `@ummgoban/shared/react`
- `@ummgoban/shared/types`
- `@ummgoban/shared/utils`
- `@ummgoban/shared/http`
- `@ummgoban/shared/http/error`
- `@ummgoban/shared/http/api-client`

# dev

Expand All @@ -33,9 +32,24 @@ NPM_TOKEN=ghp_...
## test

```bash
yarn test
yarn test # 테스트 실행
yarn test:watch # 테스트 실시간 감시 모드
yarn test:coverage # 테스트 커버리지 보고서 생성
```

## 테스트 커버리지

현재 프로젝트의 테스트 커버리지는 38.11%입니다. 커버리지 보고서는 `coverage` 디렉토리에서 확인할 수 있습니다.

```bash
yarn test:coverage # 커버리지 보고서 생성 후 coverage/index.html 확인
```

커버리지 보고서에서 제외된 파일:
- `**/index.ts` 파일들
- 테스트 파일들 (`*.spec.ts`, `*.test.ts`)
- 타입 정의 파일들 (`*.d.ts`)

## dev

```bash
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"ci": "./.script/ci.sh",
"build": "rm -rf dist && rollup -c",
"test": "vitest run",
"test:watch": "vitest watch",
"test:coverage": "vitest run --coverage",
"dev": "vite",
"type-check": "tsc --noEmit",
"lint": "eslint src --ext .ts,.tsx --fix",
Expand Down Expand Up @@ -66,6 +68,7 @@
"@typescript-eslint/eslint-plugin": "^8.23.0",
"@typescript-eslint/parser": "^8.23.0",
"@vitejs/plugin-react": "^4.5.0",
"@vitest/coverage-v8": "3.1.4",
"axios": "^1.7.4",
"dotenv": "^16.4.7",
"eslint": "^9.27.0",
Expand Down
16 changes: 16 additions & 0 deletions src/network/error/http-error.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {CustomError} from './http-error';

describe('CustomError', () => {
it('should create an instance with error message ', () => {
const error = new CustomError(new Error('test error message'));
expect(error).toBeDefined();
expect(error.message).toBe('test error message');
expect(error.errorMessage).toBe('test error message');
});
it('should create an instance with default error message ', () => {
const error = new CustomError(new Error());
expect(error).toBeDefined();
expect(error.message).toBe('서버에서 오류가 발생했어요. 문제가 지속된다면 문의주세요');
expect(error.errorMessage).toBe('서버에서 오류가 발생했어요. 문제가 지속된다면 문의주세요');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export class CustomError<T = unknown> extends Error {
errorMessage?: string;

constructor(args: CustomErrorType<T>) {
if (args instanceof Error) {
if (args instanceof Error && args.message) {
super(args.message);
this.errorMessage = args.message;
} else {
super('서버에서 오류가 발생했어요. 문제가 지속된다면 문의주세요');
this.errorMessage = '서버에서 오류가 발생했어요. 문제가 지속된다면 문의주세요';
Expand Down
2 changes: 1 addition & 1 deletion src/network/error/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// http error
export * from './http-error.type';
export * from './http-error';
1 change: 1 addition & 0 deletions src/react/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-pull-down-refresh';
3 changes: 3 additions & 0 deletions src/react/hooks/use-pull-down-refresh/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import usePullDownRefresh from './use-pull-down-refresh';

export {usePullDownRefresh};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import usePullDownRefresh from './use-pull-down-refresh';

import {act, renderHook} from '@testing-library/react';

describe('usePullDownRefresh', () => {
let callbackPromise: ReturnType<typeof vi.fn>;

beforeEach(() => {
callbackPromise = vi.fn().mockResolvedValue(undefined);
});

it('should call callback when onRefresh is called', async () => {
const {result} = renderHook(() => usePullDownRefresh(callbackPromise));

await act(async () => {
await result.current.onRefresh();
});

expect(callbackPromise).toHaveBeenCalledTimes(1);
});

it('should set refreshing to true while refreshing and false when done', async () => {
// 콜백 함수가 해결될 때까지 기다리는 프로미스 생성
let resolveCallback: () => void;
const waitForCallback = new Promise<void>(resolve => {
resolveCallback = resolve;
});

callbackPromise = vi.fn().mockImplementation(() => waitForCallback);

const {result} = renderHook(() => usePullDownRefresh(callbackPromise));

// 초기 상태 확인
expect(result.current.refreshing).toBe(false);

// onRefresh 호출 시작
let onRefreshPromise: Promise<void>;
act(() => {
onRefreshPromise = result.current.onRefresh();
});

// refreshing 상태가 true로 변경되었는지 확인
expect(result.current.refreshing).toBe(true);

// 콜백 함수 완료
act(() => {
resolveCallback();
});

// onRefresh가 완료될 때까지 기다림
await act(async () => {
await onRefreshPromise;
});

// refreshing 상태가 false로 돌아왔는지 확인
expect(result.current.refreshing).toBe(false);
});

it('should handle errors and set refreshing to false', async () => {
// 에러를 발생시키는 콜백 함수
const error = new Error('Test error');
callbackPromise = vi.fn().mockRejectedValue(error);

// 콘솔 에러 모니터링
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

const {result} = renderHook(() => usePullDownRefresh(callbackPromise));

await act(async () => {
await result.current.onRefresh();
});

// 에러가 콘솔에 기록되었는지 확인
expect(consoleSpy).toHaveBeenCalledWith(error);

// 에러가 발생해도 refreshing 상태가 false로 돌아왔는지 확인
expect(result.current.refreshing).toBe(false);

// 스파이 정리
consoleSpy.mockRestore();
});
});
21 changes: 21 additions & 0 deletions src/react/hooks/use-pull-down-refresh/use-pull-down-refresh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {useCallback, useState} from 'react';

const usePullDownRefresh = (callback: () => Promise<void>) => {
const [refreshing, setRefreshing] = useState(false);

const onRefresh = useCallback(async () => {
setRefreshing(true);

try {
await callback();
} catch (reason) {
console.error(reason);
} finally {
setRefreshing(false);
}
}, [callback]);

return {refreshing, onRefresh};
};

export default usePullDownRefresh;
1 change: 1 addition & 0 deletions src/react/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './hooks';
export * from './provider';
2 changes: 1 addition & 1 deletion src/react/provider/combine-provider.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createContext, useContext} from 'react';

import {render, screen} from '@testing-library/react';
import {describe, it, expect} from 'vitest';

import {combineProviders, ProviderEntry, Providers} from './combine-provider';

Expand Down
11 changes: 11 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ export default defineConfig({
setupFiles: './vitest.setup.ts',
coverage: {
reporter: ['text', 'json', 'html'],
exclude: [
'**/index.ts',
'node_modules/**',
'.yarn/**',
'dist/**',
'**/*.d.ts',
'**/*.type.ts',
'**/*.config.*',
'**/*.{test,spec}.{js,jsx,ts,tsx}',
'__tests__/**',
],
},
},
});
Loading
Loading