<|start_filename|>app/templates/ui/js/components/common/index.js<|end_filename|>
import React from 'react';
import PropTypes from 'prop-types';

import * as commonStyles from 'style/commonStyles';

export function Title({children}) {
    return <h1 className={commonStyles.TitleStyle}>{children}</h1>
}

Title.propTypes = { children: PropTypes.any.isRequired };

export function Text({children}) {
    return <p className={commonStyles.TextStyle}>{children}</p>
}

Text.propTypes = {
    children: PropTypes.any.isRequired
};

export function Code({children}) {
    return (
        <pre className={commonStyles.CodeStyle}>
            <code>{children}</code>
        </pre>
    );
}
Code.propTypes = {
    children: PropTypes.any.isRequired
};



<|start_filename|>app/templates/ui/js/components/common/LoginBox.js<|end_filename|>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { css } from 'emotion';

import * as authActions from 'js/actions/authActions';
import { Text } from 'components/common';

class LoginBox extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'LoginBox';
        this.state = {
            username: '',
            password: ''
        };

        this.submitLogin = this.submitLogin.bind(this);
        this.onChange = this.onChange.bind(this);

    }

    submitLogin(e) {
        e.preventDefault();
        this.props.actions.login(this.state.username, this.state.password);

    }

    onChange(evt) {
        const { value, name } = evt.target;
        let state = {};
        state[name] = value;
        this.setState(state);
    }



    render() {
        const csrfClass = css`
            display: none;
        `

        const content = this.props.user ?
            (
                <div>
                    <Text>Welcome {this.props.user}</Text>
                    <button onClick={this.props.actions.logout}>Logout</button>
                </div>
            ) :
            (
                <form onSubmit={this.submitLogin}>
                    <input
                        className={csrfClass}
                        name="csrfToken"
                        defaultValue={this.props.csrfToken}
                    />
                    <Text>Username</Text>
                    <input
                        type="text"
                        name="username"
                        onChange={this.onChange}
                        value={this.state.username} />
                    <Text>Password</Text>
                    <input
                        type="password"
                        name="password"
                        onChange={this.onChange}
                        value={this.state.password} />
                    <br/>
                    <button type="submit">Login</button>
                    {this.props.errors.map( (error, idx) => {
                        return <Text key={idx}>{error}</Text> ;
                    })}
                </form>
            )
        return (
            <div>
                {content}
            </div>
        );
    }
}

LoginBox.propTypes = {
    endpoint: PropTypes.string.isRequired,
    csrfToken: PropTypes.string.isRequired,
    actions: PropTypes.object.isRequired,
    user: PropTypes.oneOfType([
        PropTypes.bool,
        PropTypes.string
    ]),
    errors: PropTypes.array
};

function mapStateToProps(state) {
    return {
        user: state.auth.user,
        errors: state.auth.errors,

    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(authActions, dispatch)
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(LoginBox);


<|start_filename|>app/templates/ui/entry.js<|end_filename|>
import 'babel-polyfill';

import React from 'react';
import ReactDOM from 'react-dom';

import { ConnectedRouter } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';

import { Provider } from 'react-redux';

import App from 'js/components/App';

import configureStore from 'js/store/configureStore';
import rootApiSaga from 'js/sagas/authSaga';
import heroDataSaga from 'js/sagas/heroDataSaga';

import 'normalize.css';
// import 'bootstrap/dist/css/bootstrap.min.css';

const { store, sagaMiddleware } = configureStore();

const history = createHistory();

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <App />
        </ConnectedRouter>
    </Provider>,
    document.getElementById('app')
);

sagaMiddleware.run(rootApiSaga);
sagaMiddleware.run(heroDataSaga);


<|start_filename|>app/templates/ui/js/actions/heroDataActions.js<|end_filename|>
import * as type from './actionTypes';

export function loadHeroData() {
    return { type: type.FETCH_ASYNC_HERODATA }
}


<|start_filename|>app/templates/ui/js/components/page/IndexPage.js<|end_filename|>
import React, { Component } from 'react';
// import PropTypes from 'prop-types';
import  { Link } from 'react-router-dom';

import { Text, Title } from 'js/components/common';


class IndexPage extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'IndexPage';

    }

    render() {
        return (
            <div>
                <Text>Welcome to your new boilerplate!</Text>
                <Title>Everything your front end needs to get started in one shot</Title>
                <div>
                    <Text>
                        At the high level, I include the following libraries:
                    </Text>
                    <ul>
                        <li>Emotion (CSS-in-JS) styling</li>
                        <li>React</li>
                        <li>React-Router</li>
                        <li>Redux and React-Redux</li>
                        <li>Redux-Saga</li>
                        <li>Express.js</li>
                    </ul>
                </div>
                <Title>Principles</Title>
                    <Text>
                        Setting up a single page React application sucks!
                        The React Ecosystem is just asking for newcomers to get #JavascriptFatigue.
                    </Text>
                    <ul>
                        <li>Avoid #JavascriptFatigue at all costs</li>
                        <li>Create a fun and easy system and architecture to develop on</li>
                        <li>Build modular and testable</li>
                        <li>Front-End first</li>
                        <li>Minimal Backend setup</li>
                        <li>Maximize performance</li>
                    </ul>
                <Text>Organized in a styled-components-ish manner</Text>
                <Text>Hot loading enabled for development, disabled for production (for obvious reasons)</Text>
                <Text>
                    The backend is set up in a highly minimalistic fashion. As many front end developers
                    enjoy working in the React ecosystem, the Single Page App architecture has been in a
                    loose sense, {`"standardized"`}. If I was starting a project from scratch, this is the exact
                     front end setup I would personally use.
                </Text>
                <Title>Where to start?</Title>
                <Text>
                    For starters, you can check out the <Link to='/docs'>Documentation</Link> to get you started :D
                </Text>
                <Title>Hey! I want to help out!</Title>
                <Text>Great! I love contributions! Make a fork, then make a pull request!</Text>
                <br/>

            </div>
        );
    }
}

