From e75e1ac2431b94b071fd6b9022b5e4863687c718 Mon Sep 17 00:00:00 2001 From: Ivan Belyaev Date: Fri, 6 Jul 2018 22:50:39 +0300 Subject: [PATCH 1/3] Ivan3008/homework_5 --- README.md | 18 ++++- cypress/integration/homework_spec.js | 72 +++++++++++++++++++ src/components/App/App.js | 20 ++++++ src/components/App/index.js | 1 + .../AuthorizeProvider/AuthorizeProvider.js | 38 ++++++++++ .../AuthorizeProvider.test.js | 34 +++++++++ src/components/AuthorizeProvider/index.js | 1 + src/components/Login/Login.js | 17 +++++ src/components/Login/index.js | 1 + src/components/Private/Private.js | 3 + src/components/Private/index.js | 1 + src/components/PrivateRoute/PrivateRoute.js | 10 +++ src/components/PrivateRoute/index.js | 1 + src/components/Public/Public.js | 3 + src/components/Public/index.js | 1 + src/index.css | 5 ++ src/index.js | 10 ++- 17 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 src/components/App/App.js create mode 100644 src/components/App/index.js create mode 100644 src/components/AuthorizeProvider/AuthorizeProvider.js create mode 100644 src/components/AuthorizeProvider/AuthorizeProvider.test.js create mode 100644 src/components/AuthorizeProvider/index.js create mode 100644 src/components/Login/Login.js create mode 100644 src/components/Login/index.js create mode 100644 src/components/Private/Private.js create mode 100644 src/components/Private/index.js create mode 100644 src/components/PrivateRoute/PrivateRoute.js create mode 100644 src/components/PrivateRoute/index.js create mode 100644 src/components/Public/Public.js create mode 100644 src/components/Public/index.js create mode 100644 src/index.css diff --git a/README.md b/README.md index 8549f22..12bd351 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,17 @@ -# Репозиторий домашних заданий +# 5 домашнее задание + +[Пример](http://5ace8f74792f894fd14327c4.sleepy-nightingale-7d715f.netlify.com) + +Почта: student +Пароль: 123 + + +Задачи: +Написать простые компоненты заглушки для страниц `/`, `/private`, `/public`. +Для страницы `/login` написать форму. + +В тестах есть пояснения по поводу необходимых редиректов. +Компонент App должен содержать роуты и ссылки на страницы приложения. + +Компонент на странице `login` так же должен выводить предупреждение о неправильном пароле или почте. -Мастер бранч содержит в себе все основные натсройки проекта, каждое домашнее задание находится в отдельном [бранче](https://github.com/dex157/june-loftschool-react-homeworks/branches). diff --git a/cypress/integration/homework_spec.js b/cypress/integration/homework_spec.js index e69de29..c05398b 100644 --- a/cypress/integration/homework_spec.js +++ b/cypress/integration/homework_spec.js @@ -0,0 +1,72 @@ +describe('Домашняя работа', () => { + beforeEach(() => { + cy.visit('/'); + }); + describe('Верстка', () => { + it('Присутствует тег nav', () => { + cy.get('nav'); + }); + it('Присутствует ссылка «Войти» на /login', () => { + cy + .get('a') + .contains('Войти') + .should('have.attr', 'href', '/login'); + }); + it('Присутствует ссылка «Секретная страница» на /private', () => { + cy + .get('a') + .contains('Секретная страница') + .should('have.attr', 'href', '/private'); + }); + it('Присутствует ссылка «Главная» на /', () => { + cy + .get('a') + .contains('Главная') + .should('have.attr', 'href', '/'); + }); + }); + + describe('Сценарий входа', () => { + it('Приватная страница должна редиректить на страницу /login', () => { + cy.visit('/private'); + + cy.location().should(location => { + expect(location.pathname).to.eq('/login') + }) + }) + it('После ввода логина и пароля должен на странице /login предиректит на /', () => { + cy.visit('/login'); + + cy.get('input[name="email"]').type('student'); + cy.get('input[name="password"]').type('123'); + cy.get('button').click(); + + cy.location().should(location => { + expect(location.pathname).to.eq('/') + }) + }) + it('После ввода логина и неверного пароля должен появится тег p.error', () => { + cy.visit('/login'); + + cy.get('input[name="email"]').type('student'); + cy.get('input[name="password"]').type('wrong password'); + cy.get('button').click(); + + cy.get('p.error') + }) + it('После ввода авторизации можно войти на private', () => { + cy.visit('/login'); + + cy.get('input[name="email"]').type('student'); + cy.get('input[name="password"]').type('123'); + cy.get('button').click(); + + cy + .get('a') + .contains('Секретная страница') + .click() + + cy.get('p').contains('Private page'); + }) + }) +}); diff --git a/src/components/App/App.js b/src/components/App/App.js new file mode 100644 index 0000000..fcd8a3f --- /dev/null +++ b/src/components/App/App.js @@ -0,0 +1,20 @@ +import React, { PureComponent } from 'react'; +import { Switch, withRouter } from 'react-router-dom'; +import { AuthorizeProvider } from 'components/AuthorizeProvider'; + +export class App extends PureComponent { + render() { + return ( + +
+ +
+
+ ); + } +} + +// это важно! +// необходимо использовать этот хок(withRouter), потому что при использовании нескольких контекстов +// реакт-роутер теряет свой контекст. Причина — использование старого апи. +export default withRouter(App); diff --git a/src/components/App/index.js b/src/components/App/index.js new file mode 100644 index 0000000..9122fa1 --- /dev/null +++ b/src/components/App/index.js @@ -0,0 +1 @@ +export { default } from './App'; diff --git a/src/components/AuthorizeProvider/AuthorizeProvider.js b/src/components/AuthorizeProvider/AuthorizeProvider.js new file mode 100644 index 0000000..47766c5 --- /dev/null +++ b/src/components/AuthorizeProvider/AuthorizeProvider.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; + +const { Provider, Consumer } = React.createContext({ isAuthorized: false }); + +// к сожалению тест раннер еще не готов к тестам с новым контекстом +// по этому тут так много кода, изучайте как следует! +// немного кода я все таки вырезал ^^ +class AuthorizeProvider extends Component { + render() { + const { children } = this.props; + const { isAuthorized } = this.state; + + return ( + + {children} + + ); + } +} + +const AuthHOC = WrappedComponent => + class extends Component { + static displayName = 'AuthHOCWrapper'; + render() { + return ( + + {({ isAuthorized, authorizeUser }) => ( + + )} + + ); + } + }; + +export { AuthorizeProvider, AuthHOC }; diff --git a/src/components/AuthorizeProvider/AuthorizeProvider.test.js b/src/components/AuthorizeProvider/AuthorizeProvider.test.js new file mode 100644 index 0000000..5b499b1 --- /dev/null +++ b/src/components/AuthorizeProvider/AuthorizeProvider.test.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { AuthorizeProvider } from './AuthorizeProvider'; +import { shallow } from 'enzyme'; + +describe('компонента AuthorizeProvider', () => { + describe('Компонента при создании', () => { + const wrapper = shallow(); + + it('Стейт содержит только поле isAuthorized: false', () => { + expect(wrapper.state()).toEqual({ isAuthorized: false }); + }); + + it('Компонента имеет метод authorzeUser', () => { + expect(wrapper.instance().authorizeUser).toBeDefined(); + }); + }); + + describe('поведение метода autorizeUser', () => { + const wrapper = shallow(); + + it('С аргументами ("student", "123") возвращает true', () => { + expect(wrapper.instance().authorizeUser('student', '123')).toBeTruthy(); + }); + + it('С отличными от правильных аргументов например ("student", "124") возвращает false', () => { + expect(wrapper.instance().authorizeUser('student', '124')).toBeFalsy(); + }); + + it('С аргументами ("student", "123") устанавливает state в {isAuthorized: true}', () => { + wrapper.instance().authorizeUser('student', '123'); + expect(wrapper.state()).toEqual({ isAuthorized: true }); + }); + }); +}); diff --git a/src/components/AuthorizeProvider/index.js b/src/components/AuthorizeProvider/index.js new file mode 100644 index 0000000..6b5fc02 --- /dev/null +++ b/src/components/AuthorizeProvider/index.js @@ -0,0 +1 @@ +export { AuthorizeProvider, AuthHOC } from './AuthorizeProvider'; diff --git a/src/components/Login/Login.js b/src/components/Login/Login.js new file mode 100644 index 0000000..fbbb10d --- /dev/null +++ b/src/components/Login/Login.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react'; +import { AuthHOC } from 'components/AuthorizeProvider'; +import { Redirect } from 'react-router-dom'; + +class Login extends Component { + handleChange = e => { + this.setState({ [e.target.name]: e.target.value }); + }; + + render() { + const { isAuthorized } = this.props; + + return isAuthorized ? : null; + } +} + +export default AuthHOC(Login); diff --git a/src/components/Login/index.js b/src/components/Login/index.js new file mode 100644 index 0000000..2a741cd --- /dev/null +++ b/src/components/Login/index.js @@ -0,0 +1 @@ +export { default } from './Login'; diff --git a/src/components/Private/Private.js b/src/components/Private/Private.js new file mode 100644 index 0000000..1373354 --- /dev/null +++ b/src/components/Private/Private.js @@ -0,0 +1,3 @@ +import React from 'react'; + +export default () =>

