Skip to content

Commit

Permalink
rebase: github login (#20)
Browse files Browse the repository at this point in the history
* rebase: github login

* chore: update snapshots & add [thunk] to TopBar.test.js

* chore: update snapshots & add [thunk] to TopBar.test.js

* chore: add auth.reducer tests

* chore: auth.actions specs, part I

* Fix tests and packege-lock

* Bugfix
  • Loading branch information
kgajowy authored and Piotr Zientara committed Jun 10, 2018
1 parent 5d8a439 commit 42de4ea
Show file tree
Hide file tree
Showing 19 changed files with 1,065 additions and 50 deletions.
592 changes: 577 additions & 15 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"auth0-js": "^9.6.0",
"prop-types": "^15.6.1",
"react": "^16.3.2",
"react-dom": "^16.3.2",
Expand All @@ -17,6 +18,7 @@
"build": "react-scripts build",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage --collectCoverageFrom=**/src/**/*.js",
"eject": "react-scripts eject",
"precommit": "lint-staged"
},
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<script type="text/javascript" src="../node_modules/auth0-js/build/auth0.js"></script>
</head>
<body>
<noscript>
Expand Down
3 changes: 3 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import WorkshopForm from './components/WorkshopForm';
import Header from './components/Header';
import TopicContainer from './topic/TopicContainer';

import AuthCallbackContainer from './shared/components/AuthCallbackContainer';