export default IndexPage;


<|start_filename|>app/templates/ui/js/components/common/FetchDataDemo.js<|end_filename|>
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import { Title, Text } from 'components/common';

import * as heroDataActions from 'js/actions/heroDataActions';

class FetchDataDemo extends React.Component {

    render() {

        const {dataActions, dataValue, isLoggedIn} = this.props;

        /**
         * @method loadApiCall
         * @param {Object} props - dataValue will be passed as an argument.
         * @requires {dataValue} - Will be based on returned data upon API call.
         * @return {Object} - Will be returned as jsx elements representing the loaded elements upon fetch request
         * if the user has logged in successfully, otherwise the user won't be able to see the loaded data.
         */

        const loadApiCall = (props) => {
            if (isLoggedIn && dataValue) {
                return props.map((lists, idx) => {
                    return (
                        <ul key={idx}>
                            <li>{lists.firstName}</li>
                            <li>{lists.lastName}</li>
                            <li>{lists.alias}</li>
                        </ul>
                    );
                });
            } else {
                return (
                    <p>You must be logged in to see a working example of Redux-Saga</p>
                );
            }
        }

        return (
            <div>
                <Title>Load JSON File Using Redux-Saga</Title>
                <Text>An easy example to fetch an API call using Redux-Saga</Text>
                <button onClick={dataActions.loadHeroData}>Load Hero Data</button>
                {loadApiCall(dataValue)}
            </div>
        )
    }
}

function mapStateToProps(state) {
    return {
        dataValue: state.heroData.dataValue,
        isLoggedIn: state.auth.user
    }
}

function mapDispatchToProps(dispatch) {
    return {
        dataActions: bindActionCreators(heroDataActions, dispatch)
    }
}

FetchDataDemo.defaultProps = {
    dataValue: []
}

FetchDataDemo.propTypes = {
    dataActions: PropTypes.object.isRequired,
    dataValue: PropTypes.array.isRequired,
    isLoggedIn: PropTypes.oneOfType([
        PropTypes.bool,
        PropTypes.string
    ]).isRequired
};

export default connect(mapStateToProps, mapDispatchToProps)(FetchDataDemo);


<|start_filename|>app/templates/server.js<|end_filename|>
/* eslint-disable no-console */

// ======================
// Dotenv - Load .env file depending on development
// or production
//
const ENV = process.env.NODE_ENV || 'development';
require('dotenv').load({
    path: ENV === 'development' ?
        '.env.dev' :
        '.nev.prod'
});

// ======================
// Standard Node tools and modules
//
const path = require('path');


// ======================
// Express + middleware
//
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());

// Security Middleware
const helmet = require('helmet');
app.use(helmet());

// Logging
const logger = require('morgan');
app.use(logger('dev'));

// ======================
// Webpack and front end compilation tools
//

const pathIndex = path.resolve(__dirname, 'dist');
if(ENV === 'development') {
    // Have webpack compile everything
    // and enable hot module replacement
    const webpackDevMiddleware = require('webpack-dev-middleware');
    const webpack = require('webpack');
    const webpackConfig = require('./webpack.config');
    const compiler = webpack(webpackConfig);
    app.use(webpackDevMiddleware(compiler, {
        publicPath: '/',
        hot: true,
        contentBase: pathIndex,
    }));

    app.use(require('webpack-hot-middleware')(compiler));
} else {
    // Production only settings

    // Compress everything for super fast loading times!
    const compression = require('compression');
    app.use(compression());

    // enable static files to be served from 'dist'
    app.use(express.static('dist'));
}

// ======================
// Routing
//

const apiRouter = require('./api/apiRouter');

app.use('/api', apiRouter);
app.get('*', (req, res) => {
    res.sendFile(pathIndex+'/index.html');
});

// DEFAULT PORT is 3000 unless specified by dotenv
const PORT = Number(JSON.stringify(process.env.PORT)) ?
    Number(JSON.stringify(process.env.PORT)) : 3000;
app.listen(PORT, () => {
    console.log(`Server running in ${ENV.toUpperCase()} mode`);
    console.log(`listening on Port ${PORT}`);
});



<|start_filename|>app/templates/ui/js/components/common/Counter.js<|end_filename|>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { css } from 'emotion';

import * as counterActions from 'js/actions/counterActions';
import * as colors from 'style/colorPalette';

class Counter extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'Counter';
    }

    render() {
        const { value, actions }  = this.props;
        const counterClass = css`
            .counter {
                font-size: 26px;
                color: ${colors.primary};
            }
        `;

        // const delay = this.props.delay * 1000;
        return (
            <div className={counterClass}>
                <p className="counter">{value}</p>
                <button onClick={actions.increment}>
                    Increment
                </button>
                <button onClick={actions.decrement}>
                    Decrement
                </button>
                <button onClick={actions.reset}>
                    Reset
                </button>
            </div>
        )
    }
}

Counter.defaultProps = {
    delay: 1,
    value: 0
};

Counter.propTypes = {
    delay: PropTypes.number.isRequired,
    value: PropTypes.number.isRequired,
    actions: PropTypes.object,
    onDecrement: PropTypes.func,
    onIncrementAsync: PropTypes.func
};