Private page

; diff --git a/src/components/Private/index.js b/src/components/Private/index.js new file mode 100644 index 0000000..2e5cf79 --- /dev/null +++ b/src/components/Private/index.js @@ -0,0 +1 @@ +export { default } from './Private'; diff --git a/src/components/PrivateRoute/PrivateRoute.js b/src/components/PrivateRoute/PrivateRoute.js new file mode 100644 index 0000000..e9b1047 --- /dev/null +++ b/src/components/PrivateRoute/PrivateRoute.js @@ -0,0 +1,10 @@ +import React, { PureComponent } from 'react'; +import { AuthHOC } from 'components/AuthorizeProvider'; + +class PrivateRoute extends PureComponent { + render() { + return null; + } +} + +export default AuthHOC(PrivateRoute); diff --git a/src/components/PrivateRoute/index.js b/src/components/PrivateRoute/index.js new file mode 100644 index 0000000..6ebc0bb --- /dev/null +++ b/src/components/PrivateRoute/index.js @@ -0,0 +1 @@ +export { default } from './PrivateRoute'; diff --git a/src/components/Public/Public.js b/src/components/Public/Public.js new file mode 100644 index 0000000..86a1999 --- /dev/null +++ b/src/components/Public/Public.js @@ -0,0 +1,3 @@ +import React from 'react'; + +export default () =>

