diff --git a/fake-server/db.json b/fake-server/db.json index 52c6d41..e88ad8c 100644 --- a/fake-server/db.json +++ b/fake-server/db.json @@ -18,7 +18,6 @@ "contents": "우리는 이것저것 합니다.1", "tags": [ "JavaScript", - "React", "Algorithm" ] }, @@ -34,8 +33,7 @@ "contents": "우리는 이것저것 합니다.2", "tags": [ "JavaScript", - "React", - "Algorithm" + "React" ] }, { @@ -52,7 +50,6 @@ "contents": "우리는 이것저것 합니다.3", "tags": [ "JavaScript", - "React", "Algorithm" ] }, @@ -97,7 +94,6 @@ "personnel": 9, "contents": "우리는 이것저것 합니다.5", "tags": [ - "JavaScript", "React", "Algorithm" ] @@ -137,8 +133,7 @@ "contents": "우리는 이것저것 합니다.7", "tags": [ "JavaScript", - "React", - "Algorithm" + "React" ] }, { @@ -343,7 +338,7 @@ "tags": [ "JavaScript", "React", - "Algorithm" + "C" ] }, { @@ -364,7 +359,7 @@ "tags": [ "JavaScript", "React", - "Algorithm" + "C" ] }, { @@ -379,7 +374,6 @@ "contents": "우리는 이것저것 합니다.19", "tags": [ "JavaScript", - "React", "Algorithm" ] }, @@ -416,9 +410,8 @@ "personnel": 8, "contents": "우리는 이것저것 합니다.21", "tags": [ - "JavaScript", "React", - "Algorithm" + "Java" ] } ], diff --git a/fixtures/study-groups.js b/fixtures/study-groups.js index 1ba2d65..edcd3b0 100644 --- a/fixtures/study-groups.js +++ b/fixtures/study-groups.js @@ -32,7 +32,6 @@ const studyGroups = [ personnel: 2, contents: '우리는 이것저것 합니다.2', tags: [ - 'JavaScript', 'React', 'Algorithm', ], diff --git a/package-lock.json b/package-lock.json index 0c888ac..9836a72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11019,10 +11019,9 @@ } }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "querystring": { "version": "0.2.0", @@ -11645,6 +11644,12 @@ "uuid": "^3.3.2" }, "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", diff --git a/package.json b/package.json index 0c81c53..e3f3f95 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "axios": "^0.21.0", "moment": "^2.29.1", "moment-timezone": "^0.5.32", + "qs": "^6.9.4", "react": "^17.0.1", "react-dom": "^17.0.1", "react-moment": "^1.0.0", diff --git a/src/components/common/Tags.jsx b/src/components/common/Tags.jsx index 501ad44..ded60d3 100644 --- a/src/components/common/Tags.jsx +++ b/src/components/common/Tags.jsx @@ -8,7 +8,7 @@ import palette from '../../styles/palette'; const TagsWrapper = styled.div` margin-top: 1rem; - .lang { + .tag { display: inline-flex; align-items: center; padding-left: .6em; @@ -36,8 +36,8 @@ const Tags = ({ tags }) => { {tags.map((tag) => ( {`#${tag}`} diff --git a/src/components/common/Tags.test.jsx b/src/components/common/Tags.test.jsx index cd31af0..de58794 100644 --- a/src/components/common/Tags.test.jsx +++ b/src/components/common/Tags.test.jsx @@ -16,13 +16,14 @@ describe('Tags', () => { )); context('with tags', () => { + const tags = ['JavaScript', 'C', 'Python']; it('renders tags name', () => { - const tags = ['JavaScript', 'C', 'Python']; - const { container } = renderTags(tags); tags.forEach((tag) => { expect(container).toHaveTextContent(tag); + + expect(container.innerHTML).toContain(' { + const { search } = useLocation(); + + const dispatch = useDispatch(); const groups = useSelector(get('groups')); + useEffect(() => { + const { tag } = qs.parse(search, { + ignoreQueryPrefix: true, + }); + + dispatch(loadStudyGroups(tag)); + }, [dispatch, search]); + if (!groups || !groups.length) { return
스터디가 존재하지 않습니다.
; } diff --git a/src/containers/groups/StudyGroupsContainer.test.jsx b/src/containers/groups/StudyGroupsContainer.test.jsx index fa46e8a..3eed582 100644 --- a/src/containers/groups/StudyGroupsContainer.test.jsx +++ b/src/containers/groups/StudyGroupsContainer.test.jsx @@ -3,10 +3,19 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import StudyGroupsContainer from './StudyGroupsContainer'; +const mockSearch = jest.fn(); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation() { + return { search: mockSearch }; + }, +})); + describe('StudyGroupsContainer', () => { const dispatch = jest.fn(); @@ -31,6 +40,7 @@ describe('StudyGroupsContainer', () => { moderatorId: 'user1', title: '소개합니다.', participants: [], + tags: ['JavaScript'], }])); it('renders groups title', () => { @@ -38,6 +48,14 @@ describe('StudyGroupsContainer', () => { expect(container).toHaveTextContent('소개합니다.'); }); + + it('click event calls dispatch', () => { + const { getByText } = renderStudyGroupsContainer(); + + fireEvent.click(getByText('#JavaScript')); + + expect(dispatch).toBeCalled(); + }); }); context('without groups', () => { diff --git a/src/pages/MainPage.jsx b/src/pages/MainPage.jsx index 457f483..b8c25b2 100644 --- a/src/pages/MainPage.jsx +++ b/src/pages/MainPage.jsx @@ -1,10 +1,7 @@ -import React, { useEffect } from 'react'; - -import { useDispatch } from 'react-redux'; +import React from 'react'; import styled from '@emotion/styled'; -import { loadStudyGroups } from '../reducers/slice'; import StudyGroupsContainer from '../containers/groups/StudyGroupsContainer'; import Responsive from '../styles/Responsive'; @@ -12,19 +9,11 @@ const MainPageWrapper = styled(Responsive)` `; -const MainPage = () => { - const dispatch = useDispatch(); - - useEffect(() => { - dispatch(loadStudyGroups()); - }, []); - - return ( - -

지금 바로 시작하세요!

- -
- ); -}; +const MainPage = () => ( + +

지금 바로 시작하세요!

+ +
+); export default MainPage; diff --git a/src/pages/MainPage.test.jsx b/src/pages/MainPage.test.jsx index a7bd0ed..f1366e3 100644 --- a/src/pages/MainPage.test.jsx +++ b/src/pages/MainPage.test.jsx @@ -2,11 +2,12 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; -import MainPage from './MainPage'; +import { MemoryRouter } from 'react-router-dom'; -jest.mock('react-redux'); +import MainPage from './MainPage'; +import STUDY_GROUPS from '../../fixtures/study-groups'; describe('MainPage', () => { const dispatch = jest.fn(); @@ -16,13 +17,15 @@ describe('MainPage', () => { useDispatch.mockImplementation(() => dispatch); useSelector.mockImplementation((selector) => selector({ - groups: [], + groups: STUDY_GROUPS, })); }); - const renderMainPage = () => render( - , - ); + const renderMainPage = () => render(( + + + + )); it('renders Main Page Title', () => { const { container } = renderMainPage(); @@ -31,7 +34,18 @@ describe('MainPage', () => { }); it('calls dispatch with loadStudyGroups action', () => { - renderMainPage(); + const { container } = renderMainPage(); + + expect(dispatch).toBeCalled(); + + expect(container).toHaveTextContent('스터디를 소개합니다.1'); + expect(container).toHaveTextContent('스터디를 소개합니다.2'); + }); + + it('Click event to calls dispatch', () => { + const { getByText } = renderMainPage(); + + fireEvent.click(getByText('#JavaScript')); expect(dispatch).toBeCalled(); }); diff --git a/src/reducers/slice.js b/src/reducers/slice.js index b671dd4..651224e 100644 --- a/src/reducers/slice.js +++ b/src/reducers/slice.js @@ -12,10 +12,16 @@ const { actions, reducer } = createSlice({ group: null, }, reducers: { - setStudyGroups(state, { payload: groups }) { + setStudyGroups(state, { payload: { groups, tag } }) { return { ...state, - groups, + groups: tag ? groups.reduce((studies, group) => { + if (group.tags.includes(tag)) { + return [...studies, group]; + } + + return studies; + }, []) : groups, }; }, setStudyGroup(state, { payload: group }) { @@ -32,10 +38,10 @@ export const { setStudyGroup, } = actions; -export const loadStudyGroups = () => async (dispatch) => { +export const loadStudyGroups = (tag) => async (dispatch) => { const groups = await getStudyGroups(); - dispatch(setStudyGroups(groups)); + dispatch(setStudyGroups({ groups, tag })); }; export const loadStudyGroup = (id) => async (dispatch) => { diff --git a/src/reducers/slice.test.js b/src/reducers/slice.test.js index cf8fb2f..4935970 100644 --- a/src/reducers/slice.test.js +++ b/src/reducers/slice.test.js @@ -33,14 +33,34 @@ describe('reducer', () => { }); describe('setStudyGroups', () => { - it('changes groups', () => { - const initialState = { - groups: [], - }; + context('with tag', () => { + it('get study groups list with tags filtered', () => { + const initialState = { + groups: [], + }; + + const state = reducer( + initialState, + setStudyGroups({ groups: STUDY_GROUPS, tag: 'JavaScript' }), + ); + + expect(state.groups).toHaveLength(1); + }); + }); - const state = reducer(initialState, setStudyGroups(STUDY_GROUPS)); + context('without tag', () => { + it("get study groups list doesn't tags filtered", () => { + const initialState = { + groups: [], + }; - expect(state.groups).toHaveLength(2); + const state = reducer( + initialState, + setStudyGroups({ groups: STUDY_GROUPS, tag: '' }), + ); + + expect(state.groups).toHaveLength(2); + }); }); }); @@ -65,16 +85,19 @@ describe('async actions', () => { store = mockStore({}); }); - it('loads groups', async () => { + it('loads study group list', async () => { await store.dispatch(loadStudyGroups()); const actions = store.getActions(); - expect(actions[0]).toEqual(setStudyGroups([])); + expect(actions[0]).toEqual(setStudyGroups({ + groups: [], + tag: undefined, + })); }); }); - describe('loadStudyGroups', () => { + describe('loadStudyGroup', () => { beforeEach(() => { store = mockStore({}); });