function mapStateToProps(state) {
    return {
        value: state.counter.value
    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(counterActions, dispatch)
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Counter);


<|start_filename|>app/templates/ui/js/reducers/counterReducer.js<|end_filename|>
import * as types from '../actions/actionTypes';
import initialState from './initialState';

export default function counterReducer(state = initialState.counter , action) {

    switch(action.type) {
        case types.COUNTER_INCREMENT: {
            const newState = Object.assign({}, state);
            newState.value = newState.value + 1;
            return newState;
        }

        case types.COUNTER_DECREMENT: {
            const newState = Object.assign({}, state);
            newState.value = newState.value - 1;
            return newState;
        }

        case types.COUNTER_RESET: {
            const newState = Object.assign({}, state);
            newState.value = 0;
            return newState;
        }

        default: {
            return state;
        }
    }
}


<|start_filename|>app/templates/ui/js/actions/heroDataActions.test.js<|end_filename|>
import * as types from './actionTypes';
import * as heroDataActions from './heroDataActions';

describe('fetch data actions', () => {
    it('Sends dataOnload action', () => {
        const expected = { type: types.FETCH_ASYNC_HERODATA }
        const actual = heroDataActions.loadHeroData();

        expect(actual).toEqual(expected);
    });
});


<|start_filename|>app/templates/ui/js/components/App.js<|end_filename|>
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
// import { css } from 'emotion';

import Header from 'components/common/Header';
import Footer from 'components/common/Footer';
import IndexPage from 'components/page/IndexPage';
import DocsPage from 'components/page/DocsPage';
import ReduxPage from 'components/page/ReduxPage';

import { globalStyle } from 'style/commonStyles';
class App extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'App';
    }

    render() {
        return (
            <div className={globalStyle}>
                <Header />
                <Switch>
                    <Route exact path="/" component={IndexPage} />
                    <Route path="/docs" component={DocsPage} />
                    <Route path="/redux" component={ReduxPage} />
                </Switch>
                <Footer />
            </div>
        );
    }
}

export default App;


<|start_filename|>app/templates/ui/js/store/configureStore.js<|end_filename|>
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import createSagaMiddleware from 'redux-saga';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore(initialState) {

    let sagaMiddleware = createSagaMiddleware();

    let store = createStore(
        rootReducer,
        initialState,
        composeEnhancers(
            applyMiddleware(
                sagaMiddleware
            )
        )
    );

    return {
        store,
        sagaMiddleware
    };
}


<|start_filename|>app/templates/ui/js/actions/authActions.js<|end_filename|>
import * as types from './actionTypes';

export function login(username, password) {
    return {
        type: types.AUTH_LOGIN,
        username,
        password
    };

}

export function logout() {
    return {
        type: types.AUTH_LOGOUT
    };
}


<|start_filename|>app/templates/ui/js/sagas/heroDataSaga.js<|end_filename|>
import axios from 'axios';
import { takeEvery, call, put } from 'redux-saga/effects';

import * as types from 'js/actions/actionTypes';

export function* fetchAsyncHeroData() {
    try {
        const fetchHeroData = yield call(axios.get, '/api/heroData.json');
        yield put({ type: types.FETCH_ASYNC_HERODATA_SUCCESS, data: fetchHeroData.data });
    } catch (e) {
        const {message} = e;
        yield put({ type: types.FETCH_ASYNC_HERODATA_FAILURE, errorMessages: message });
    }
}

export default function* watchForAsyncHeroData() {
    yield takeEvery(types.FETCH_ASYNC_HERODATA, fetchAsyncHeroData);
}


<|start_filename|>app/templates/ui/style/typography.js<|end_filename|>
export const primary = 'Abel';
export const secondary = 'Ubuntu';
export const code = 'monospace';


<|start_filename|>app/templates/ui/style/colorPalette.js<|end_filename|>
export const primary = 'dodgerblue';
export const primary_dark = '#006ad1';
export const secondary = '#1eff8d';


<|start_filename|>app/templates/ui/js/reducers/heroDataReducer.js<|end_filename|>
import * as type from '../actions/actionTypes';
import initialState from './initialState';

export default function fetchData(state = initialState.heroData, action) {

    switch(action.type) {

        case type.FETCH_ASYNC_HERODATA_SUCCESS: {
            return Object.assign({}, state, {dataValue: action.data});
        }

        case type.FETCH_ASYNC_HERODATA_FAILURE: {
            return Object.assign({}, state, {
                errors: state.errors.concat(action.errorMessages)
            });
        }

        default: {
            return state;
        }
    }
}


<|start_filename|>app/templates/ui/js/sagas/authSaga.test.js<|end_filename|>
/* global
    describe
    it
    expect
 */

import axios from 'axios';
import { put, call, takeEvery, all } from 'redux-saga/effects';

import * as types from 'js/actions/actionTypes';
import rootApiSaga, { loginSaga, logoutSaga, watchForLogin, watchForLogout } from 'js/sagas/authSaga';


describe('User Auth Sagas', () => {
    describe('Login (Successful)', () => {

        const auth = JSON.stringify({ username: 'admin', password: 'password'});
        let gen = loginSaga(auth);
        it('Handles a Successful Login', () => {
            expect(gen.next().value).toEqual(call(axios.post, '/api/login', auth));
        });

        it('Dispatches an AUTH_LOGIN_SUCCESS action after getting a response', () => {
            expect(gen.next().value).toEqual(put( {type: types.AUTH_LOGIN_SUCCESS }));
        });
    });

    describe('Login (Unauthorized)', () => {

        const auth = JSON.stringify({ username: 'bad', password: '<PASSWORD>'});
        let gen = loginSaga(auth);
        it('Dispatches an AUTH_LOGIN_UNAUTHORIZED on a failed login', () => {
            const mockError = {
                response: { status: 401}
            }
            expect(gen.next().value).toEqual(call(axios.post, '/api/login', auth));
            expect(gen.throw(mockError).value).toEqual(put({ type: types.AUTH_LOGIN_UNAUTHORIZED}));
        });
    });

     describe('Logout', () => {

        let saga = logoutSaga();
        it('Dispatches AUTH_LOGOUT_SUCCESS when a user logs out', () => {
            expect(saga.next().value).toEqual(call(axios.get, '/api/logout'));
            expect(saga.next().value).toEqual(put({ type: types.AUTH_LOGOUT_SUCCESS}));
        });

        it('Dispatches NETWORK_FAILURE action if it cannot connect', () => {
            expect(saga.throw({}).value).toEqual(put( {type: types.NETWORK_FAILURE}));
        });
    });

    describe('Watch Sagas', () => {
        it('Watches login saga', () => {
            let saga = watchForLogin();
            expect(saga.next().value).toEqual(takeEvery(types.AUTH_LOGIN, loginSaga));
        });
        it('Watches logout saga', () => {
            let saga = watchForLogout();
            expect(saga.next().value).toEqual(takeEvery(types.AUTH_LOGOUT, logoutSaga));
        });
    });

    describe('Root Auth API Saga', () => {
        it('Yields all Auth API sagas in a rootSaga', () => {

            let rootSaga = rootApiSaga();
            expect(rootSaga.next().value).toEqual(
                all([watchForLogin(), watchForLogout()]));
        });
    });

});


