From 5d190551e6b5d7a5684ceaaed8eddca83b8c7988 Mon Sep 17 00:00:00 2001 From: vitaliy Date: Sun, 8 Jul 2018 21:31:26 +0300 Subject: [PATCH 1/2] ht2_2: show event list and loader --- admin/src/App.js | 2 + admin/src/components/common/loader.js | 3 + admin/src/components/events/list.js | 7 +++ admin/src/ducks/events.js | 90 +++++++++++++++++++++++++++ admin/src/redux/reducer.js | 4 +- admin/src/redux/saga.js | 3 +- admin/src/routes/events.js | 32 ++++++++++ 7 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 admin/src/components/common/loader.js create mode 100644 admin/src/components/events/list.js create mode 100644 admin/src/ducks/events.js create mode 100644 admin/src/routes/events.js diff --git a/admin/src/App.js b/admin/src/App.js index c54f214..0f7f786 100644 --- a/admin/src/App.js +++ b/admin/src/App.js @@ -3,12 +3,14 @@ import { Route } from 'react-router-dom' import ProtectedRoute from './components/common/protected-route' import Auth from './routes/auth' import Admin from './routes/admin' +import Events from './routes/events' class App extends Component { render() { return (
+
) diff --git a/admin/src/components/common/loader.js b/admin/src/components/common/loader.js new file mode 100644 index 0000000..a169e58 --- /dev/null +++ b/admin/src/components/common/loader.js @@ -0,0 +1,3 @@ +import React from 'react' + +export default () =>

loading...

diff --git a/admin/src/components/events/list.js b/admin/src/components/events/list.js new file mode 100644 index 0000000..90e8a5e --- /dev/null +++ b/admin/src/components/events/list.js @@ -0,0 +1,7 @@ +import React from 'react' + +const EventsList = ({ events }) => { + return +} + +export default EventsList diff --git a/admin/src/ducks/events.js b/admin/src/ducks/events.js new file mode 100644 index 0000000..0c7916a --- /dev/null +++ b/admin/src/ducks/events.js @@ -0,0 +1,90 @@ +import { appName } from '../config' +import { Record, List } from 'immutable' +import { createSelector } from 'reselect' +import firebase from 'firebase/app' +import { put, takeEvery, call } from 'redux-saga/effects' + +/** + * Constants + * */ +export const moduleName = 'events' +const prefix = `${appName}/${moduleName}` +export const EVENTS_REQUEST = `${prefix}/EVENTS_REQUES` +export const EVENTS_REQUES_SUCCESS = `${prefix}/EVENTS_REQUES_SUCCESS` + +/** + * Reducer + * */ +const ReducerState = Record({ + entities: new List([]), + loading: false +}) + +const EventRecord = Record({ + title: null, + url: null, + where: null, + when: null, + month: null, + submissionDeadline: null +}) + +export default function reducer(state = new ReducerState(), action) { + const { type, payload } = action + + switch (type) { + case EVENTS_REQUEST: + return state.set('loading', true) + case EVENTS_REQUES_SUCCESS: + const { events } = payload + const newState = state.set('loading', false) + return Object.keys(events).reduce((acc, key) => { + return acc.update('entities', (entities) => + entities.push(new EventRecord(events[key])) + ) + }, newState) + default: + return state + } +} +/** + * Selectors + * */ + +export const stateSelector = (state) => state[moduleName] +export const eventsSelector = createSelector(stateSelector, (state) => + state.entities.valueSeq().toArray() +) +export const loadingSelector = createSelector( + stateSelector, + (state) => state.loading +) + +/** + * Action Creators + * */ + +export function loadEvents() { + return { + type: EVENTS_REQUEST + } +} + +/** + * Sagas + */ + +export function* loadEvensSaga(action) { + const ref = firebase.database().ref('events') + const data = yield call([ref, ref.once], 'value') + const events = data.val() + + yield put({ + type: EVENTS_REQUES_SUCCESS, + payload: { events } + }) +} + +export function* saga() { + yield takeEvery(EVENTS_REQUEST, loadEvensSaga) +} diff --git a/admin/src/redux/reducer.js b/admin/src/redux/reducer.js index 60ea924..c28e1e8 100644 --- a/admin/src/redux/reducer.js +++ b/admin/src/redux/reducer.js @@ -2,9 +2,11 @@ import { combineReducers } from 'redux' import { reducer as form } from 'redux-form' import authReducer, { moduleName as authModule } from '../ducks/auth' import peopleReducer, { moduleName as peopleModule } from '../ducks/people' +import eventsReducer, { moduleName as eventsModule } from '../ducks/events' export default combineReducers({ form, [authModule]: authReducer, - [peopleModule]: peopleReducer + [peopleModule]: peopleReducer, + [eventsModule]: eventsReducer }) diff --git a/admin/src/redux/saga.js b/admin/src/redux/saga.js index ca88652..71a1ad8 100644 --- a/admin/src/redux/saga.js +++ b/admin/src/redux/saga.js @@ -1,7 +1,8 @@ import { all } from 'redux-saga/effects' import { saga as peopleSaga } from '../ducks/people' import { saga as authSaga } from '../ducks/auth' +import { saga as eventsSaga } from '../ducks/events' export default function*() { - yield all([authSaga(), peopleSaga()]) + yield all([authSaga(), peopleSaga(), eventsSaga()]) } diff --git a/admin/src/routes/events.js b/admin/src/routes/events.js new file mode 100644 index 0000000..ad831e7 --- /dev/null +++ b/admin/src/routes/events.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' + +import { loadEvents, loadingSelector, eventsSelector } from '../ducks/events' +import Loader from '../components/common/loader' +import EventsList from '../components/events/list' + +class EventsRoute extends Component { + componentDidMount() { + this.props.loadEvents() + } + + render() { + const { loading, events } = this.props + console.log('---', loading) + return ( +
+

Events Page

+ {loading && } + +
+ ) + } +} + +export default connect( + (state) => ({ + loading: loadingSelector(state), + events: eventsSelector(state) + }), + { loadEvents } +)(EventsRoute) From 2fb37b3541412a78b8c7fa37733a1cac949d04b3 Mon Sep 17 00:00:00 2001 From: vitaliy Date: Mon, 9 Jul 2018 00:57:30 +0300 Subject: [PATCH 2/2] ht2_1: write test for auth --- admin/src/ducks/auth.spec.js | 107 +++++++++++++++++++++++++++++++++++ admin/src/routes/events.js | 1 - 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 admin/src/ducks/auth.spec.js diff --git a/admin/src/ducks/auth.spec.js b/admin/src/ducks/auth.spec.js new file mode 100644 index 0000000..24d5c0b --- /dev/null +++ b/admin/src/ducks/auth.spec.js @@ -0,0 +1,107 @@ +import { call, take, put, apply } from 'redux-saga/effects' +import firebase from 'firebase/app' +import { + SIGN_IN_REQUEST, + SIGN_IN_ERROR, + SIGN_UP_SUCCESS, + SIGN_UP_ERROR, + SIGN_IN_MAX_TRIES_ERROR, + signInSaga, + signUpSaga +} from './auth' + +describe('Saga', () => { + describe('sign in', () => { + it('should success', () => { + const saga = signInSaga() + + expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST)) + + const payload = { email: 'test@test.com', password: 'test password' } + const auth = firebase.auth() + + expect(saga.next({ payload }).value).toEqual( + apply(auth, auth.signInWithEmailAndPassword, [ + payload.email, + payload.password + ]) + ) + }) + + const checkIncorrectSignIn = (saga) => { + expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST)) + + const payload = { email: 'test@test.com', password: 'test password' } + const auth = firebase.auth() + + expect(saga.next({ payload }).value).toEqual( + apply(auth, auth.signInWithEmailAndPassword, [ + payload.email, + payload.password + ]) + ) + const error = new Error('error') + expect(saga.throw(error).value).toEqual( + put({ type: SIGN_IN_ERROR, error }) + ) + } + + it('should error', () => { + const saga = signInSaga() + + for (let i = 0; i < 3; i++) { + checkIncorrectSignIn(saga) + } + + expect(saga.next().value).toEqual( + put({ + type: SIGN_IN_MAX_TRIES_ERROR + }) + ) + }) + }) + + describe('sign up', () => { + it('should success', () => { + const action = { + payload: { email: 'test@test.conm', password: 'pass12345678' } + } + const { payload } = action + const auth = firebase.auth() + + const saga = signUpSaga(action) + expect(saga.next().value).toEqual( + call( + [auth, auth.createUserWithEmailAndPassword], + payload.email, + payload.password + ) + ) + const user = { firstName: 'testName' } + expect(saga.next(user).value).toEqual( + put({ type: SIGN_UP_SUCCESS, payload: { user } }) + ) + }) + + it('should error', () => { + const action = { + payload: { email: 'test@test.conm', password: 'pass12345678' } + } + const { payload } = action + const auth = firebase.auth() + + const saga = signUpSaga(action) + expect(saga.next().value).toEqual( + call( + [auth, auth.createUserWithEmailAndPassword], + payload.email, + payload.password + ) + ) + const error = new Error('error msg') + expect(saga.throw(error).value).toEqual( + put({ type: SIGN_UP_ERROR, error }) + ) + }) + }) +}) diff --git a/admin/src/routes/events.js b/admin/src/routes/events.js index ad831e7..0b5d20b 100644 --- a/admin/src/routes/events.js +++ b/admin/src/routes/events.js @@ -12,7 +12,6 @@ class EventsRoute extends Component { render() { const { loading, events } = this.props - console.log('---', loading) return (

Events Page