Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/sanghunlee-711/iron-mate int…
Browse files Browse the repository at this point in the history
…o main
  • Loading branch information
cloud committed Oct 22, 2023
2 parents 9c3dbec + 8388fa7 commit d29e205
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 28 deletions.
121 changes: 121 additions & 0 deletions src/app/class/__test__/storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { STORAGE } from '@/app/constants/message';
import CustomAlert from '@/app/utils/alert';
import DataStorage from '../storage';

class MockJSON {
originStringify = JSON.stringify;
originParse = JSON.parse;

mockStringify(mockValue: any) {
JSON.stringify = mockValue;
}

mockParse(mockValue: any) {
JSON.parse = mockValue;
}

clearMock() {
JSON.stringify = this.originStringify;
JSON.parse = this.originParse;
}
}

const MOCK = {
DATA: {
KEY: 'test-key',
VALUE: 'test-value',
},
};

describe('DataStorage클래스 테스트', () => {
const setup = () => {
const customAlert = new CustomAlert();
const dataStorage = new DataStorage(customAlert);
const mockJSON = new MockJSON();

jest.spyOn(customAlert, 'toast');

return {
dataStorage,
customAlert,
mockJSON,
};
};

beforeEach(() => {
window.localStorage.clear();
});

describe('set > 데이터 저장', () => {
it('올바르지 않은 데이터 형태의 경우 alert를 발생시켜야 한다.', () => {
const { dataStorage, customAlert } = setup();
dataStorage.set(MOCK.DATA.KEY, null);

expect(customAlert.toast).toHaveBeenCalledWith(STORAGE.INCORRECT_DATA);
});

it('알 수 없는 이유로 데이터 저장에 실패한 경우 alert를 발생시켜야 한다.', () => {
const { dataStorage, customAlert, mockJSON } = setup();

mockJSON.mockStringify(
jest.fn(() => {
throw new Error();
})
);

dataStorage.set(MOCK.DATA.KEY, MOCK.DATA.VALUE);
expect(customAlert.toast).toHaveBeenCalledWith(STORAGE.SAVE_FAILURE);
mockJSON.clearMock();
});

it('데이터 저장에 성공한 경우 alert를 발생시켜야 한다.', () => {
const { dataStorage, customAlert } = setup();

dataStorage.set(MOCK.DATA.KEY, MOCK.DATA.VALUE);

expect(customAlert.toast).toHaveBeenCalledWith(STORAGE.SAVE_SUCCESS);
});
});

describe('get > 데이터 가져오기', () => {
it('key에 해당하는 데이터가 없는 경우 null을 반환해준다.', () => {
const { dataStorage } = setup();

expect(dataStorage.get(MOCK.DATA.KEY)).toBeNull();
});

it('key에 해당하는 데이터가 있는 경우 값을 반환해준다.', () => {
const { dataStorage } = setup();

dataStorage.set(MOCK.DATA.KEY, MOCK.DATA.VALUE);
expect(dataStorage.get(MOCK.DATA.KEY)).toBe(MOCK.DATA.VALUE);
});

it('알 수 없는 이유로 데이터 가져오기에 실패한 경우 alert를 발생시켜야 한다.', () => {
const { dataStorage, customAlert, mockJSON } = setup();

mockJSON.mockParse(
jest.fn(() => {
throw new Error();
})
);

dataStorage.set(MOCK.DATA.KEY, MOCK.DATA.VALUE);
dataStorage.get(MOCK.DATA.KEY);

expect(customAlert.toast).toHaveBeenCalledWith(STORAGE.GET_FAILURE);
mockJSON.clearMock();
});
});

describe('remove > 데이터 삭제', () => {
it('데이터 삭제 완료 후 alert를 발생시킨다.', () => {
const { dataStorage, customAlert } = setup();

dataStorage.set(MOCK.DATA.KEY, MOCK.DATA.VALUE);
dataStorage.remove(MOCK.DATA.KEY);

expect(customAlert.toast).toHaveBeenCalledWith(STORAGE.REMOVE_SUCCESS);
});
});
});
50 changes: 50 additions & 0 deletions src/app/class/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { STORAGE } from '../constants/message';
import { IAlert } from '../interface/alert';
import CustomAlert from '../utils/alert';

class DataStorage {
customAlert: IAlert;

constructor(alertInstance: IAlert = new CustomAlert()) {
this.customAlert = alertInstance;
}

set(key: string = 'iron-mate-data', data: any) {
try {
if (!data || !key) throw new Error(STORAGE.INCORRECT_DATA);

global?.window?.localStorage.setItem(key, JSON.stringify(data));
this.customAlert.toast(STORAGE.SAVE_SUCCESS);
} catch (e) {
const error = e as Error;
this.customAlert.toast(error.message || STORAGE.SAVE_FAILURE);
console.error(e);
}
}

get(key: string = 'iron-mate-data') {
try {
const keyData = global?.window?.localStorage.getItem(key) || '';

if (!keyData) return null;

const data = JSON.parse(keyData as string);
return data;
} catch (e) {
this.customAlert.toast(STORAGE.GET_FAILURE);
console.error(e);
}
}

remove(key: string = 'iron-mate-data') {
try {
global?.window?.localStorage.removeItem(key);
this.customAlert.toast(STORAGE.REMOVE_SUCCESS);
} catch (e) {
this.customAlert.toast(STORAGE.REMOVE_FAILURE);
console.error(e);
}
}
}