<|start_filename|>app/templates/ui/js/components/common/Header.js<|end_filename|>
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { css } from 'emotion';

import { Title } from 'components/common';


// import PropTypes from 'prop-types';

class Header extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'Header';

    }

    render() {
        const style = css`
            width: 100%;
            display: block;
            margin-bottom: 15px;

            h1, a {
                text-align: center;
            }
            a {
                display: inline-block;
                margin: 0 8px;
            }
        `;

        return (
            <div className={style}>
                <Title css={`text-align:center;`}>Not Another React Boilerplate...</Title>
                <Link to="/">Homepage</Link>
                <Link to="/docs">Documentation</Link>
                <Link to="/redux">Redux Demo</Link>
            </div>
        );
    }
}

export default Header;


<|start_filename|>app/templates/ui/js/reducers/authReducer.test.js<|end_filename|>
/* global
    describe
    it
    expect
 */

import * as types from 'js/actions/actionTypes';
import authReducer from 'js/reducers/authReducer';

describe('Auth Reducer Tests', () => {
    it('Returns a successful login', () => {
        const state = { errors: [], user: '' };
        const expected = { errors: [], user: 'admin'};
        const actual = authReducer(state, { type: types.AUTH_LOGIN_SUCCESS});
        expect(actual).toEqual(expected);
    });

    it('Returns an unsuccessful login', () => {
        const state = { errors: [], user: '' };
        const expected = {
            errors: ['Unauthorized. Please try again'],
            user: false
        };
        const actual = authReducer(state, { type: types.AUTH_LOGIN_UNAUTHORIZED});
        expect(actual).toEqual(expected);
    });

    it('Successfully logs a user out', () => {
        const state = { errors: ['Oh snap'], user: 'dingo williams' };
        const expected = { errors: [], user: false};
        const actual = authReducer(state, { type: types.AUTH_LOGOUT_SUCCESS});
        expect(actual).toEqual(expected);
    });

    it('Returns state if no-op is passed into reducer', () => {
        const expected = { errors: [], user: false};
        const actual = authReducer(undefined, { type: 'non-existant'});
        expect(actual).toEqual(expected);
    });
});


<|start_filename|>app/templates/ui/js/sagas/authSaga.js<|end_filename|>
import axios from 'axios';
import { takeEvery, call, all, put } from 'redux-saga/effects';

import * as types from 'js/actions/actionTypes';

export function* loginSaga(auth) {

    try {
        yield call(axios.post, '/api/login', auth);
        yield put({ type: types.AUTH_LOGIN_SUCCESS });
    } catch(e) {
        const {status} = e.response;
        if(status === 401) {
            // Unauthorized!
            yield put({type: types.AUTH_LOGIN_UNAUTHORIZED })
        }

    }

}

export function* logoutSaga() {
    try {
        yield call(axios.get, '/api/logout');
        yield put({ type: types.AUTH_LOGOUT_SUCCESS });
    } catch(e) {
        yield put({type: types.NETWORK_FAILURE });
    }
}

export function* watchForLogin() {
    yield takeEvery(types.AUTH_LOGIN, loginSaga);
}

export function* watchForLogout() {
    yield takeEvery(types.AUTH_LOGOUT, logoutSaga);
}

export default function* rootApiSaga() {
    yield all([
        watchForLogin(),
        watchForLogout()
    ]);
}


<|start_filename|>package.json<|end_filename|>
{
  "name": "generator-not-another-react-boilerplate",
  "version": "0.1.8",
  "description": "Developer satisfaction centric complete single page react application with HMR, React-Router, Redux, Redux-Saga, Jest, and minimal backend with Express. ",
  "main": "index.js",
  "author": "<NAME>",
  "license": "MIT",
  "repository": "https://github.com/MrBenJ/generator-not-another-react-boilerplate",
  "keywords": [
    "yeoman-generator",
    "react",
    "redux",
    "redux-saga",
    "jest",
    "react-boilerplate"
  ],
  "files": [
    "app"
  ],
  "dependencies": {
    "chalk": "^2.0.1",
    "yeoman-generator": "^1.1.1",
    "yosay": "^2.0.0"
  },
  "devDependencies": {}
}


<|start_filename|>app/templates/ui/js/reducers/heroDataReducer.test.js<|end_filename|>
import * as type from '../actions/actionTypes';
import heroDataReducer from './heroDataReducer';

