Skip to content

Commit

Permalink
Merge pull request #53 from Readme-Monster/Feature-38
Browse files Browse the repository at this point in the history
[RM-38]: HomepageUI
  • Loading branch information
lee-ji-hong authored Apr 25, 2024
2 parents f24ef9d + 13aa194 commit ac8212e
Show file tree
Hide file tree
Showing 18 changed files with 1,219 additions and 50 deletions.
2 changes: 0 additions & 2 deletions README.old.md

This file was deleted.

40 changes: 40 additions & 0 deletions __tests__/unit/App.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { render, screen } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import React, { ReactNode } from 'react';
import { ReactElement } from 'react';
import '@testing-library/jest-dom';

import { Routes as AppRoutes } from '../../src/pages/Routes';
import App from "../../src/pages/App";

describe('Routes 테스트', () => {
test('기본 경로("/")에서 HomePage 컴포넌트가 렌더링되어야 함', () => {
render(
<MemoryRouter initialEntries={['/']}>
<AppRoutes />
</MemoryRouter>
);
expect(screen.getByTestId('home')).toBeInTheDocument();
});

test('잘못된 경로에 접근했을 때 루트 경로("/")로 리다이렉트되어야 함', () => {
render(
<MemoryRouter initialEntries={['/some/wrong/path']}>
<AppRoutes />
</MemoryRouter>
);
expect(screen.getByTestId('home')).toBeInTheDocument();
});
});

describe('App',()=>{
test('App 컴포넌트가 성공적으로 렌더링되어야 함', async () => {
renderWithRouter(<App />, { route: '/' });
const linkElement = screen.getByTestId('home');
expect(await linkElement).toBeInTheDocument();
});

function renderWithRouter(Component: ReactElement, options: { route: string }) {
return render(<MemoryRouter initialEntries={[options.route]}>{Component}</MemoryRouter>);
}
})
59 changes: 59 additions & 0 deletions __tests__/unit/HomePage/Page.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import React from 'react';
import '@testing-library/jest-dom';

import HomePage, { Button } from "../../../src/pages/HomePage";

