Skip to content
This repository has been archived by the owner on Jul 17, 2020. It is now read-only.

Developer's Guide

kenjiO edited this page Aug 25, 2017 · 10 revisions

Introduction

The project is based on React, Redux and Bootstrap/AdminLTE for the client and Express and Mongoose for the server.

This project is built using the ES6 javascript standard which is a little different than the ES5 standard used in the freeCodeCamp curriculum. If you are not familiar with ES6, check out the ES6 Intro on the beta freeCodeCamp site in the Javascript Algorithms and Data Structures section.

This project uses gulp, webpack, babel as build tools. The root folder contains mostly:

  • app.json heroku configuration
  • assets/ is copied to the public client folder during build. Contains images and robots.txt
  • .babelrc server babel configuration
  • client/ the client js
  • common/ js required by the client and server
  • dist/ the build target
  • gulpfile.js gulpfile for building client, common and server and running webpack-dev-server and nodemon for development
  • server/ the server js
  • webpack.config.js common webpack configuration, merged with webpack-merge into the env specific configurations

Common

Code that's required by client and server.

  • constants.js contains various types, roles and identifiers.
  • placeholders.js define the placeholders (like template strings) used in the Quill editor on the client and processing on the server.
  • schemas.js are normalizr schemas describing the shape of the api data received by the client. They're here so the server can also generate redux actions and send them with socket.io.
  • validators.js validate input fields and return any errors.

Server

An express/socket.io server set up with babel for es6 and async/await.

  • index.js in development, runs the server through babel-register
  • server.js sets up mongoose, mongoose plugins, express, passport then starts an http server and binds the express app and socket.io server.
  • config/ environment specific configuration, the express server, an emailer and passport config
  • lib/ various middleware, helper functions, mail generation, error definitions and seed data
  • models/ mongoose models and schemas
  • routes/ Api routes bundled into routes/api.js then loaded by express under the api/ prefix Routes with admin/ prefix require the requesting user to have the admin role. Uses express-promise-router to pass uncaught errors in route handlers to the default error handler. You should almost never need to handle errors.
  • controllers/ route handlers

Client

A react/redux app built with es6 and react babel presets

  • index.html the index page used by html-webpack-plugin
  • app.js the entry point
  • Application.js the root component, loads required data and renders the app, loading indicator or an error page
  • Router.js the router, renders static content (header, sidebar...) and route specific components
  • lib/ helpers for roles, interfacing questionnaire fields with redux form
  • components/ components used in multiple modules
  • modules/ submodules of the app
  • store/ the redux store

Components

  • Layout components - page, box, login
  • Form components - form/FieldGroup renders a bootstrap field group, form/RFFieldGroup wraps a FieldGroup in a redux-form Field component
  • Wrappers - error/ErrorWrapper wraps a component with an error message, LoadingWrapper wraps a loading indicator, withConfirmNavigation is a HOC that takes a 'dirty' prop and shows a dialog if you try to navigate away from the page
  • Questionnaire - render the application forms/views
  • Router - HOC's to wrap router components with access control and a fall-through not found component

Modules

A module is a group of components and reducers (if necessary) to render a page.

modules/core

Components to render header, footer, sidebar, navbar, page title and a modal dialog. Reducers control the dialog and which menu items to display based on the users roles.

store/

  • index.js sets up the store
  • reducers.js create the root reducer from module reducers, redux form and react router
  • selectors.js combines all the selectors defined in module reducers for easy querying. more info
  • entities.js contains the actual entities received from the api
  • utils.js creates actions and a reducer for typical crud operations
  • middleware/api.js middleware to simplify making api requests

Reducers

Reducers are defined in the same file as related action creators and selectors. Selectors use recompose where appropriate.

Testing

All tests use mocha, chai, sinon and babel-plugin-rewire - you can 'rewire' variables in the modules scope making it easy to mock dependencies. Unlike webpack inject-loader or, I think, proxyquire, the rewiring is done after the module has been loaded so things like this won't work:

component.js

const enhancer = connect(mapStateToProps)
const Component = () => <div></div>
export default enhancer(Component)

test.js

Component.__Rewire__('connect', connectMock)
const wrapper = shallow(<Component />)
// error about no store because the real connect was 
// already applied to the component when it was imported

Server

The server has integration tests in tests/integration that use supertest for api calls and helpers in tests/helpers.js - createUserSession(user) returns an express app with a mocked session where the user is authenticated. This app can be passed to supertest.

The server needs more unit tests for routes and controllers, particularly to ensure that access is restricted properly.

Client

The client tests are set up with JSDOM and enzyme. Redux tests are fairly complete, I'm not sure of the need for tests with a mock store when the api middleware is tested. Component tests are pretty limited so far.