describe('heroDataReducer tests', () => {
    const data = [
        {
            "firstName": "Clark",
            "lastName": "Kent",
            "alias": "Superman"
        }, 
        {
            "firstName": "John",
            "lastName": "Stewart",
            "alias": "<NAME>"
        }, 
        {
            "firstName": "Bruce",
            "lastName": "Wayne",
            "alias": "Batman"
        }, 
        {
            "firstName": "Matthew",
            "lastName": "Murdock",
            "alias": "Daredevil"
        }
    ];

    it('should return data after a successful fetch request', () => {
        const state = { dataValue: [], errors: [] };
        const expected = { dataValue: data, errors: [] };

        const actual = heroDataReducer(state, { type: type.FETCH_ASYNC_HERODATA_SUCCESS, data });
        expect(actual).toEqual(expected);
    });

    it('should not return data when the fetch request has failed', () => {
        const errors = 'Request failed with status code 404';
        const state = {
            dataValue: [],
            errors: []
        };
        const expected = { dataValue: [], errors: [errors] };

        const actual = heroDataReducer(state, { type: type.FETCH_ASYNC_HERODATA_FAILURE, errorMessages: errors });
        expect(actual).toEqual(expected);
    });

    it('should handle the default case', () => {
        const expected = { dataValue: [], errors: [] };

        const actual = heroDataReducer(undefined, {});
        expect(actual).toEqual(expected);
    });
});


<|start_filename|>app/templates/ui/js/actions/actionTypes.js<|end_filename|>
export const COUNTER_INCREMENT = 'COUNTER_INCREMENT';
export const COUNTER_DECREMENT = 'COUNTER_DECREMENT';
export const COUNTER_RESET = 'COUNTER_RESET';

export const AUTH_LOGIN = 'AUTH_LOGIN';
export const AUTH_LOGIN_SUCCESS = 'AUTH_LOGIN_SUCCESS';
export const AUTH_LOGIN_UNAUTHORIZED = 'AUTH_LOGIN_UNAUTHORIZED';
export const AUTH_LOGOUT = 'AUTH_LOGOUT';
export const AUTH_LOGOUT_SUCCESS = 'AUTH_LOGOUT_SUCCESS'

export const NETWORK_FAILURE = 'NETWORK_FAILURE';

export const FETCH_ASYNC_HERODATA = 'FETCH_ASYNC_HERODATA';
export const FETCH_ASYNC_HERODATA_SUCCESS = 'FETCH_ASYNC_HERODATA_SUCCESS';
export const FETCH_ASYNC_HERODATA_FAILURE = 'FETCH_ASYNC_HERODATA_FAILURE';

<|start_filename|>app/templates/ui/js/reducers/index.js<|end_filename|>
import { combineReducers } from 'redux';
import counter from './counterReducer';
import auth from './authReducer';
import heroData from './heroDataReducer';

const rootReducer = combineReducers({
    counter,
    auth,
    heroData
});

export default rootReducer;


<|start_filename|>app/templates/ui/js/actions/counterActions.test.js<|end_filename|>
/* global
    expect
    it
    describe
*/

import * as types from './actionTypes';
import * as counterActions from './counterActions';

describe('Counter Actions', () => {
    it('Sends Increment Action', () => {
        const expected = { type: types.COUNTER_INCREMENT };
        const actual = counterActions.increment();

        expect(actual).toEqual(expected);
    });

    it('Sends Decrements Action', () => {
        const expected = { type: types.COUNTER_DECREMENT };
        const actual = counterActions.decrement();

        expect(actual).toEqual(expected);
    });

    it('Sends Reset Action', () => {
        const expected = { type: types.COUNTER_RESET };
        const actual = counterActions.reset();

        expect(actual).toEqual(expected);
    });
});


<|start_filename|>app/templates/ui/js/components/common/index.test.js<|end_filename|>
/* global
    describe
    expect
    it
 */

import {shallow} from 'enzyme';
import React from 'react';

import { Text, Title, Code } from './';

describe('Common Component Tests', () => {
    describe('<Text>', () => {

        const setupText = () => shallow(<Text>Hello</Text>);


        it('Renders with children', () => {
            const TextWrapper = setupText();
            expect(TextWrapper.props().children).toBe('Hello');
        });

        it('Renders a <p> tag', () => {
            const TextWrapper = setupText();
            expect(TextWrapper.find('p').length).toBe(1);
        })

        it('Has styles', () => {
            const TextWrapper = setupText();
            expect(TextWrapper.props().className).toBeTruthy();
        });

    });

    describe('<Title>', () => {
        const setupTitle = () => shallow(<Title>Title Here</Title>);

        it('Renders with children', () => {
            const TitleWrapper = setupTitle();
            expect(TitleWrapper.props().children).toBe('Title Here');
        });

        it('Renders an <h1> tag', () => {
            const TitleWrapper = setupTitle();
            expect(TitleWrapper.find('h1').length).toBe(1);
        });

        it('Has styles', () => {
            const TitleWrapper = setupTitle();
            expect(TitleWrapper.props().className).toBeTruthy();
        });
    });

    describe('<Code>', () => {
        const setupCode = () => shallow(<Code>pretty code</Code>);

        it('Renders with children', () => {
            const CodeWrapper = setupCode();
            expect(CodeWrapper.find('code').props().children).toBe('pretty code');
        });

        it('Renders a <pre> and <code> tag', () => {
            const CodeWrapper = setupCode();
            expect(CodeWrapper.find('code').length).toBe(1);
            expect(CodeWrapper.find('pre').length).toBe(1);
        });

        it('Has styles', () => {
            const CodeWrapper = setupCode();
            expect(CodeWrapper.props().className).toBeTruthy();
        });
    })
});


<|start_filename|>app/templates/ui/js/actions/counterActions.js<|end_filename|>
import * as type from './actionTypes';

export function increment() {
    return { type: type.COUNTER_INCREMENT };
}

export function decrement() {
    return { type: type.COUNTER_DECREMENT };
}

export function reset() {
    return { type: type.COUNTER_RESET };
}