describe('HomePage 테스트', () => {
test('HomePage가 정상적으로 렌더링되어야 함', () => {
render(
<MemoryRouter initialEntries={['/']}>
<HomePage />
</MemoryRouter>
);

expect(screen.getByTestId('logo')).toBeInTheDocument();
expect(screen.getByTestId('title')).toBeInTheDocument();
expect(screen.getByTestId('description')).toBeInTheDocument();
});

test("올바른 props로 두 개의 버튼이 렌더링되어야 함", () => {
render(
<MemoryRouter initialEntries={['/']}>
<HomePage />
</MemoryRouter>);

const firstButton = screen.getByTestId("firstButton");
expect(firstButton).toBeInTheDocument();

const secondButton = screen.getByTestId("secondButton");
expect(secondButton).toBeInTheDocument();
});

test("버튼 클릭시 올바른 URL로 이동해야 함", () => {
const pushMock = jest.fn();

const useRouterMock = jest.spyOn(require("../../../src/pages/routing"), "useRouter").mockReturnValue({
push: pushMock
});
render(
<MemoryRouter initialEntries={['/']}>
<HomePage />
</MemoryRouter>
);

// 첫 번째 버튼 클릭
const firstButton = screen.getByTestId("firstButton");
fireEvent.click(firstButton);
expect(pushMock).toHaveBeenCalledWith("/login");

// 두 번째 버튼 클릭
const secondButton = screen.getByTestId("secondButton");
fireEvent.click(secondButton);
expect(pushMock).toHaveBeenCalledWith("/signup");

useRouterMock.mockRestore();
});
});
2 changes: 1 addition & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-react"],
presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
};
3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
testEnvironment: 'jsdom',
};
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.34.1",
"husky": "^9.0.11",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^15.2.2",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
Expand All @@ -41,17 +43,18 @@
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"test": "jest",
"eject": "react-scripts eject",
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
"prepare": "husky install"
},
"dependencies": {
"bootstrap": "^5.3.3",
"@testing-library/user-event": "^14.5.2",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
"react-dom": "^18.2.0",
"react-router-dom": "6",
"react-router-dom": "^6.23.0",
"react-scripts": "^5.0.1",
"web-vitals": "^3.5.2"
},
Expand Down
Binary file added public/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./pages/App";
import "../src/styles/global.css";
import "bootstrap/dist/css/bootstrap.min.css";
Expand All @@ -10,9 +10,11 @@ const rootElement = document.getElementById("root");
if (rootElement) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>,
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
);
} else {
console.error("Failed to find the root element");
Expand Down
9 changes: 2 additions & 7 deletions src/pages/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import AppLayout from "../layout/AppLayout";
import NotFoundPage from "./NotFoundPage/NotFoundPage";
import { Routes } from "./Routes";

function App() {
return (
<div className="flex-Center h-[100vh]">
<Routes>
<Route path="/" element={<AppLayout />}></Route>
<Route path="*" element={<NotFoundPage />} />
</Routes>
<Routes/>
</div>
);
}
Expand Down
62 changes: 62 additions & 0 deletions src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import { RoutePath,useRouter } from "./routing";

interface ButtonProps {
url: RoutePath;
color: string;
backgroundColor: string;
description: string;
hover: string;
testId: string;
}

function HomePage() {

return (
<div>
<div className="flex-Center flex-col" data-testid="home">
<img alt="로고" src="/img/logo.png" width="140" height="140" data-testid="logo"/>
<span className="text-textPrimary text-3xl md:text-6xl sm:text-5xl font-bold line-lg" data-testid="title">README-MONSTER</span>
<p className="max-w-md mx-auto mt-3 text-textSecondary text-gray-500 display-contents sm:text-lg md:mt-5 md:text-xl md:max-w-3xl" data-testid="description">
<span>당신의 프로젝트 README를</span><span>자동으로 만들 수 있는 가장 빠르고 쉬운 방법</span>
</p>
</div>
<div className="flex justify-center mt-6" data-testid="button">
<Button url="/login" color="text-white" backgroundColor="bg-textBlue" hover="hover:bg-textBlueHover" description="시작하기" testId="firstButton"/>
<Button url="/signup" color="text-textPrimary" backgroundColor="bg-gray-200" hover="hover:bg-textgreyHover" description="더 알아보기" testId="secondButton"/>
</div>
</div>
);
}

export default HomePage;

export function Button({ url, color, backgroundColor, description, hover, testId }: ButtonProps){
const router = useRouter();
const handleClick = () => {
router.push(url);
};

return (
<div className="flex flex-col items-center">
<span className="inline-flex rounded-md shadow">
<a
className={`
inline-flex items-center px-4 py-2
font-medium text-xl
${backgroundColor}
${hover}
border border-transparent rounded-lg
${color}
w-[250px] h-[54px]
justify-center`
}
onClick={handleClick}
data-testid={testId}
>
{description}
</a>
</span>
</div>
);
}
13 changes: 13 additions & 0 deletions src/pages/Routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { Route, Routes as ReactRouterRoutes, Navigate } from "react-router-dom";
import HomePage from "./HomePage";


export const Routes = () => {
return (
<ReactRouterRoutes>
<Route path="/" element={<HomePage />} />
<Route path="*" element={<Navigate replace to="/" />} />
</ReactRouterRoutes>
);
};
23 changes: 23 additions & 0 deletions src/pages/routing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useNavigate } from "react-router-dom";
import { useMemo } from "react";
import { stringify } from "qs";

export function useRouter() {
const navigate = useNavigate();
return useMemo(() => {
return {
back(steps = 1) {
navigate(-steps);
},
push(path: RoutePath, search?: string) {
navigate({ pathname: path, search: search ? stringify(search, { indices: false }) : undefined });
},
};
}, [navigate]);
}

export type RoutePath =
| "/"
| "/login"
| "/signup";

1 change: 0 additions & 1 deletion src/react-app-env.d.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
.flex-Center {
@apply flex items-center justify-center
}
.line-sm {line-height: 1em !important;}
.line-lg {line-height: 1.2em !important;}

@media (max-width: 768px) { /* Tailwind의 md 브레이크포인트는 기본적으로 768px입니다. */
.display-contents {display: contents;}
}
}
7 changes: 0 additions & 7 deletions src/test.js

This file was deleted.

2 changes: 2 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ module.exports = {
textPrimary: "#343A40",
textSecondary: "#868E96",
textBlue: "#4B88FF",
textBlueHover: "#2F73FA",
textgreyHover:"#E0DEDE"
}
},
},
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
},
"include": [
"src"
]
, "jest.config.js", "App.test.tsx" ]
}
Loading

0 comments on commit ac8212e

Please sign in to comment.