export default DataStorage;
10 changes: 10 additions & 0 deletions src/app/constants/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const MESSAGE = {};

export const STORAGE = {
SAVE_SUCCESS: '브라우저 내 데이터 저장이 완료되었습니다.',
SAVE_FAILURE: '브라우저 내 데이터 저장에 실패하였습니다.',
INCORRECT_DATA: '올바르지 않은 데이터 형태입니다.',
GET_FAILURE: '브라우저 내 데이터 가져오기에 실패하였습니다.',
REMOVE_SUCCESS: '브라우저 내 데이터 삭제가 완료되었습니다.',
REMOVE_FAILURE: '브라우저 내 데이터 삭제에 실패하였습니다.',
};
21 changes: 17 additions & 4 deletions src/app/home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
'use client';

import React from 'react';
import Calendar from '../components/Calendar';
import SummaryInDateTable from '../components/SummaryInDateTable';
import SummaryInMonth from '../components/SummaryInMonth';
import { TTrainData } from '../train/types/table';
import DataStorage from '../utils/storage';
import DataStorage from '../class/storage';
import { checkPossibilityToRender } from '../utils/validate';
import Button from '../components/buttons/Button';
import { useRouter } from 'next/navigation';
import { DEFAULT_EXCEL_DATA } from '../constants/table';
import dynamic from 'next/dynamic';

const Calendar = dynamic(() => import('../components/Calendar'), {
ssr: false,
});

const SummaryInDateTable = dynamic(
() => import('../components/SummaryInDateTable'),
{
ssr: false,
}
);

const SummaryInMonth = dynamic(() => import('../components/SummaryInMonth'), {
ssr: false,
});

const NotMachedForamt = ({
handleDeleteExcel,
Expand Down
13 changes: 1 addition & 12 deletions src/app/hooks/useTrain.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useParams, useRouter } from 'next/navigation';
// import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useForm, useFieldArray } from 'react-hook-form';
import { BASE_TABLE_FORM } from '../constants/table';
import { ITrain, TableForm, TTrainData } from '../train/types/table';
import { formatSaveDate, pushDateFormat } from '../utils/format';
import DataStorage from '../utils/storage';
import DataStorage from '../class/storage';
import { checkPossibilityToSave } from '../utils/validate';

const useTrain = () => {
Expand Down Expand Up @@ -82,15 +81,6 @@ const useTrain = () => {
return dataStorage.set('iron-mate-data', saveData);
};

const updateTableValue = (itemIndex: number, type: keyof ITrain) => {
if (fields.length - 1 < itemIndex) return;

const uniqueId = `trainTable.${Number(itemIndex)}.${type}` as const;
const currentSet = getValues(uniqueId) as unknown as string;

setValue(uniqueId, (Number(currentSet) + 1) as never);
};

const updateWorkoutSets = (itemIndex: number) => {
//*TODO: itemIndex가 전체 인덱스 범위에 없는 경우 에러처리 필요.
// if (fields.length - 1 < itemIndex) return;
Expand Down Expand Up @@ -121,7 +111,6 @@ const useTrain = () => {

if (dateParams) return setCalendarDate(dateParams as string);
setCalendarDate(pushDateFormat(today));
// route.replace(`/train/${pushDateFormat(today)}`);
}, [dateParams]);

useEffect(() => {
Expand Down
3 changes: 3 additions & 0 deletions src/app/interface/alert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface IAlert {
toast: (message: string) => void;
}
5 changes: 2 additions & 3 deletions src/app/manage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react';
import { INFORMATION_FORMAT_MAP } from '../constants/table';
import { TTrainData } from '../train/types/table';
import { Excel } from '../utils/excel';
import DataStorage from '../utils/storage';
import DataStorage from '../class/storage';
import { checkWithTargetList } from '../utils/validate';

const Manage = () => {
Expand Down Expand Up @@ -54,8 +54,7 @@ const Manage = () => {
};

const removeExcel = () => {
dataStorage.remove();
return alert('액셀 데이터 삭제가 완료 되었습니다.');
return dataStorage.remove();
};

return (
Expand Down
9 changes: 1 addition & 8 deletions src/app/train/[date]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
'use client';

import React, { useEffect, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import React from 'react';
import Button from '../../components/buttons/Button';
import EnterInformationTable from '../../components/table/EnterInformationTable';
import Timer from '../../components/Timer';
import { BASE_TABLE_FORM } from '../../constants/table';
import { TableForm, TTrainData } from '../types/table';
import DateInput from '../../components/input/DateInput';
import { useParams, useRouter } from 'next/navigation';
import { formatSaveDate, pushDateFormat } from '@/app/utils/format';
import DataStorage from '@/app/utils/storage';
import { checkPossibilityToSave } from '@/app/utils/validate';
import useTrain from '@/app/hooks/useTrain';

const Train = () => {
Expand Down
4 changes: 3 additions & 1 deletion src/app/utils/alert.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default class CustomAlert {
import { IAlert } from '../interface/alert';

export default class CustomAlert implements IAlert {
constructor() {}

toast = (message: string) => {
Expand Down

0 comments on commit d29e205

Please sign in to comment.