<|start_filename|>app/templates/ui/js/reducers/counterReducer.test.js<|end_filename|>
/* global
    describe
    it
    expect
*/

import counterReducer from './counterReducer';
import * as types from 'js/actions/actionTypes';

describe('Counter Reducer Tests', () => {

    it('Increments', () => {
        let state = { value: 0 };
        const expected = { value: 1 };

        const actual = counterReducer(
            state,
            { type: types.COUNTER_INCREMENT }
        );
        expect(actual).toEqual(expected);
    });

    it('Decrements', () => {
        let state = { value: 50 };
        const expected = { value: 49 };

        const actual = counterReducer(
            state,
            { type: types.COUNTER_DECREMENT }
        );
        expect(actual).toEqual(expected);
    });

    it('Resets', () => {
        let state = { value: 234 };
        const expected = { value: 0 };

        const actual = counterReducer(
            state,
            { type: types.COUNTER_RESET }
        );
        expect(actual).toEqual(expected);
    });

    it('Handles the default case', () => {
        let state = { value: 0 };
        const expected = { value: 0 };

        const actual = counterReducer(
            state,
            { type: 'NO_OP' }
        );
        expect(actual).toEqual(expected);
    });

    it('Gets initial state if no state is passed in', () => {

        const expected = { value: 1 };

        const actual = counterReducer(
            undefined,
            { type: types.COUNTER_INCREMENT }
        );
        expect(actual).toEqual(expected);
    });
});


<|start_filename|>app/templates/ui/js/reducers/authReducer.js<|end_filename|>
import * as types from '../actions/actionTypes';
import initialState from './initialState';

export default function authReducer(state = initialState.auth, action) {

    switch(action.type) {

        case types.AUTH_LOGIN_SUCCESS: {
            let newState = Object.assign({}, state);
            newState.user = 'admin';
            newState.errors = [];
            return newState;
        }

        case types.AUTH_LOGIN_UNAUTHORIZED: {
            let newState = Object.assign({}, state);
            newState.user = false;
            newState.errors = [];
            newState.errors.push('Unauthorized. Please try again');
            return newState;
        }

        case types.AUTH_LOGOUT_SUCCESS: {
            let newState = Object.assign({}, state);
            newState.user = false;
            newState.errors = [];
            return newState;
        }

        default: {
            return state;
        }
    }
}


<|start_filename|>app/index.js<|end_filename|>
let Generator = require('yeoman-generator');
let yosay = require('yosay');
let chalk = require('chalk');

module.exports = class extends Generator {

    prompting() {
        this.log(yosay(
            `Hey! It's yet another ${chalk.red('React Application Generator')}!`
        ));

        this.log("Howdy there! Let's build you a 🙌  React Single Page Application  🙌");
        this.log("‼️ Please make sure you're in the root directory of your project when running this generator ‼️");
        this.log('‼️ It is highly recommended you have "yarn" installed on your machine before starting ‼️');
        this.log('Visit this page for instructions on how to install yarn: https://yarnpkg.com/en/docs/install');
        this.prompt([
            {
                type: 'input',
                name: 'name',
                message: 'Enter the name of your project: '
            }
        ]).then( ({name}) => {
            name = name.trim();
            if(!name) {
                this.log('😱  Invalid name entered. Please restart the generator  😡');
                return;
            }

            this.log('💥 BAM! Let\'s do this! 👌');
            this.log(`Installing ${name} in directory: ${this.destinationRoot()}`);
            this.fs.copyTpl(
                this.templatePath('.'),
                this.destinationPath('.'),
                { name }
            );

            this._dotfiles(name);
            this.yarnInstall(undefined, undefined, () => {
                this.log('All done! Run "yarn start" and head to localhost:3000 to see documentation!');
                this.log('If you liked this generator, Give it a Star :D - @MrBenJ would love you forever ;)');
            });
        });

    }
    _dotfiles(name) {
            console.log('Copying dotfiles...');
            this.fs.copyTpl(
                this.templatePath('.babelrc'),
                this.destinationPath('.babelrc'),
                { name }
            );
            this.fs.copyTpl(
                this.templatePath('.env.dev'),
                this.destinationPath('.env.dev'),
                { name }
            )
            this.fs.copyTpl(
                this.templatePath('.env.prod'),
                this.destinationPath('.env.prod'),
                { name }
            )
            this.fs.copyTpl(
                this.templatePath('.eslintrc.json'),
                this.destinationPath('.eslintrc.json'),
                { name }
            );
            this.fs.copy(
                this.templatePath('gitignore'),
                this.destinationPath('.gitignore'),
                { name }
            );
        }

}


<|start_filename|>app/templates/ui/js/sagas/heroDataSaga.test.js<|end_filename|>
import axios from 'axios';
import {call, put, takeEvery} from 'redux-saga/effects';
import watchForAsyncHeroData, {fetchAsyncHeroData} from './heroDataSaga';

import * as types from '../actions/actionTypes';

describe('fetching Data from the server', () => {

    describe('Fetch Data request successful', () => {

        let gen = fetchAsyncHeroData();

        it('should successfully find the requested data from the server', () => {
            expect(gen.next().value).toEqual(call(axios.get, '/api/heroData.json'));
        });

        it('should dispatch a FETCH_ASYNC_HERODATA_SUCCESS action after the request has been found',
        () => {
            const mockData = {};
            expect(gen.next(mockData).value).toEqual(put({type: types.FETCH_ASYNC_HERODATA_SUCCESS}));
        });

        it('should dispatch a FETCH_ASYNC_HERODATA_FAILURE action after a failed request', () => {
            const errorMessages = {
                message: 'Request failed with status code 404'
            };
            expect(gen.throw(errorMessages).value).toEqual(put({type: types.FETCH_ASYNC_HERODATA_FAILURE, errorMessages: errorMessages.message}));
        });
    });

    describe('Watch Sagas', () => {

        const gen = fetchAsyncHeroData;

        it('watches for the FETCH_ASYNC_HERODATA saga', () => {
            let saga = watchForAsyncHeroData();
            expect(saga.next().value).toEqual(takeEvery(types.FETCH_ASYNC_HERODATA, gen));
        });
    });
});


