From 374b121708a0021d8f1df5bb09122dc49fb4bfa2 Mon Sep 17 00:00:00 2001 From: saseungmin Date: Thu, 26 Nov 2020 01:01:00 +0900 Subject: [PATCH] [Feature] Register for study group - Create actions for clearWriteFields - Create api writeStudyGroup and axios postStudyGroup - success write test --- src/App.test.jsx | 5 +- src/components/write/WriteButtons.jsx | 19 +++++ src/components/write/WriteButtons.test.jsx | 18 +++++ .../write/WriteButtonsContainer.jsx | 39 ++++++++++ .../write/WriteButtonsContainer.test.jsx | 75 +++++++++++++++++++ src/pages/WritePage.jsx | 5 +- src/pages/WritePage.test.jsx | 10 ++- src/reducers/slice.js | 20 +++++ src/reducers/slice.test.js | 40 +++++++++- src/services/__mocks__/api.js | 4 +- src/services/api.js | 5 ++ src/services/api.test.js | 16 ++++ 12 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 src/components/write/WriteButtons.jsx create mode 100644 src/components/write/WriteButtons.test.jsx create mode 100644 src/containers/write/WriteButtonsContainer.jsx create mode 100644 src/containers/write/WriteButtonsContainer.test.jsx diff --git a/src/App.test.jsx b/src/App.test.jsx index dcc42d6..dcf46a6 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -23,7 +23,7 @@ describe('App', () => { useSelector.mockImplementation((selector) => selector({ groups: STUDY_GROUPS, - group: STUDY_GROUP, + group: given.group, writeField: { tags: [], }, @@ -37,6 +37,7 @@ describe('App', () => { )); context('with path /', () => { + given('group', () => (null)); it('renders the study list page', () => { const { container } = renderApp({ path: '/' }); @@ -45,6 +46,7 @@ describe('App', () => { }); context('with path /introduce', () => { + given('group', () => (STUDY_GROUP)); it('renders the study introduce page', () => { const { container } = renderApp({ path: '/introduce/1' }); @@ -53,6 +55,7 @@ describe('App', () => { }); context('with path /write', () => { + given('group', () => (null)); it('renders the study write page', () => { const { container } = renderApp({ path: '/write' }); diff --git a/src/components/write/WriteButtons.jsx b/src/components/write/WriteButtons.jsx new file mode 100644 index 0000000..7a23c0b --- /dev/null +++ b/src/components/write/WriteButtons.jsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import styled from '@emotion/styled'; + +const WriteButtonsWrapper = styled.div``; + +const WriteButtons = ({ onSubmit }) => ( + + + + +); + +export default WriteButtons; diff --git a/src/components/write/WriteButtons.test.jsx b/src/components/write/WriteButtons.test.jsx new file mode 100644 index 0000000..5c6ac7e --- /dev/null +++ b/src/components/write/WriteButtons.test.jsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import { render } from '@testing-library/react'; + +import WriteButtons from './WriteButtons'; + +describe('WriteButtons', () => { + const renderWriteButtons = () => render(( + + )); + + it('render Write buttons', () => { + const { container } = renderWriteButtons(); + + expect(container).toHaveTextContent('등록하기'); + expect(container).toHaveTextContent('취소'); + }); +}); diff --git a/src/containers/write/WriteButtonsContainer.jsx b/src/containers/write/WriteButtonsContainer.jsx new file mode 100644 index 0000000..a0f1caa --- /dev/null +++ b/src/containers/write/WriteButtonsContainer.jsx @@ -0,0 +1,39 @@ +import React, { useEffect, useCallback } from 'react'; + +import { useDispatch, useSelector } from 'react-redux'; + +import { useHistory } from 'react-router-dom'; + +import { get } from '../../util/utils'; +import { writeStudyGroup } from '../../reducers/slice'; + +import WriteButtons from '../../components/write/WriteButtons'; + +const WriteButtonsContainer = () => { + const history = useHistory(); + const dispatch = useDispatch(); + + const writeField = useSelector(get('writeField')); + const group = useSelector(get('group')); + + const onSubmit = useCallback(() => { + // TODO: write form validate 체크 하기 + dispatch(writeStudyGroup()); + }, [dispatch]); + + useEffect(() => { + if (group) { + const { id } = group; + history.push(`/introduce/${id}`); + } + }, [history, group]); + + return ( + + ); +}; + +export default WriteButtonsContainer; diff --git a/src/containers/write/WriteButtonsContainer.test.jsx b/src/containers/write/WriteButtonsContainer.test.jsx new file mode 100644 index 0000000..0fe07ad --- /dev/null +++ b/src/containers/write/WriteButtonsContainer.test.jsx @@ -0,0 +1,75 @@ +import React from 'react'; + +import { useDispatch, useSelector } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; + +import { render, fireEvent } from '@testing-library/react'; + +import WriteButtonsContainer from './WriteButtonsContainer'; + +import WRITE_FORM from '../../../fixtures/write-form'; +import STUDY_GROUP from '../../../fixtures/study-group'; + +const mockPush = jest.fn(); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory() { + return { push: mockPush }; + }, +})); + +describe('WriteButtonsContainer', () => { + const dispatch = jest.fn(); + + beforeEach(() => { + dispatch.mockClear(); + mockPush.mockClear(); + + useDispatch.mockImplementation(() => dispatch); + + useSelector.mockImplementation((state) => state({ + writeField: WRITE_FORM, + group: given.group, + })); + }); + + const renderWriteButtonsContainer = () => render(( + + + + )); + + it('render Write buttons', () => { + const { container } = renderWriteButtonsContainer(); + + expect(container).toHaveTextContent('등록하기'); + expect(container).toHaveTextContent('취소'); + }); + + describe('when click submit button', () => { + context('with group', () => { + given('group', () => (STUDY_GROUP)); + it('dispatch action submit event', () => { + const { getByText } = renderWriteButtonsContainer(); + + fireEvent.click(getByText('등록하기')); + + expect(dispatch).toBeCalledTimes(1); + + expect(mockPush).toBeCalledWith('/introduce/1'); + }); + }); + + context('without group', () => { + given('group', () => (null)); + it('dispatch action submit event', () => { + const { getByText } = renderWriteButtonsContainer(); + + fireEvent.click(getByText('등록하기')); + + expect(mockPush).not.toBeCalled(); + }); + }); + }); +}); diff --git a/src/pages/WritePage.jsx b/src/pages/WritePage.jsx index 12f971c..0038974 100644 --- a/src/pages/WritePage.jsx +++ b/src/pages/WritePage.jsx @@ -1,6 +1,7 @@ import React from 'react'; import TagFormContainer from '../containers/write/TagsFormContainer'; +import WriteButtonsContainer from '../containers/write/WriteButtonsContainer'; import WriteFormContainer from '../containers/write/WriteFormContainer'; import Responsive from '../styles/Responsive'; @@ -10,9 +11,7 @@ const IntroducePage = () => (

스터디 그룹 개설하기

-
- -
+ ); export default IntroducePage; diff --git a/src/pages/WritePage.test.jsx b/src/pages/WritePage.test.jsx index f410059..524f772 100644 --- a/src/pages/WritePage.test.jsx +++ b/src/pages/WritePage.test.jsx @@ -33,11 +33,10 @@ describe('WritePage', () => { }); it('renders write form tag', () => { - const { getByPlaceholderText, getByText } = renderWritePage(); + const { getByPlaceholderText } = renderWritePage(); expect(getByPlaceholderText('제목을 입력하세요')).not.toBeNull(); expect(getByPlaceholderText('내용')).not.toBeNull(); - expect(getByText('저장')).not.toBeNull(); }); it('renders tag form text', () => { @@ -46,5 +45,12 @@ describe('WritePage', () => { expect(getByPlaceholderText('태그를 입력하세요')).not.toBeNull(); expect(container).toHaveTextContent('태그'); }); + + it('renders buttons', () => { + const { getByText } = renderWritePage(); + + expect(getByText('등록하기')).not.toBeNull(); + expect(getByText('취소')).not.toBeNull(); + }); }); }); diff --git a/src/reducers/slice.js b/src/reducers/slice.js index bc0a4e8..d712d39 100644 --- a/src/reducers/slice.js +++ b/src/reducers/slice.js @@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit'; import { getStudyGroup, getStudyGroups, + postStudyGroup, } from '../services/api'; const writeInitialState = { @@ -52,6 +53,14 @@ const { actions, reducer } = createSlice({ }, }; }, + clearWriteFields(state) { + return { + ...state, + writeField: { + ...writeInitialState, + }, + }; + }, }, }); @@ -59,6 +68,7 @@ export const { setStudyGroups, setStudyGroup, changeWriteField, + clearWriteFields, } = actions; export const loadStudyGroups = (tag) => async (dispatch) => { @@ -75,4 +85,14 @@ export const loadStudyGroup = (id) => async (dispatch) => { dispatch(setStudyGroup(group)); }; +export const writeStudyGroup = () => async (dispatch, getState) => { + const { writeField } = getState(); + + // NOTE: 현재 로그인 기능이 없는 관계로 임의로 작성자(moderatorId)를 넣어줌 + const group = await postStudyGroup({ ...writeField, moderatorId: 'user1' }); + + dispatch(setStudyGroup(group)); + dispatch(clearWriteFields()); +}; + export default reducer; diff --git a/src/reducers/slice.test.js b/src/reducers/slice.test.js index b98380d..4b416d1 100644 --- a/src/reducers/slice.test.js +++ b/src/reducers/slice.test.js @@ -9,10 +9,13 @@ import reducer, { setStudyGroup, loadStudyGroup, changeWriteField, + writeStudyGroup, + clearWriteFields, } from './slice'; import STUDY_GROUPS from '../../fixtures/study-groups'; import STUDY_GROUP from '../../fixtures/study-group'; +import WRITE_FORM from '../../fixtures/write-form'; const middlewares = [thunk]; const mockStore = configureStore(middlewares); @@ -108,6 +111,24 @@ describe('reducer', () => { expect(state.writeField.tags).toEqual(['JavaScript', 'React']); }); }); + + describe('clearWriteFields', () => { + const initialState = { + writeField: { + title: '타이틀', + contents: '내용', + }, + }; + + it('clears fields of write', () => { + const state = reducer(initialState, clearWriteFields()); + + const { writeField: { title, contents } } = state; + + expect(title).toBe(''); + expect(contents).toBe(''); + }); + }); }); describe('async actions', () => { @@ -141,7 +162,24 @@ describe('async actions', () => { const actions = store.getActions(); expect(actions[0]).toEqual(setStudyGroup(null)); - expect(actions[1]).toEqual(setStudyGroup([])); + expect(actions[1]).toEqual(setStudyGroup(undefined)); + }); + }); + + describe('writeStudyGroup', () => { + beforeEach(() => { + store = mockStore({ + writeField: WRITE_FORM, + }); + }); + + it('dispatches clearWriteFields', async () => { + await store.dispatch(writeStudyGroup()); + + const actions = store.getActions(); + + expect(actions[0]).toEqual(setStudyGroup(undefined)); + expect(actions[1]).toEqual(clearWriteFields(undefined)); }); }); }); diff --git a/src/services/__mocks__/api.js b/src/services/__mocks__/api.js index bcf0b9b..55c13c7 100644 --- a/src/services/__mocks__/api.js +++ b/src/services/__mocks__/api.js @@ -1,3 +1,5 @@ export const getStudyGroups = async () => []; -export const getStudyGroup = async () => []; +export const getStudyGroup = async () => {}; + +export const postStudyGroup = async () => {}; diff --git a/src/services/api.js b/src/services/api.js index be36463..2fb9f44 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -9,3 +9,8 @@ export const getStudyGroup = async (id) => { const response = await axios.get(`http://localhost:3000/groups/${id}`); return response.data; }; + +export const postStudyGroup = async (post) => { + const response = await axios.post('http://localhost:3000/groups/', post); + return response.data; +}; diff --git a/src/services/api.test.js b/src/services/api.test.js index c2f5fc6..baae116 100644 --- a/src/services/api.test.js +++ b/src/services/api.test.js @@ -3,6 +3,7 @@ import axios from 'axios'; import { getStudyGroup, getStudyGroups, + postStudyGroup, } from './api'; import STUDY_GROUPS from '../../fixtures/study-groups'; @@ -39,4 +40,19 @@ describe('api', () => { ); }); }); + + describe('postStudyGroup', () => { + beforeEach(() => { + axios.post.mockResolvedValue({ data: STUDY_GROUP }); + }); + + it('returns study group post', async () => { + await expect(postStudyGroup(STUDY_GROUP)).resolves.toEqual(STUDY_GROUP); + + expect(axios.post).toHaveBeenCalledWith( + 'http://localhost:3000/groups/', + STUDY_GROUP, + ); + }); + }); });