From 99fd339ec46fd3ab718c30ca89b0b2da02b6d78b Mon Sep 17 00:00:00 2001 From: saseungmin Date: Sat, 30 Jan 2021 23:30:38 +0900 Subject: [PATCH] [Feature] Save actions for study review form - redux dispatches action call firestore api - save study review in reviews of group collection --- src/components/introduce/StudyReviewForm.jsx | 7 ++- .../introduce/StudyReviewForm.test.jsx | 33 +++++++++---- .../introduce/StudyReviewContainer.jsx | 12 ++++- .../introduce/StudyReviewContainer.test.jsx | 10 ++++ src/reducers/groupSlice.js | 22 +++++++++ src/reducers/groupSlice.test.js | 46 +++++++++++++++++++ src/services/__mocks__/api.js | 2 + src/services/api.js | 28 +++++++---- 8 files changed, 136 insertions(+), 24 deletions(-) diff --git a/src/components/introduce/StudyReviewForm.jsx b/src/components/introduce/StudyReviewForm.jsx index 77d77f2..5526512 100644 --- a/src/components/introduce/StudyReviewForm.jsx +++ b/src/components/introduce/StudyReviewForm.jsx @@ -45,7 +45,7 @@ const isValidateUserInfo = (user) => (participants) => !!participants .find(({ id, confirm }) => id === user && confirm && confirm === true); const StudyReviewForm = ({ - group, user, time, fields, onChangeReview, + group, user, time, fields, onChangeReview, onSubmit, }) => { const { participants, personnel, applyEndDate, @@ -103,7 +103,10 @@ const StudyReviewForm = ({ placeholder="후기를 입력해주세요!" onChange={handleChangeReview} /> - + 후기 등록하기 diff --git a/src/components/introduce/StudyReviewForm.test.jsx b/src/components/introduce/StudyReviewForm.test.jsx index dda62cb..49fd35f 100644 --- a/src/components/introduce/StudyReviewForm.test.jsx +++ b/src/components/introduce/StudyReviewForm.test.jsx @@ -8,7 +8,12 @@ import { yesterday } from '../../util/utils'; import STUDY_GROUP from '../../../fixtures/study-group'; describe('StudyReviewForm', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + const handleChange = jest.fn(); + const handleSubmit = jest.fn(); const reviewForm = { rating: 3, review: '' }; @@ -16,10 +21,11 @@ describe('StudyReviewForm', () => { group, time, user, fields = reviewForm, }) => render(( )); @@ -36,18 +42,17 @@ describe('StudyReviewForm', () => { context('with user', () => { describe('When the user is approved applicant and applyEndDate is Deadline', () => { + const settings = { + participants: [{ id: 'user1', confirm: true }], + user: 'user1', + }; + it('renders study review form', () => { - const { container } = renderStudyReviewForm(userStatusSetting({ - participants: [{ id: 'user1', confirm: true }], - user: 'user1', - })); + const { container } = renderStudyReviewForm(userStatusSetting(settings)); expect(container).toHaveTextContent('스터디 후기를 작성해주세요!'); }); it('call event change review form', () => { - const { getByPlaceholderText } = renderStudyReviewForm(userStatusSetting({ - participants: [{ id: 'user1', confirm: true }], - user: 'user1', - })); + const { getByPlaceholderText } = renderStudyReviewForm(userStatusSetting(settings)); const textarea = getByPlaceholderText('후기를 입력해주세요!'); @@ -60,6 +65,14 @@ describe('StudyReviewForm', () => { expect(handleChange).toBeCalled(); }); + + it('call event click for review form', () => { + const { getByText } = renderStudyReviewForm(userStatusSetting(settings)); + + fireEvent.click(getByText('후기 등록하기')); + + expect(handleSubmit).toBeCalled(); + }); }); describe('When the user is not approved applicant', () => { diff --git a/src/containers/introduce/StudyReviewContainer.jsx b/src/containers/introduce/StudyReviewContainer.jsx index 8b3f478..3897b81 100644 --- a/src/containers/introduce/StudyReviewContainer.jsx +++ b/src/containers/introduce/StudyReviewContainer.jsx @@ -1,12 +1,12 @@ import React, { useState, useCallback } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; import { useInterval } from 'react-use'; +import { useSelector, useDispatch } from 'react-redux'; import { getAuth, getGroup } from '../../util/utils'; +import { changeStudyReviewFields, setStudyReview } from '../../reducers/groupSlice'; import StudyReviewForm from '../../components/introduce/StudyReviewForm'; -import { changeStudyReviewFields } from '../../reducers/groupSlice'; const StudyReviewContainer = () => { const [realTime, setRealTime] = useState(Date.now()); @@ -25,6 +25,13 @@ const StudyReviewContainer = () => { dispatch(changeStudyReviewFields({ name, value })); }, [dispatch]); + const onSubmitReview = useCallback(() => { + dispatch(setStudyReview({ + id: user, + ...studyReviewFields, + })); + }, [dispatch, user, studyReviewFields]); + if (!group) { return null; } @@ -36,6 +43,7 @@ const StudyReviewContainer = () => { time={realTime} fields={studyReviewFields} onChangeReview={onChangeReviewFields} + onSubmit={onSubmitReview} /> ); }; diff --git a/src/containers/introduce/StudyReviewContainer.test.jsx b/src/containers/introduce/StudyReviewContainer.test.jsx index d3643d3..c56104c 100644 --- a/src/containers/introduce/StudyReviewContainer.test.jsx +++ b/src/containers/introduce/StudyReviewContainer.test.jsx @@ -71,6 +71,16 @@ describe('StudyReviewContainer', () => { payload: form, }); }); + + describe('Click the button to submit for study review', () => { + it('dispatch actions call setStudyReview', () => { + const { getByText } = renderStudyReviewContainer(); + + fireEvent.click(getByText('후기 등록하기')); + + expect(dispatch).toBeCalledTimes(1); + }); + }); }); context('without login and group', () => { diff --git a/src/reducers/groupSlice.js b/src/reducers/groupSlice.js index bd42bf2..61f5039 100644 --- a/src/reducers/groupSlice.js +++ b/src/reducers/groupSlice.js @@ -11,6 +11,7 @@ import { updateConfirmPostParticipant, deletePostGroup, editPostStudyGroup, + postUpdateStudyReview, } from '../services/api'; const writeInitialState = { @@ -21,6 +22,7 @@ const writeInitialState = { participants: [], personnel: '1', tags: [], + reviews: [], }; const applyInitialState = { @@ -127,6 +129,13 @@ const { actions, reducer } = createSlice({ draft.studyReviewFields[name] = value; }); }, + + clearStudyReviewFields(state) { + return { + ...state, + studyReviewFields: studyReviewInitialState, + }; + }, }, }); @@ -141,6 +150,7 @@ export const { clearApplyFields, setOriginalArticle, changeStudyReviewFields, + clearStudyReviewFields, } = actions; export const loadStudyGroups = (tag) => async (dispatch) => { @@ -276,4 +286,16 @@ export const deleteGroup = (groupId) => async (dispatch) => { dispatch(loadStudyGroups()); }; +export const setStudyReview = (review) => async (dispatch, getState) => { + const { groupReducer: { group } } = getState(); + const { id } = group; + + await postUpdateStudyReview({ + id, + review, + }); + + dispatch(clearStudyReviewFields()); +}; + export default reducer; diff --git a/src/reducers/groupSlice.test.js b/src/reducers/groupSlice.test.js index 6cbf373..2d3e27f 100644 --- a/src/reducers/groupSlice.test.js +++ b/src/reducers/groupSlice.test.js @@ -22,11 +22,14 @@ import reducer, { editStudyGroup, setGroupError, changeStudyReviewFields, + clearStudyReviewFields, + setStudyReview, } from './groupSlice'; import STUDY_GROUPS from '../../fixtures/study-groups'; import STUDY_GROUP from '../../fixtures/study-group'; import WRITE_FORM from '../../fixtures/write-form'; + import { editPostStudyGroup, postStudyGroup } from '../services/api'; const middlewares = [thunk]; @@ -50,6 +53,7 @@ describe('reducer', () => { participants: [], personnel: '1', tags: [], + reviews: [], }, applyFields: { reason: '', @@ -244,6 +248,24 @@ describe('reducer', () => { expect(state.studyReviewFields.rating).toBe(5); }); }); + + describe('clearStudyReviewFields', () => { + const initialState = { + studyReviewFields: { + rating: 5, + review: 'test', + }, + }; + + it('clears fields of study review form', () => { + const state = reducer(initialState, clearStudyReviewFields()); + + const { studyReviewFields: { rating, review } } = state; + + expect(rating).toBe(3); + expect(review).toBe(''); + }); + }); }); describe('async actions', () => { @@ -524,4 +546,28 @@ describe('async actions', () => { }); }); }); + + describe('setStudyReview', () => { + beforeEach(() => { + store = mockStore({ + groupReducer: { + group: { id: '1' }, + }, + }); + }); + + it('dispatches clearStudyReviewFields', async () => { + await store.dispatch(setStudyReview({ + user: 'user', + review: 'test', + rating: 5, + })); + + const actions = store.getActions(); + + expect(actions[0]).toEqual({ + type: 'group/clearStudyReviewFields', + }); + }); + }); }); diff --git a/src/services/__mocks__/api.js b/src/services/__mocks__/api.js index 026eebd..5068e2d 100644 --- a/src/services/__mocks__/api.js +++ b/src/services/__mocks__/api.js @@ -19,3 +19,5 @@ export const updateConfirmPostParticipant = jest.fn(); export const deletePostGroup = jest.fn(); export const editPostStudyGroup = jest.fn(); + +export const postUpdateStudyReview = jest.fn(); diff --git a/src/services/api.js b/src/services/api.js index 26709a1..54e023e 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -66,11 +66,11 @@ export const postStudyGroup = async (group) => { export const editPostStudyGroup = async ({ title, applyEndDate, contents, tags, personnel, id, }) => { - const groups = db.collection('groups').doc(id); + const group = db.collection('groups').doc(id); const timeStamp = fireStore.Timestamp.fromDate(new Date(applyEndDate)); - await groups.update({ + await group.update({ title, contents, applyEndDate: timeStamp, @@ -79,32 +79,40 @@ export const editPostStudyGroup = async ({ }); }; +export const postUpdateStudyReview = async ({ id, review }) => { + const group = db.collection('groups').doc(id); + + await group.set({ + reviews: fireStore.FieldValue.arrayUnion(review), + }, { merge: true }); +}; + export const updatePostParticipant = async ({ id, user }) => { - const groups = db.collection('groups').doc(id); + const group = db.collection('groups').doc(id); - await groups.update({ + await group.update({ participants: fireStore.FieldValue.arrayUnion(user), }); }; export const deletePostParticipant = async ({ id, participants }) => { - const groups = db.collection('groups').doc(id); + const group = db.collection('groups').doc(id); - await groups.update({ + await group.update({ participants, }); }; export const deletePostGroup = async (id) => { - const groups = db.collection('groups').doc(id); + const group = db.collection('groups').doc(id); - await groups.delete(); + await group.delete(); }; export const updateConfirmPostParticipant = async ({ id, participants }) => { - const groups = db.collection('groups').doc(id); + const group = db.collection('groups').doc(id); - await groups.update({ + await group.update({ participants, }); };