<|start_filename|>app/templates/ui/js/components/common/Footer.js<|end_filename|>
import React, { Component } from 'react';
// import PropTypes from 'prop-types';
import { css } from 'emotion';

import { Text } from 'components/common';

class Footer extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'Footer';

    }

    render() {
        const footerClass = css`
            margin-top: 20px;
        `;
        return (
            <footer className={footerClass}>
                <Text>
                    Created by <a target="_blank" rel="noreferrer noopener" href="https://www.github.com/mrbenj">@MrBenJ</a>
                </Text>
            </footer>
        );
    }
}
export default Footer;


<|start_filename|>app/templates/ui/js/components/page/ReduxPage.js<|end_filename|>
import React, { Component } from 'react';
import { Title, Text } from 'components/common';
import Counter from 'components/common/Counter';
import LoginBox from 'components/common/LoginBox';
import FetchDataDemo from 'components/common/FetchDataDemo';

class ReduxPage extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'ReduxPage';

    }

    render() {

        return (
            <div>
                <Title>Demonstration of Redux powered components</Title>
                <Text>
                    A short sample of some (extremely) basic Redux components. Unit tests for all components are in the same folder as the component<br/>
                    Note that there are also numerous tests for reducers, sagas, and more!
                </Text>
                <Title>Counter</Title>
                <Text>Obligatory (kinda stupid) Counter increment/decrement component</Text>
                <Counter delay={1} />
                <Title>Login Box</Title>
                <Text>
                    A username and password combo that works here is<br/>
                    admin/password <br/>
                    <br/>
                    Try entering anything else and see what happens
                </Text>
                <LoginBox
                    csrfToken="<PASSWORD>"
                    endpoint="/api/auth"
                />
                <FetchDataDemo />
            </div>
        )
    }
}
export default ReduxPage;


<|start_filename|>app/templates/ui/js/reducers/initialState.js<|end_filename|>
export default {
    counter: {
        value: 0
    },
    auth: {
        user: false,
        errors: []
    },
    heroData: {
        dataValue: [],
        errors: []
    }
};


<|start_filename|>app/templates/api/apiRouter.js<|end_filename|>
const router = require('express').Router();
const path = require('path');


router.post('/login', (req, res) => {

    const { username, password } = req.body;

    res.setHeader('Content-Type', 'application/json');
    if(username === 'admin' && password === 'password') {
        res.status(200).send(JSON.stringify({status: 'ok'}));
    } else {
        res.status(401).send(JSON.stringify({status: 'incorrect username or password'}));
    }


});

router.get('/logout', (req,res) => {
    res.setHeader('Content-Type', 'application/json');

    res.send(JSON.stringify({status: 'ok'}));
});

//Get the json file upon GET REQUEST.
router.get(/^\/.*\.json$/, (req, res) => {
    const jsonFile = req.url.slice(req.url.lastIndexOf('/') + 1);
    res.sendFile(path.join(__dirname, jsonFile));
});

module.exports = router;


<|start_filename|>app/templates/ui/style/commonStyles.js<|end_filename|>
import { css } from 'emotion';
import * as colors from 'style/colorPalette';
import * as fonts from 'style/typography';

export const globalStyle = css`
    font-family: ${fonts.secondary};
    font-size: 14px;
    color: black;

    a, a:visited {
        font-size: 16px;
        color: ${colors.primary_dark};
        text-decoration: none;
    }

    a:hover {
        color: ${colors.primary};
    }
`

export const TitleStyle = css`
    font-family: ${fonts.primary};
    font-size: 24px;
    color: ${colors.primary};
`;

export const TextStyle = css`
    font-family: ${fonts.secondary};
    font-size: 14px;
    color: black;
`;

export const CodeStyle = css`
    background-color: #ffffe0;
    font-family: ${fonts.code};
    font-size: 14px;

`;


<|start_filename|>app/templates/ui/js/components/common/Header.test.js<|end_filename|>
/* global
    expect
    it
    describe
*/

import React from 'react';
import { shallow } from 'enzyme';
import Header from 'components/common/Header';

describe('<Header> Component Tests', () => {
    const setupHeader = () => shallow(<Header />);

    it('Renders without exploding', () => {
        const HeaderWrapper = setupHeader();
        expect(HeaderWrapper).toBeTruthy();
    });
});


<|start_filename|>app/templates/ui/js/components/page/DocsPage.js<|end_filename|>
import React, { Component } from 'react';

import { Title, Text, Code } from 'components/common';

class DocsPage extends Component {
    constructor(props) {
        super(props);
        this.displayName = 'DocsPage';
    }