Public page

; diff --git a/src/components/Public/index.js b/src/components/Public/index.js new file mode 100644 index 0000000..b822f7c --- /dev/null +++ b/src/components/Public/index.js @@ -0,0 +1 @@ +export { default } from './Public'; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..32c0d80 --- /dev/null +++ b/src/index.css @@ -0,0 +1,5 @@ +body { + margin: 0; + padding: 20px; + font-family: sans-serif; +} diff --git a/src/index.js b/src/index.js index f7c918f..323a5a5 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; - +import './index.css'; import App from 'components/App'; +import { BrowserRouter } from 'react-router-dom'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render( + + + , + document.getElementById('root') +); From d4f71fb4e6844fb271ce3797920d15a0e471c6cc Mon Sep 17 00:00:00 2001 From: Ivan Belyaev Date: Fri, 6 Jul 2018 23:04:00 +0300 Subject: [PATCH 2/3] Ivan3008/homework_5 --- src/components/App/App.js | 22 +++++++++++++--- .../AuthorizeProvider/AuthorizeProvider.js | 11 ++++++++ src/components/Login/Login.js | 25 ++++++++++++++++--- src/components/Private/Private.js | 7 +++++- src/components/Public/Public.js | 6 ++++- 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/components/App/App.js b/src/components/App/App.js index fcd8a3f..7970938 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -1,13 +1,29 @@ import React, { PureComponent } from 'react'; -import { Switch, withRouter } from 'react-router-dom'; -import { AuthorizeProvider } from 'components/AuthorizeProvider'; +import { Switch, withRouter, Link, Route } from 'react-router-dom'; +import { AuthorizeProvider, AuthHOC } from 'components/AuthorizeProvider'; +import Login from 'components/Login'; +import Private from 'components/Private'; +import Public from 'components/Public'; export class App extends PureComponent { render() { + const wrappedPrivate = AuthHOC(Private) + const wrappedLogin = AuthHOC(Login) return (
- + +
+ + + + + +
); diff --git a/src/components/AuthorizeProvider/AuthorizeProvider.js b/src/components/AuthorizeProvider/AuthorizeProvider.js index 47766c5..af7dac4 100644 --- a/src/components/AuthorizeProvider/AuthorizeProvider.js +++ b/src/components/AuthorizeProvider/AuthorizeProvider.js @@ -6,6 +6,17 @@ const { Provider, Consumer } = React.createContext({ isAuthorized: false }); // по этому тут так много кода, изучайте как следует! // немного кода я все таки вырезал ^^ class AuthorizeProvider extends Component { + state = { + isAuthorized: false + }; + + authorizeUser = (email, password) => { + if (email === 'student' && password === '123'){ + this.setState({ isAuthorized: true }); + return true; + } + }; + render() { const { children } = this.props; const { isAuthorized } = this.state; diff --git a/src/components/Login/Login.js b/src/components/Login/Login.js index fbbb10d..418c136 100644 --- a/src/components/Login/Login.js +++ b/src/components/Login/Login.js @@ -1,16 +1,35 @@ -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; import { AuthHOC } from 'components/AuthorizeProvider'; import { Redirect } from 'react-router-dom'; class Login extends Component { - handleChange = e => { + state = { + email: '', + password: '' + }; + + handleChange = (e) => { this.setState({ [e.target.name]: e.target.value }); }; + checkPass = () => { + this.props.authorizeUser(this.state.email, this.state.password); + } + render() { const { isAuthorized } = this.props; - return isAuthorized ? : null; + return isAuthorized ? ( + + ) : ( + +
+ + +
+ +
+ ); } } diff --git a/src/components/Private/Private.js b/src/components/Private/Private.js index 1373354..846bf7f 100644 --- a/src/components/Private/Private.js +++ b/src/components/Private/Private.js @@ -1,3 +1,8 @@ import React from 'react'; +import { Redirect } from 'react-router-dom'; -export default () =>

