From 63e0664a982fb95a2cf8cb55187985827de087d8 Mon Sep 17 00:00:00 2001 From: dodoongtak Date: Tue, 6 Apr 2021 16:49:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20feat:=20Show=20Apartment=20Deta?= =?UTF-8?q?ils=20on=20Result=20Page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 2 + src/App.test.jsx | 31 ++++-- src/components/LinkField.jsx | 7 +- src/components/LinkField.test.jsx | 60 +++++++++--- src/pages/Apartment/Apartment.jsx | 34 +++---- src/pages/Apartment/Apartment.test.jsx | 43 +++----- src/pages/Apartment/ApartmentPage.jsx | 11 ++- src/pages/Apartment/Apartments.jsx | 30 ++++++ src/pages/Apartment/Apartments.test.jsx | 63 ++++++++++++ src/pages/Apartment/ApartmentsContainer.jsx | 18 +++- src/pages/Home/Greet.jsx | 37 +++++++ src/pages/Home/Greet.test.jsx | 80 +++++++++++++++ src/pages/Home/Home.jsx | 49 +++------- src/pages/Home/Home.test.jsx | 67 +++---------- src/pages/Profile/ProfileContainer.jsx | 12 ++- src/pages/Profile/ProfilePage.jsx | 4 +- src/pages/Result/ApartmentDetail.jsx | 60 ++++++++++++ src/pages/Result/ApartmentDetail.test.jsx | 27 +++++ src/pages/Result/Result.jsx | 46 +++++++++ src/pages/Result/Result.test.jsx | 103 ++++++++++++++++++++ src/pages/Result/ResultContainer.jsx | 23 +++++ src/pages/Result/ResultContainer.test.jsx | 67 +++++++++++++ src/pages/Result/ResultPage.jsx | 29 ++++++ src/pages/Result/ResultPage.test.jsx | 44 +++++++++ src/pages/Result/index.js | 1 + src/pages/index.js | 1 + src/redux/appSlice.js | 20 +++- src/redux/appSlice.test.js | 27 ++++- 28 files changed, 817 insertions(+), 179 deletions(-) create mode 100644 src/pages/Apartment/Apartments.jsx create mode 100644 src/pages/Apartment/Apartments.test.jsx create mode 100644 src/pages/Home/Greet.jsx create mode 100644 src/pages/Home/Greet.test.jsx create mode 100644 src/pages/Result/ApartmentDetail.jsx create mode 100644 src/pages/Result/ApartmentDetail.test.jsx create mode 100644 src/pages/Result/Result.jsx create mode 100644 src/pages/Result/Result.test.jsx create mode 100644 src/pages/Result/ResultContainer.jsx create mode 100644 src/pages/Result/ResultContainer.test.jsx create mode 100644 src/pages/Result/ResultPage.jsx create mode 100644 src/pages/Result/ResultPage.test.jsx create mode 100644 src/pages/Result/index.js diff --git a/src/App.jsx b/src/App.jsx index c97c2e0..e3589f6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -12,6 +12,7 @@ import { ProfilePage, NewProfilePage, ApartmentPage, + ResultPage, NotFoundPage, } from './pages'; @@ -40,6 +41,7 @@ export default function App() { + diff --git a/src/App.test.jsx b/src/App.test.jsx index a9d8dc5..c3466a9 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -18,6 +18,7 @@ describe('App', () => { const dispatch = jest.fn(); const profile = { + isNew: false, name: '신형탁', age: 29, salary: 5000, @@ -41,7 +42,18 @@ describe('App', () => { }, ]; + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + given('userFields', () => (profile)); given('apartments', () => apartments); + given('apartment', () => apartment); beforeEach(() => { jest.clearAllMocks(); @@ -51,6 +63,7 @@ describe('App', () => { useSelector.mockImplementation((selector) => selector({ userFields: given.userFields, apartments: given.apartments, + apartment: given.apartment, })); loadItem.mockImplementation(() => null); @@ -65,8 +78,6 @@ describe('App', () => { } context('with path /', () => { - given('userFields', () => (profile)); - beforeEach(() => { loadItem.mockImplementation(() => (JSON.stringify(profile))); }); @@ -79,8 +90,6 @@ describe('App', () => { }); context('with path /profile/new', () => { - given('userFields', () => (profile)); - it('renders new profile page', () => { renderApp({ path: '/profile/new' }); @@ -95,8 +104,6 @@ describe('App', () => { }); context('with path /profile', () => { - given('userFields', () => (profile)); - it('renders profile page', () => { renderApp({ path: '/profile' }); @@ -123,6 +130,18 @@ describe('App', () => { }); }); + context('with path /result', () => { + it('renders the apartment result page', () => { + renderApp({ path: '/result' }); + + expect(screen.getByText('결과')).toBeInTheDocument(); + + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('129.92')).toBeInTheDocument(); + expect(screen.getByText('470,000')).toBeInTheDocument(); + }); + }); + context('with invalid path', () => { it('renders the not found page', () => { renderApp({ path: '/amazingtoyproject' }); diff --git a/src/components/LinkField.jsx b/src/components/LinkField.jsx index 0805921..efb6297 100644 --- a/src/components/LinkField.jsx +++ b/src/components/LinkField.jsx @@ -1,8 +1,13 @@ import React, { useCallback } from 'react'; -export default function LinkField({ url, title, onClick }) { +export default function LinkField({ + url, title, onClick, apartment, changeApartment, +}) { const handleClick = useCallback((event) => { event.preventDefault(); + if (title === '보기') { + changeApartment(apartment); + } onClick({ url }); }, [onClick]); diff --git a/src/components/LinkField.test.jsx b/src/components/LinkField.test.jsx index ddf81b4..411d5b3 100644 --- a/src/components/LinkField.test.jsx +++ b/src/components/LinkField.test.jsx @@ -6,38 +6,68 @@ import LinkField from './LinkField'; describe('LinkField', () => { const handleClick = jest.fn(); + const changeApartment = jest.fn(); beforeEach(() => { jest.clearAllMocks(); }); - it('renders Link', () => { - render(( - - )); + context("with '내 정보 입력하러가기' title", () => { + it('renders Link', () => { + render(( + + )); + + expect(screen.getByRole('link', { + name: '내 정보 입력하러가기', + })).toBeInTheDocument(); + }); + }); + + context("with '거주하고 싶은 아파트 둘러보기' title", () => { + it('calls handleClick upon clicking link', () => { + render(( + + )); + + fireEvent.click(screen.getByRole('link', { + name: '거주하고 싶은 아파트 둘러보기', + })); - expect(screen.getByRole('link', { - name: '내 정보 입력하러가기', - })).toBeInTheDocument(); + expect(handleClick).toBeCalledWith({ url: '/apartment' }); + }); }); - it('calls handleClick upon clicking link', () => { + context("with '보기' title", () => { render(( )); fireEvent.click(screen.getByRole('link', { - name: '거주하고 싶은 아파트 둘러보기', + name: '보기', })); - expect(handleClick).toBeCalledWith({ url: '/apartment' }); + expect(handleClick).toBeCalled(); + expect(changeApartment).toBeCalled(); }); }); diff --git a/src/pages/Apartment/Apartment.jsx b/src/pages/Apartment/Apartment.jsx index ed9048f..6a6a324 100644 --- a/src/pages/Apartment/Apartment.jsx +++ b/src/pages/Apartment/Apartment.jsx @@ -1,26 +1,20 @@ import React from 'react'; -export default function Apartment({ apartments }) { - if (!apartments.length) { - return ( -

loading

- ); - } +export default function Apartment({ apartment }) { + const { + name, + district, + area, + } = apartment; return ( - <> - {apartments?.map((apartment) => ( -
-
-
아파트명:
-
{apartment.name}
-
법정동:
-
{apartment.district}
-
전용면적:
-
{apartment.area}
-
-
- ))} - +
+
아파트명:
+
{name}
+
법정동:
+
{district}
+
전용면적:
+
{area}
+
); } diff --git a/src/pages/Apartment/Apartment.test.jsx b/src/pages/Apartment/Apartment.test.jsx index f6164dd..4ea3606 100644 --- a/src/pages/Apartment/Apartment.test.jsx +++ b/src/pages/Apartment/Apartment.test.jsx @@ -5,37 +5,20 @@ import { render, screen } from '@testing-library/react'; import Apartment from './Apartment'; describe('Apartment', () => { - const apartments = [ - { - name: '아크로리버파크', - date: '2021-03', - area: '129.92', - price: '470,000', - lotNumber: 1, - }, - { - name: '서울', - date: '2021-02', - area: '200.27', - price: '420,000', - lotNumber: 2, - }, - ]; + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; - context('without apartments', () => { - it('renders loading', () => { - render(); + it('renders apartment', () => { + render(); - expect(screen.getByText('loading')).toBeInTheDocument(); - }); - }); - - context('with apartments', () => { - it('renders Apartment Page', () => { - render(); - - expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); - expect(screen.getByText('서울')).toBeInTheDocument(); - }); + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('반포동')).toBeInTheDocument(); + expect(screen.getByText('129.92')).toBeInTheDocument(); }); }); diff --git a/src/pages/Apartment/ApartmentPage.jsx b/src/pages/Apartment/ApartmentPage.jsx index e51ef41..328adfa 100644 --- a/src/pages/Apartment/ApartmentPage.jsx +++ b/src/pages/Apartment/ApartmentPage.jsx @@ -14,10 +14,13 @@ export default function ApartmentPage({ params }) { }, [history]); return ( -
-

거주하고 싶은신 아파트를 선택해주세요

+
+
거주하고 싶은신 아파트를 선택해주세요
- -
+ + ); } diff --git a/src/pages/Apartment/Apartments.jsx b/src/pages/Apartment/Apartments.jsx new file mode 100644 index 0000000..89e5521 --- /dev/null +++ b/src/pages/Apartment/Apartments.jsx @@ -0,0 +1,30 @@ +import React from 'react'; + +import LinkField from '../../components/LinkField'; + +import Apartment from './Apartment'; + +export default function Apartments({ apartments, onClick, changeApartment }) { + if (!apartments.length) { + return ( +

loading

+ ); + } + + return ( + <> + {apartments?.map((apartment) => ( +
+ + +
+ ))} + + ); +} diff --git a/src/pages/Apartment/Apartments.test.jsx b/src/pages/Apartment/Apartments.test.jsx new file mode 100644 index 0000000..3994746 --- /dev/null +++ b/src/pages/Apartment/Apartments.test.jsx @@ -0,0 +1,63 @@ +import React from 'react'; + +import { fireEvent, render, screen } from '@testing-library/react'; + +import Apartments from './Apartments'; + +describe('Apartments', () => { + const handleClick = jest.fn(); + const changeApartment = jest.fn(); + + const apartments = [ + { + name: '아크로리버파크', + date: '2021-03', + area: '129.92', + price: '470,000', + lotNumber: 1, + }, + { + name: '서울', + date: '2021-02', + area: '200.27', + price: '420,000', + lotNumber: 2, + }, + ]; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + context('without apartments', () => { + it('renders loading', () => { + render(); + + expect(screen.getByText('loading')).toBeInTheDocument(); + }); + }); + + context('with apartments', () => { + it('renders Apartment Page', () => { + render(); + + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('서울')).toBeInTheDocument(); + + expect(screen.getAllByText('보기')).toHaveLength(2); + }); + + it("calls handleClick upon clicking '보기' button", () => { + render(); + + fireEvent.click(screen.getAllByText('보기')[0]); + + expect(handleClick).toBeCalledWith({ url: '/result' }); + }); + }); +}); diff --git a/src/pages/Apartment/ApartmentsContainer.jsx b/src/pages/Apartment/ApartmentsContainer.jsx index d8f615f..f397c46 100644 --- a/src/pages/Apartment/ApartmentsContainer.jsx +++ b/src/pages/Apartment/ApartmentsContainer.jsx @@ -2,13 +2,13 @@ import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { loadApartments } from '../../redux/appSlice'; +import { loadApartments, setApartment } from '../../redux/appSlice'; import { get } from '../../utils/utils'; -import Apartment from './Apartment'; +import Apartments from './Apartments'; -export default function Apartments({ apartmentCategory }) { +export default function ApartmentContainer({ apartmentCategory, onClick }) { const dispatch = useDispatch(); useEffect(() => { @@ -17,10 +17,18 @@ export default function Apartments({ apartmentCategory }) { const apartments = useSelector(get('apartments')); + function changeApartment(apartment) { + dispatch(setApartment(apartment)); + } + return (
- {apartmentCategory} - +
{apartmentCategory}
+
); } diff --git a/src/pages/Home/Greet.jsx b/src/pages/Home/Greet.jsx new file mode 100644 index 0000000..5b116a0 --- /dev/null +++ b/src/pages/Home/Greet.jsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import LinkField from '../../components/LinkField'; + +export default function Greet({ profile, onClick }) { + const { isNew, name } = profile; + + if (!isNew) { + return ( +
+

+ {name} + 님 안녕하세요!! +

+ + +
+ ); + } + return ( +
+ {isNew && ( + <> + + + )} +
+ ); +} diff --git a/src/pages/Home/Greet.test.jsx b/src/pages/Home/Greet.test.jsx new file mode 100644 index 0000000..64ca562 --- /dev/null +++ b/src/pages/Home/Greet.test.jsx @@ -0,0 +1,80 @@ +import React from 'react'; + +import { + render, + screen, + fireEvent, +} from '@testing-library/react'; + +import given from 'given2'; + +import { initialUserField } from '../../fixtures/initials'; + +import Greet from './Greet'; + +describe('Greet', () => { + const handleClick = jest.fn(); + + const profile = { + isNew: false, + name: '신형탁', + age: 29, + salary: 5000, + asset: 10000, + }; + + const renderGreet = () => render(( + + )); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + context('without profile', () => { + given('profile', () => initialUserField); + + it('renders a link for user to fill up to form', () => { + renderGreet(); + + expect(screen.getByText('내 정보 입력하러가기')).toBeInTheDocument(); + }); + + it('calls handleClick upon clicking new profile', () => { + renderGreet(); + + fireEvent.click(screen.getByRole('link', { + name: '내 정보 입력하러가기', + })); + + expect(handleClick).toBeCalledTimes(1); + }); + }); + + context('with profile', () => { + given('profile', () => (profile)); + + it('renders user name', () => { + renderGreet(); + + expect(screen.getByText(/신형탁/)).toBeInTheDocument(); + + expect(screen.getByRole('link', { + name: '내 정보 확인하러가기', + })).toBeInTheDocument(); + }); + + it('calls handleClick upon clicking check my profile', () => { + renderGreet(); + + fireEvent.click(screen.getByRole('link', { + name: '내 정보 확인하러가기', + })); + + expect(handleClick).toBeCalledTimes(1); + }); + }); +}); diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index 6c5fc21..1c03e4a 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -2,44 +2,25 @@ import React from 'react'; import LinkField from '../../components/LinkField'; +import Greet from './Greet'; + export default function Home({ profile, onClick }) { - if (!profile.isNew) { - return ( - <> -

- {profile.name} - 님 안녕하세요!! -

+ return ( +
+
꿈꾸는 삶을 살기 위해 얼마나 많은 돈을 더 벌어야 될까요?
- - - ); - } - - return ( -
-

꿈꾸는 삶을 살기 위해 얼마나 많은 돈을 더 벌어야 될까요?

- { - profile?.isNew && ( - <> - + )} - - - ) - } -
+ +
); } diff --git a/src/pages/Home/Home.test.jsx b/src/pages/Home/Home.test.jsx index bf2b482..879d03b 100644 --- a/src/pages/Home/Home.test.jsx +++ b/src/pages/Home/Home.test.jsx @@ -1,11 +1,13 @@ import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { + render, + screen, + fireEvent, +} from '@testing-library/react'; import given from 'given2'; -import { initialUserField } from '../../fixtures/initials'; - import Home from './Home'; describe('Home', () => { @@ -22,58 +24,21 @@ describe('Home', () => { jest.clearAllMocks(); }); - context('without profile', () => { - given('profile', () => initialUserField); - - it('renders Home Page', () => { - renderHome(); - - expect(screen.getByText('내 정보 입력하러가기')).toBeInTheDocument(); - }); - - it('calls handleClick upon clicking new profile', () => { - renderHome(); - - fireEvent.click(screen.getByRole('link', { - name: '내 정보 입력하러가기', - })); - - expect(handleClick).toBeCalledTimes(1); - }); + it('renders a link for user to check apartments', () => { + renderHome(); - it('calls handleClick upon clicking check apartments', () => { - renderHome(); - - fireEvent.click(screen.getByRole('link', { - name: '거주하고 싶은 아파트 둘러보기', - })); - - expect(handleClick).toBeCalledTimes(1); - }); + expect(screen.getByRole('link', { + name: '거주하고 싶은 아파트 둘러보기', + })).toBeInTheDocument(); }); - context('with profile', () => { - given('profile', () => ({ - name: '신형탁', - age: 29, - salary: 5000, - asset: 10000, - })); - - it('renders user name', () => { - renderHome(); - expect(screen.getByText(/신형탁/)).toBeInTheDocument(); - expect(screen.getByText('내 정보 확인하러가기')).toBeInTheDocument(); - }); - - it('calls handleClick upon clicking check my profile', () => { - renderHome(); + it('calls handleClick upon clicking check apartments', () => { + renderHome(); - fireEvent.click(screen.getByRole('link', { - name: '내 정보 확인하러가기', - })); + fireEvent.click(screen.getByRole('link', { + name: '거주하고 싶은 아파트 둘러보기', + })); - expect(handleClick).toBeCalledTimes(1); - }); + expect(handleClick).toBeCalledTimes(1); }); }); diff --git a/src/pages/Profile/ProfileContainer.jsx b/src/pages/Profile/ProfileContainer.jsx index 836ba87..45862fa 100644 --- a/src/pages/Profile/ProfileContainer.jsx +++ b/src/pages/Profile/ProfileContainer.jsx @@ -4,15 +4,17 @@ import { useSelector } from 'react-redux'; import { get } from '../../utils/utils'; -import MyPage from './Profile'; +import Profile from './Profile'; export default function ProfileContainer({ onClickNewProfile }) { const profile = useSelector(get('userFields')); return ( - +
+ +
); } diff --git a/src/pages/Profile/ProfilePage.jsx b/src/pages/Profile/ProfilePage.jsx index 4956e1a..0d18ef7 100644 --- a/src/pages/Profile/ProfilePage.jsx +++ b/src/pages/Profile/ProfilePage.jsx @@ -11,6 +11,8 @@ export default function ProfilePage() { }, [history]); return ( - +
+ +
); } diff --git a/src/pages/Result/ApartmentDetail.jsx b/src/pages/Result/ApartmentDetail.jsx new file mode 100644 index 0000000..19e6e8b --- /dev/null +++ b/src/pages/Result/ApartmentDetail.jsx @@ -0,0 +1,60 @@ +import React from 'react'; + +export default function ApartmentDetail({ apartment }) { + const { + name, + area, + price, + date, + district, + lotNumber, + } = apartment; + + return ( + <> +
+
+ 아파트명: +
+
+ {name} +
+ +
+ 전용면적: +
+
+ {area} +
+ +
+ 거래금액: +
+
+ {price} +
+ +
+ 매매일자: +
+
+ {date} +
+ +
+ 법정동: +
+
+ {district} +
+ +
+ 지번: +
+
+ {lotNumber} +
+
+ + ); +} diff --git a/src/pages/Result/ApartmentDetail.test.jsx b/src/pages/Result/ApartmentDetail.test.jsx new file mode 100644 index 0000000..906e88d --- /dev/null +++ b/src/pages/Result/ApartmentDetail.test.jsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import { render, screen } from '@testing-library/react'; + +import ApartmentDetail from './ApartmentDetail'; + +describe('ApartmentDetail', () => { + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + it('renders apartment details', () => { + render(); + + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('129.92')).toBeInTheDocument(); + expect(screen.getByText('470,000')).toBeInTheDocument(); + expect(screen.getByText('2021-03')).toBeInTheDocument(); + expect(screen.getByText('반포동')).toBeInTheDocument(); + expect(screen.getByText('1')).toBeInTheDocument(); + }); +}); diff --git a/src/pages/Result/Result.jsx b/src/pages/Result/Result.jsx new file mode 100644 index 0000000..e9791fe --- /dev/null +++ b/src/pages/Result/Result.jsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import LinkField from '../../components/LinkField'; + +import Profile from '../Profile/Profile'; + +import ApartmentDetail from './ApartmentDetail'; + +export default function Result({ + profile, apartment, onClick, goBack, +}) { + if (profile.isNew) { + return ( + <> +

+ 정보를 아직 입력하지 않으셨습니다. +

+ + + ); + } + + return ( + <> + + + {apartment + && ( + + )} + + + + ); +} diff --git a/src/pages/Result/Result.test.jsx b/src/pages/Result/Result.test.jsx new file mode 100644 index 0000000..17edf8c --- /dev/null +++ b/src/pages/Result/Result.test.jsx @@ -0,0 +1,103 @@ +import React from 'react'; + +import { + fireEvent, + render, + screen, +} from '@testing-library/react'; + +import given from 'given2'; + +import { initialUserField } from '../../fixtures/initials'; + +import Result from './Result'; + +describe('Result', () => { + const handleClick = jest.fn(); + const goBack = jest.fn(); + + const profile = { + isNew: false, + name: '신형탁', + age: 29, + salary: 5000, + asset: 10000, + }; + + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + function renderResult() { + return render(( + + )); + } + + beforeEach(() => { + jest.clearAllMocks(); + }); + + context('with profile', () => { + given('profile', () => profile); + + it('renders Result', () => { + renderResult(); + + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('129.92')).toBeInTheDocument(); + expect(screen.getByText('470,000')).toBeInTheDocument(); + expect(screen.getByText('2021-03')).toBeInTheDocument(); + expect(screen.getByText('반포동')).toBeInTheDocument(); + expect(screen.getByText('1')).toBeInTheDocument(); + + expect(screen.getByText('신형탁')).toBeInTheDocument(); + expect(screen.getByText('29')).toBeInTheDocument(); + expect(screen.getByText('5000')).toBeInTheDocument(); + expect(screen.getByText('10000')).toBeInTheDocument(); + }); + + it("calls handleClick upon clicking '뒤로가기'", () => { + renderResult(); + + fireEvent.click(screen.getByRole('link', { + name: '뒤로가기', + })); + + expect(goBack).toBeCalledWith({ url: '/apartment' }); + }); + }); + + context('without profile', () => { + given('profile', () => initialUserField); + + it('renders a link for user to enter their profile', () => { + renderResult(); + expect(screen.getByText('정보를 아직 입력하지 않으셨습니다.')).toBeInTheDocument(); + + expect(screen.getByRole('link', { + name: '내 정보 입력 하러가기', + })).toBeInTheDocument(); + }); + + it("calls handleClick upon Clicking '입력' button.", () => { + renderResult(); + + fireEvent.click(screen.getByRole('link', { + name: '내 정보 입력 하러가기', + })); + + expect(handleClick).toBeCalledWith({ url: '/profile/new' }); + }); + }); +}); diff --git a/src/pages/Result/ResultContainer.jsx b/src/pages/Result/ResultContainer.jsx new file mode 100644 index 0000000..c2bb2a6 --- /dev/null +++ b/src/pages/Result/ResultContainer.jsx @@ -0,0 +1,23 @@ +import React from 'react'; + +import { useSelector } from 'react-redux'; + +import { get } from '../../utils/utils'; + +import Result from './Result'; + +export default function ResultContainer({ onClick, goBack }) { + const profile = useSelector(get('userFields')); + const apartment = useSelector(get('apartment')); + + return ( +
+ +
+ ); +} diff --git a/src/pages/Result/ResultContainer.test.jsx b/src/pages/Result/ResultContainer.test.jsx new file mode 100644 index 0000000..19a3a6f --- /dev/null +++ b/src/pages/Result/ResultContainer.test.jsx @@ -0,0 +1,67 @@ +import React from 'react'; + +import { render, screen } from '@testing-library/react'; + +import { useSelector } from 'react-redux'; + +import given from 'given2'; + +import { initialUserField } from '../../fixtures/initials'; + +import ResultContainer from './ResultContainer'; + +describe('ResultContainer', () => { + const profile = { + isNew: false, + name: '신형탁', + age: 29, + salary: 5000, + asset: 10000, + }; + + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + useSelector.mockImplementation((selector) => selector({ + userFields: given.profile, + apartment, + })); + }); + + context('with profile', () => { + given('profile', () => profile); + + it('renders result page', () => { + render(); + + expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); + expect(screen.getByText('129.92')).toBeInTheDocument(); + expect(screen.getByText('470,000')).toBeInTheDocument(); + expect(screen.getByText('2021-03')).toBeInTheDocument(); + expect(screen.getByText('반포동')).toBeInTheDocument(); + expect(screen.getByText('1')).toBeInTheDocument(); + }); + }); + + context('without profile', () => { + given('profile', () => initialUserField); + it('renders link for user to fill up the profile', () => { + render(); + + expect(screen.getByText('정보를 아직 입력하지 않으셨습니다.')).toBeInTheDocument(); + + expect(screen.getByRole('link', { + name: '내 정보 입력 하러가기', + })).toBeInTheDocument(); + }); + }); +}); diff --git a/src/pages/Result/ResultPage.jsx b/src/pages/Result/ResultPage.jsx new file mode 100644 index 0000000..6ad4ce8 --- /dev/null +++ b/src/pages/Result/ResultPage.jsx @@ -0,0 +1,29 @@ +import React, { useCallback } from 'react'; + +import { useHistory } from 'react-router-dom'; + +import ResultContainer from './ResultContainer'; + +export default function ResultPage() { + const history = useHistory(); + + const goBack = useCallback(() => { + history.goBack(); + }, [history]); + + const handleClick = useCallback(({ url }) => { + history.push(url); + }, [history]); + + return ( +
+
+ 결과 +
+ +
+ ); +} diff --git a/src/pages/Result/ResultPage.test.jsx b/src/pages/Result/ResultPage.test.jsx new file mode 100644 index 0000000..43af2e5 --- /dev/null +++ b/src/pages/Result/ResultPage.test.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; + +import { useSelector } from 'react-redux'; + +import { render, screen } from '@testing-library/react'; + +import ResultPage from './ResultPage'; + +describe('ResultPage', () => { + const profile = { + isNew: false, + name: '신형탁', + age: 29, + salary: 5000, + asset: 10000, + }; + + const apartment = { + name: '아크로리버파크', + date: '2021-03', + district: '반포동', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + beforeEach(() => { + useSelector.mockImplementation((selector) => selector({ + apartment, + userFields: profile, + })); + }); + + it('renders result page', () => { + render(( + + + + )); + + expect(screen.getByText('결과')).toBeInTheDocument(); + }); +}); diff --git a/src/pages/Result/index.js b/src/pages/Result/index.js new file mode 100644 index 0000000..2947b83 --- /dev/null +++ b/src/pages/Result/index.js @@ -0,0 +1 @@ +export { default } from './ResultPage'; diff --git a/src/pages/index.js b/src/pages/index.js index efefcdb..c3d4cee 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -2,4 +2,5 @@ export { default as HomePage } from './Home'; export { default as ProfilePage } from './Profile'; export { default as NewProfilePage } from './NewProfile'; export { default as ApartmentPage } from './Apartment'; +export { default as ResultPage } from './Result'; export { default as NotFoundPage } from './NotFound'; diff --git a/src/redux/appSlice.js b/src/redux/appSlice.js index 2a4aab0..db8a4f8 100644 --- a/src/redux/appSlice.js +++ b/src/redux/appSlice.js @@ -39,25 +39,37 @@ const { actions, reducer } = createSlice({ }; }, - setApartment(state, { payload: apartments }) { + setApartments(state, { payload: apartments }) { return { ...state, apartments, }; }, + setApartment(state, { payload: apartment }) { + return { + ...state, + apartment, + }; + }, + }, }); -export const { setUserFields, changeUserFields, setApartment } = actions; +export const { + setUserFields, + changeUserFields, + setApartments, + setApartment, +} = actions; export function loadApartments(apartmentCategory) { return async (dispatch) => { - dispatch(setApartment([])); + dispatch(setApartments([])); const apartments = await fetchApartments({ apartmentCategory }); - dispatch(setApartment(apartments)); + dispatch(setApartments(apartments)); }; } diff --git a/src/redux/appSlice.test.js b/src/redux/appSlice.test.js index b7cd111..d76f47d 100644 --- a/src/redux/appSlice.test.js +++ b/src/redux/appSlice.test.js @@ -5,6 +5,7 @@ import configureStore from 'redux-mock-store'; import reducer, { setUserFields, changeUserFields, + setApartments, setApartment, loadApartments, } from './appSlice'; @@ -115,7 +116,7 @@ describe('setApartments', () => { }, ]; - const state = reducer(initialState, setApartment(APARTMENTS)); + const state = reducer(initialState, setApartments(APARTMENTS)); expect(state.apartments[0].name).toBe('아크로리버파크'); expect(state.apartments[0].price).toBe('470,000'); @@ -125,6 +126,26 @@ describe('setApartments', () => { }); }); +describe('setApartment', () => { + it('changes apartment', () => { + const initialState = { + apartment: {}, + }; + + const apartment = { + name: '아크로리버파크', + date: '2021-03', + area: '129.92', + price: '470,000', + lotNumber: 1, + }; + + const state = reducer(initialState, setApartment(apartment)); + + expect(state.apartment).toEqual(apartment); + }); +}); + describe('loadApartments', () => { const store = mockStore({}); @@ -133,8 +154,8 @@ describe('loadApartments', () => { const actions = store.getActions(); - expect(actions[0]).toEqual(setApartment([])); - expect(actions[1]).toEqual(setApartment([ + expect(actions[0]).toEqual(setApartments([])); + expect(actions[1]).toEqual(setApartments([ { name: '아크로리버파크', date: '2021-03', From c9ea15aeb780bc1844530d0558887905e6a6036e Mon Sep 17 00:00:00 2001 From: dodoongtak Date: Wed, 7 Apr 2021 01:56:37 +0900 Subject: [PATCH 2/2] Revise --- src/App.jsx | 4 ++-- src/App.test.jsx | 8 ++++---- src/components/LinkField.test.jsx | 6 +++--- src/pages/Apartment/ApartmentNavigation.jsx | 4 ++-- src/pages/Apartment/ApartmentNavigation.test.jsx | 2 +- src/pages/Home/Greet.jsx | 14 +++++--------- src/pages/Home/Home.jsx | 2 +- src/pages/Result/Result.jsx | 2 +- src/pages/Result/Result.test.jsx | 2 +- 9 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index e3589f6..1ece9e1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -39,8 +39,8 @@ export default function App() { - - + + diff --git a/src/App.test.jsx b/src/App.test.jsx index c3466a9..daf16b2 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -114,17 +114,17 @@ describe('App', () => { }); }); - context('with path /aparment', () => { + context('with path /aparments', () => { it('renders apartment page', () => { - renderApp({ path: '/apartment' }); + renderApp({ path: '/apartments' }); expect(screen.getByText('거주하고 싶은신 아파트를 선택해주세요')).toBeInTheDocument(); }); }); - context('with path /apartment/:id', () => { + context('with path /apartments/:id', () => { it('redners the apartment page', () => { - renderApp({ path: '/apartment/riverside' }); + renderApp({ path: '/apartments/riverside' }); expect(screen.getByText('아크로리버파크')).toBeInTheDocument(); }); diff --git a/src/components/LinkField.test.jsx b/src/components/LinkField.test.jsx index 411d5b3..d920cbe 100644 --- a/src/components/LinkField.test.jsx +++ b/src/components/LinkField.test.jsx @@ -32,7 +32,7 @@ describe('LinkField', () => { it('calls handleClick upon clicking link', () => { render(( @@ -42,14 +42,14 @@ describe('LinkField', () => { name: '거주하고 싶은 아파트 둘러보기', })); - expect(handleClick).toBeCalledWith({ url: '/apartment' }); + expect(handleClick).toBeCalledWith({ url: '/apartments' }); }); }); context("with '보기' title", () => { render((
  • diff --git a/src/pages/Apartment/ApartmentNavigation.test.jsx b/src/pages/Apartment/ApartmentNavigation.test.jsx index bc78d3b..eae636b 100644 --- a/src/pages/Apartment/ApartmentNavigation.test.jsx +++ b/src/pages/Apartment/ApartmentNavigation.test.jsx @@ -35,6 +35,6 @@ describe('ApartmentNavigation', () => { name: '한강 뷰', })); - expect(handleClick).toBeCalledWith({ url: '/apartment/riverside' }); + expect(handleClick).toBeCalledWith({ url: '/apartments/riverside' }); }); }); diff --git a/src/pages/Home/Greet.jsx b/src/pages/Home/Greet.jsx index 5b116a0..7a3c06e 100644 --- a/src/pages/Home/Greet.jsx +++ b/src/pages/Home/Greet.jsx @@ -23,15 +23,11 @@ export default function Greet({ profile, onClick }) { } return (
    - {isNew && ( - <> - - - )} +
    ); } diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index 1c03e4a..5cfaf9e 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -17,7 +17,7 @@ export default function Home({ profile, onClick }) { )} diff --git a/src/pages/Result/Result.jsx b/src/pages/Result/Result.jsx index e9791fe..479c16d 100644 --- a/src/pages/Result/Result.jsx +++ b/src/pages/Result/Result.jsx @@ -27,7 +27,7 @@ export default function Result({ return ( <> diff --git a/src/pages/Result/Result.test.jsx b/src/pages/Result/Result.test.jsx index 17edf8c..1662a6d 100644 --- a/src/pages/Result/Result.test.jsx +++ b/src/pages/Result/Result.test.jsx @@ -74,7 +74,7 @@ describe('Result', () => { name: '뒤로가기', })); - expect(goBack).toBeCalledWith({ url: '/apartment' }); + expect(goBack).toBeCalledWith({ url: '/apartments' }); }); });