    render() {

        return (
            <div>
                <Title>Where to start?</Title>
                <Text>
                    Take a moment to look around and see the folder structure.<br/>
                    Don&#39;t jump into code just yet... Let&#39;s get a higher level
                    overview of what&#39;s going on with the folder structure
                </Text>
                <Code>
                    /dist                       -- Served folder for all (public) assets<br/>
                    -- index.html               -- Obligatory HTML file loading SPA bundle<br/>
                    /ui                         -- contains everything front-end related<br/>
                    -- /js                      -- folder containing components, reducers, stores, etc<br/>
                    -- -- /actions              -- Redux Actions<br/>
                    -- -- /components           -- all React Components<br/>
                    -- -- -- /common            -- Commonly used presentational components <br/>
                    -- -- -- /page              -- Page level components<br/>
                    -- -- /reducers             -- Redux Reducers <br/>
                    -- -- /sagas                -- Async Sagas used with redux-saga<br/>
                    -- -- /store                -- Redux Store <br/>
                    -- /style                   -- Folder where all the styles live <br/>
                    -- -- colorPallete.js       -- Exported colors<br/>
                    -- -- typography.js         -- Fonts<br/>
                    -- -- commonStyles.js       -- Styles for all the js/components/common components<br/>
                    -- entry.js                 -- SPA entry point<br/>
                </Code>
                <Title>npm Scripts</Title>
                <Text>You can run any of these commands on your CLI to do the following</Text>
                <Code>
                    yarn start                  -- Start the webserver in development mode (Yay Hotloading!)<br/>
                    yarn start:prod             -- Start the webserver in production mode. Use &quot;yarn build&quot; beforehand to build minified production assets first!<br/>
                    yarn deploy                 -- Deploys your application in production mode (NODE_ENV=production)<br/>
                    yarn test                   -- Runs the testing suite<br/>
                    yarn test:watch             -- Runs the testing suite in watch-mode. Runs tests every time you save a js file<br/>
                    yarn build                  -- Builds the production assets - run &quot;yarn start:prod&quot; afterwards to run the production server<br/>
                </Code>

                <Title>Import Path Aliases</Title>
                <Text>
                    You can use the following keywords to quickly navigate the filesystem:
                </Text>
                <Code>
                    js          -- {`<rootPath>/js`}<br/>
                    components  -- {`<rootPath>/js/components`}<br/>
                    page        -- {`<rootPath>/js/components/page`}<br/>
                    style       -- {`<rootPath>/style`} <br/>

                </Code>
                <Text>Instead of typing: </Text>
                <Code>import MyNewPageComponent from {`'../../js/components/page/MyNewPageComponent'`}</Code>
                <br/>
                <Text>You can do:</Text>
                <Code>import MyNewPageComponent from {`components/page/MyNewPageComponent`}</Code>
                <br/>
                <Text>If you look at the <i>components/page/DocsPage.js</i> file, you can see a little of this in action</Text>
                <Text>On Line 3 is </Text>
                <Code>import {`{ Title, Text, Code } from 'components/common'`}</Code>
                <Text>You may note that there is no <i>common.js</i> file. This is because if a folder is provided, the <i>index.js</i> in the folder is used by default</Text>
                <Text>You can also see how nice and clean everything is by using the path alias :D</Text>
                <Text>If you want to add your own, go look at <i> webpack.config.js</i> and edit the <i>alias</i> config object if you want to reorganize!</Text>

                <Title>Library Resources (Help! I am a newbie and I do not know what all/half/some/any of this stuff is!)</Title>
                <Text>Fear not... I am here to help!</Text>
                <ul>
                    <li>
                        If you are completely new to React and Redux, <strong>STOP!</strong>
                        <ul>
                            <li>
                                Go learn <a href="https://facebook.github.io/react/">React</a> by itself.
                            </li>
                            <li>
                                Do not use this boilerplate! Use <a href="https://github.com/facebookincubator/create-react-app">create-react-app</a> instead!
                            </li>
                        </ul>
                    </li>
                </ul>
                <Text>
                    Reasoning: This boilerplate is meant for large, scalable applications utilizing the most advanced and battle-tested modules in the ReactJS ecosystem.
                    It is NOT recommended to be used as a starter kit for folks just learning how to use React and Redux
                </Text>
                <ul>
                    <li>
                        <a href="https://facebook.github.io/react/">ReactJS Documentation</a>
                        <ul>
                            <li>ReactJS by itself is a view library, where components can hold {`"state"`}</li>
                        </ul>
                    </li>
                    <li>
                        <a href="http://redux.js.org/">Redux Documentation</a>
                        <ul>
                            <li>Redux forces your application to have a unidirectional data flow</li>
                            <li>Included in this boilerplate are the following libraries for Redux:
                                <ul>
                                    <li>
                                        <a href="https://github.com/reactjs/react-redux">React-Redux</a>
                                        <ul>
                                            <li>
                                                Official React Bindings that allows Redux to be used with React
                                            </li>
                                        </ul>
                                    </li>
                                    <li>
                                        <a href="">Redux-Saga</a>
                                        <ul>
                                            <li>
                                                Prevents async side effects using ES6 Generators
                                            </li>
                                        </ul>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="https://github.com/ReactTraining/react-router">React-Router</a>
                        <ul>
                            <li>Client side routing using the HTML5 History API</li>
                            <li>
                                Included in this boilerplate are the following libraries for React-Router
                                <ul>
                                    <li>
                                        <a href="https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux">React-Redux-Router</a>
                                    </li>
                                    <li>
                                        Fast and easy bindings to keep Redux and React-Router in sync with each other
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="https://expressjs.com/">Express</a>
                        <ul>
                            <li>
                                Minimalist and unopinionated Server for Node.js
                            </li>
                        </ul>
                    </li>

                </ul>
            </div>
        );
    }
}

export default DocsPage;


<|start_filename|>app/templates/ui/js/actions/authActions.test.js<|end_filename|>
/* global
    describe
    expect
    it

 */

import * as authActions from './authActions';
import * as types from './actionTypes';

describe('User Auth Actions', () => {
    describe('Login', () => {
        it('returns AUTH_LOGIN action with username and password', () => {
            const expected = {
                type: types.AUTH_LOGIN,
                username: 'dr_username',
                password: '<PASSWORD>'
            };

            const actual = authActions.login('dr_username', '<PASSWORD>');

            expect(actual).toEqual(expected);
        });
    });

    describe('Logout', () => {
        it('returns AUTH_LOGOUT when run', () => {
            const expected = {
                type: types.AUTH_LOGOUT
            };

            const actual = authActions.logout();

            expect(actual).toEqual(expected);
        });
    })
});