Private page

; +const Private = (props) => { + return props.isAuthorized ?

Private page

: ; +}; + +export default Private; diff --git a/src/components/Public/Public.js b/src/components/Public/Public.js index 86a1999..ea64498 100644 --- a/src/components/Public/Public.js +++ b/src/components/Public/Public.js @@ -1,3 +1,7 @@ import React from 'react'; -export default () =>

Public page

; +const Public = () => { + return

Public page

; +}; + +export default Public; From f81ca2efd57a53a7450d80c595c82cd79a085ee9 Mon Sep 17 00:00:00 2001 From: Ivan Belyaev Date: Fri, 6 Jul 2018 23:10:56 +0300 Subject: [PATCH 3/3] Ivan3008/homework_5 --- src/components/App/App.js | 12 ------------ .../AuthorizeProvider/AuthorizeProvider.js | 3 --- src/components/Login/Login.js | 15 --------------- src/components/Private/Private.js | 5 ----- src/components/Public/Public.js | 4 ---- 5 files changed, 39 deletions(-) diff --git a/src/components/App/App.js b/src/components/App/App.js index ac0f677..7970938 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -1,5 +1,4 @@ import React, { PureComponent } from 'react'; -<<<<<<< HEAD import { Switch, withRouter, Link, Route } from 'react-router-dom'; import { AuthorizeProvider, AuthHOC } from 'components/AuthorizeProvider'; import Login from 'components/Login'; @@ -25,17 +24,6 @@ export class App extends PureComponent { -======= -import { Switch, withRouter } from 'react-router-dom'; -import { AuthorizeProvider } from 'components/AuthorizeProvider'; - -export class App extends PureComponent { - render() { - return ( - -
- ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1
); diff --git a/src/components/AuthorizeProvider/AuthorizeProvider.js b/src/components/AuthorizeProvider/AuthorizeProvider.js index 9630608..af7dac4 100644 --- a/src/components/AuthorizeProvider/AuthorizeProvider.js +++ b/src/components/AuthorizeProvider/AuthorizeProvider.js @@ -6,7 +6,6 @@ const { Provider, Consumer } = React.createContext({ isAuthorized: false }); // по этому тут так много кода, изучайте как следует! // немного кода я все таки вырезал ^^ class AuthorizeProvider extends Component { -<<<<<<< HEAD state = { isAuthorized: false }; @@ -18,8 +17,6 @@ class AuthorizeProvider extends Component { } }; -======= ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1 render() { const { children } = this.props; const { isAuthorized } = this.state; diff --git a/src/components/Login/Login.js b/src/components/Login/Login.js index e103ab4..418c136 100644 --- a/src/components/Login/Login.js +++ b/src/components/Login/Login.js @@ -1,13 +1,8 @@ -<<<<<<< HEAD import React, { Component, Fragment } from 'react'; -======= -import React, { Component } from 'react'; ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1 import { AuthHOC } from 'components/AuthorizeProvider'; import { Redirect } from 'react-router-dom'; class Login extends Component { -<<<<<<< HEAD state = { email: '', password: '' @@ -35,16 +30,6 @@ class Login extends Component { ); -======= - handleChange = e => { - this.setState({ [e.target.name]: e.target.value }); - }; - - render() { - const { isAuthorized } = this.props; - - return isAuthorized ? : null; ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1 } } diff --git a/src/components/Private/Private.js b/src/components/Private/Private.js index 4726d80..846bf7f 100644 --- a/src/components/Private/Private.js +++ b/src/components/Private/Private.js @@ -1,5 +1,4 @@ import React from 'react'; -<<<<<<< HEAD import { Redirect } from 'react-router-dom'; const Private = (props) => { @@ -7,7 +6,3 @@ const Private = (props) => { }; export default Private; -======= - -export default () =>

Private page

; ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1 diff --git a/src/components/Public/Public.js b/src/components/Public/Public.js index 7f7542f..ea64498 100644 --- a/src/components/Public/Public.js +++ b/src/components/Public/Public.js @@ -1,11 +1,7 @@ import React from 'react'; -<<<<<<< HEAD const Public = () => { return

Public page

; }; export default Public; -======= -export default () =>

Public page

; ->>>>>>> 4679c637cdeca45dadaff1d41e614a99ac5934c1