class App extends Component {
render() {
return (
Expand All @@ -15,6 +17,7 @@ class App extends Component {
<WorkshopForm />
<TopicContainer />
</main>
<AuthCallbackContainer/>
</React.Fragment>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/App.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ exports[`Should render <App /> should match snapshot 1`] = `
<WorkshopForm />
<Connect(TopicContainer) />
</main>
<Connect(AuthCallbackContainer) />
</React.Fragment>
`;
80 changes: 80 additions & 0 deletions src/actions/__tests__/auth.actions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { LOGIN_REQUEST, LOGIN_REQUEST_ERROR, LOGIN_REQUEST_SUCCESS } from '../action_types';
import { initialState } from '../../reducers/auth.reducer';
import { loginError, loginPending, loginSuccess, requestLogin } from '../auth.actions';
import { GithubUserModel } from '../../shared/models/GithubUserModel';
import { AuthService } from '../../shared/services/AuthService';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('auth action creators', () => {

describe('when login request is called', () => {
const expectedAction = {
type: LOGIN_REQUEST
};
it('should create action', () => {
const action = loginPending();
expect(action).toEqual(expectedAction);
});
});

describe('when "user logged" action is called', () => {
const user = GithubUserModel.fromOAuth0({});
const expectedAction = {
type: LOGIN_REQUEST_SUCCESS,
payload: user
};
it('should create action', () => {
const action = loginSuccess(user);
expect(action).toEqual(expectedAction);
});
});

describe('when "user logged: finished with error', () => {
const error = new Error('Error');
const expectedAction = {
type: LOGIN_REQUEST_ERROR,
payload: error
};
it('should create action', () => {
const action = loginError(error);
expect(action).toEqual(expectedAction);
});
});

describe('when login request action is called', () => {

let store;
let serviceMock;

beforeEach(() => {
store = mockStore({ auth: initialState });
});

describe('when AuthService returns an error', () => {
const fakeError = new Error('Error');

beforeEach(() => {
serviceMock = jest.spyOn(AuthService, 'signIn').mockImplementation(() => Promise.reject(fakeError));
store.dispatch(requestLogin());
});

afterEach(() => {
serviceMock.mockRestore();
});

it('should dispatch LOGIN_REQUEST', () => {
expect(store.getActions()[0].type).toBe(LOGIN_REQUEST);
});

it('should create LOGIN_REQUEST_ERROR action', () => {
expect(store.getActions()[1].type).toBe(LOGIN_REQUEST_ERROR);
expect(store.getActions()[1].payload).toBe(fakeError);
});
});

});
});
10 changes: 8 additions & 2 deletions src/actions/action_types.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_REQUEST_SUCCESS = 'LOGIN_REQUEST_SUCCESS';
export const LOGIN_REQUEST_ERROR = 'LOGIN_REQUEST_ERROR';
export const LOGIN_RESTORE_SESSION_REQUEST = 'LOGIN_RESTORE_SESSION';

export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
export const LOGOUT_REQUEST_SUCCESS = 'LOGOUT_REQUEST_SUCCESS';
export const LOGOUT_REQUEST_ERROR = 'LOGOUT_REQUEST_ERROR';

export const TOPICS_GET_REQUEST = 'TOPICS_GET_REQUEST';
export const TOPICS_GET_REQUEST_SUCCESS = 'TOPICS_GET_REQUEST_SUCCESS';
Expand Down
85 changes: 80 additions & 5 deletions src/actions/auth.actions.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,87 @@
import { LOGIN, LOGOUT } from '../actions/action_types';
import { AuthService } from '../shared/services/AuthService';
import {
LOGIN_REQUEST, LOGIN_REQUEST_ERROR, LOGIN_REQUEST_SUCCESS, LOGIN_RESTORE_SESSION_REQUEST, LOGOUT_REQUEST,
LOGOUT_REQUEST_ERROR,
LOGOUT_REQUEST_SUCCESS
} from './action_types';

export const login = () => {
export const requestLogin = () => {
return (dispatch) => {
dispatch(loginPending());
AuthService.signIn().then(result => {
dispatch(loginSuccess(result));
}).catch(error => {
dispatch(loginError(error));
});
};
};

export const loginPending = () => {
return {
type: LOGIN_REQUEST
};
};

export const loginError = (error) => {
return {
type: LOGIN_REQUEST_ERROR,
payload: error
};
};

export const loginSuccess = (user) => {
return {
type: LOGIN_REQUEST_SUCCESS,
payload: user
};
};

export const requestLogout = () => {
return (dispatch) => {
dispatch(logoutPending());
AuthService.signOut().then(() => {
dispatch(logoutSuccess());
}).catch(error => {
dispatch(logoutError(error));
});
};
};

export const logoutPending = () => {
return {
type: LOGOUT_REQUEST
};
};

export const logoutError = (error) => {
return {
type: LOGIN
type: LOGOUT_REQUEST_ERROR,
payload: error
};
};
export const logout = () => {

export const logoutSuccess = () => {
return {
type: LOGOUT
type: LOGOUT_REQUEST_SUCCESS
};
};

export const restoringSession = () => {
return {
type: LOGIN_RESTORE_SESSION_REQUEST
};
};

export const restoreSession = () => {
return async (dispatch) => {
dispatch(restoringSession());
try {
const logged = await AuthService.isLogged();
if (logged) {
dispatch(loginSuccess(await AuthService.getUser()));
} //else if just 'not logged'
} catch (err) {
dispatch(requestLogout());//force clean up
}
};
};
36 changes: 23 additions & 13 deletions src/components/TopBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,33 @@ import React from 'react';
import styled from 'styled-components';
import Button from './Button';
import { connect } from 'react-redux';
import { login, logout } from './../actions/auth.actions';
import Text from './Text';
import Colors from '../styles/Colors';
import PropTypes from 'prop-types';
import Container from '../shared/components/Container';

import { requestLogin, requestLogout } from '../actions/auth.actions';
import ActivityIndicator from '../shared/components/ActivityIndicator';
//TODO make some AppError model and HelloGHError model (extends AppError)
class TopBar extends React.Component {

onClick = () => {
const { loggedUser, login, logout } = this.props;
const onPressAction = loggedUser ? logout : login;
const { logged, requestLogout, requestLogin } = this.props;
const onPressAction = logged ? requestLogout : requestLogin;
onPressAction();
};

render() {
const { loggedUser } = this.props;
const buttonText = loggedUser ? 'Wyloguj' : 'Zaloguj';
const { logged, pending, user, error } = this.props;
const buttonText = logged ? 'Wyloguj' : 'Zaloguj';
return (
<Container backgroundColor={Colors.black} inner={false}>
<Inner>
<Text type="basic" color={ Colors.white } display={ 'inline' }>
Zaloguj się przez GitHub
{!logged && 'Zaloguj się'}
{logged && user && user.email}
</Text>
{pending && <ActivityIndicator/>}
{error && <p>Błąd: {JSON.stringify(error)}</p>}
<Button marginLeft='10px' onClick={this.onClick} type="primary">
{ buttonText }
</Button>
Expand All @@ -35,18 +39,24 @@ class TopBar extends React.Component {
}

TopBar.propTypes = {
loggedUser: PropTypes.bool,
login: PropTypes.func,
logout: PropTypes.func
logged: PropTypes.bool,
pending: PropTypes.bool,
error: PropTypes.any,
user: PropTypes.any,
requestLogin: PropTypes.func,
requestLogout: PropTypes.func
};

const mapStateToProps = state => ({
loggedUser: state.auth.loggedUser
logged: state.auth.logged,
pending: state.auth.pending,
user: state.auth.user,
error: state.auth.error,
});

const mapDispatchToProps = dispatch => ({
login: () => dispatch(login()),
logout: () => dispatch(logout())
requestLogin: () => dispatch(requestLogin()),
requestLogout: () => dispatch(requestLogout())
});

const Inner = styled.section`
Expand Down
17 changes: 10 additions & 7 deletions src/components/__tests__/TopBar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import configureMockStore from 'redux-mock-store';
import 'jest-styled-components';
import TopBar from '../TopBar';
import Button from '../Button';
import { LOGIN, LOGOUT } from '../../actions/action_types';
import { LOGIN_REQUEST, LOGOUT_REQUEST } from '../../actions/action_types';

import thunk from 'redux-thunk';
const middlewares = [thunk];

configure({ adapter: new Adapter() });
const mockStore = configureMockStore();
const mockStore = configureMockStore(middlewares);

describe('<TopBar>', () => {
const initialState = {
auth: {
loggedUser: false
logged: false
}
};
let wrapper;
Expand All @@ -32,7 +35,7 @@ describe('<TopBar>', () => {
beforeEach(() => {
store = mockStore({
auth: {
loggedUser: true
logged: true
}
});
wrapper = shallow(<TopBar store={store}/>).dive();
Expand All @@ -47,7 +50,7 @@ describe('<TopBar>', () => {
button.simulate('click');
const actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(LOGOUT);
expect(actions[0].type).toBe(LOGOUT_REQUEST);
});

});
Expand All @@ -57,7 +60,7 @@ describe('<TopBar>', () => {
beforeEach(() => {
store = mockStore({
auth: {
loggedUser: false
logged: false
}
});
wrapper = shallow(<TopBar store={store}/>).dive();
Expand All @@ -72,7 +75,7 @@ describe('<TopBar>', () => {
button.simulate('click');
const actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(LOGIN);
expect(actions[0].type).toBe(LOGIN_REQUEST);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/__tests__/__snapshots__/TopBar.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports[`<TopBar> should render 1`] = `
display="inline"
type="basic"
>
Zaloguj się przez GitHub
Zaloguj się
</Text>
<Button
marginLeft="10px"
Expand Down
Loading

0 comments on commit 42de4ea

Please sign in to comment.