Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"env": {
"browser": true,
"es6": true,
"mocha": true,
"jest": true,
"node": true
},
"globals": {
Expand Down
58 changes: 40 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ boilerplate comes with:
- CSS, SASS and [css-modules](https://github.com/css-modules/css-modules) support with hot reloading and no [flash of unstyled content](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) ([css-hot-loader](https://github.com/shepherdwind/css-hot-loader))
- Routing with [react-router-v4](https://github.com/ReactTraining/react-router)
- Full production builds that do not rely on `babel-node`.
- Pre-configured testing tools with `jest` and `enzyme` to work with css modules, static files, and aliased module paths.

## Philosophy

Expand Down Expand Up @@ -71,6 +72,30 @@ If using Heroku, simply add a `Procfile` in the root directory. The
web: npm run serve
```

## Path Aliases

In `package.json`, there is a property named `_moduleAliases`. This object
defines the require() aliases used by both webpack and node.

Aliased paths are prefixed with one of two symbols, which denote different
things:

`@` - paths in the `common/` folder, e.g. `@components` or `@actions`, etc.
`$` - paths in the `server/` folder

Aliases are nice to use for convenience, and lets us avoid using relative paths
in our components:

```
// This sucks
import SomeComponent from '../../../components/SomeComponent';

// This is way better
import SomeComponent from '@components/SomeComponent';
```

You can add additional aliases in `package.json` to your own liking.

## Environment Variables

In development mode, environment variables are loaded by `dotenv` off the `.env`
Expand Down Expand Up @@ -160,20 +185,19 @@ higher. If you experience errors, please upgrade your version of Node.js.

## Testing

The default testing framework is Mocha, though you can use whatever you want.
Make sure you have it installed:
The default testing framework is Jest, though you can use whatever you want.

```
npm install -g mocha
```

Tests should reside in `test/spec` in their appropriate folders:
Tests and their corresponding files such as Jest snapshots, should be co-located
alongside the modules they are testing, in a `spec/` folder. For example:

```
├── test
│   ├── spec
│   │   ├── api
│   │   │   ├── todos.test.js
├── components
│   ├── todos
│   │   ├── TodoForm
│   │   │   ├── spec
│   │   │   │   ├── TodoForm.test.js
│   │   │   ├── index.js
│   │   │   ├── index.scss
```

Tests can be written with ES2015, since it passes through `babel-register`.
Expand All @@ -184,21 +208,19 @@ To run a single test:

```
npm test /path/to/single.test.js
```

To run a directory of tests:

```
npm test /path/to/test/directory
// Or, to watch for changes
npm run test:watch /path/to/single.test.js
```

To run all tests:

```
npm run test:all
```

This will run all tests in the `test/spec` directory.
// Or, to watch for changes
npm run test:all:watch
```

## Running ESLint

Expand Down
4 changes: 2 additions & 2 deletions client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import configureStore from 'store';
import App from 'containers/App';
import configureStore from '@store';
import App from '@containers/App';
import Loadable from 'react-loadable';

// Hydrate the redux store from server state.
Expand Down
2 changes: 1 addition & 1 deletion client/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
import 'semantic-ui-css/semantic.min.css';

// Base styles
import 'css/base/index.scss';
import '@css/base/index.scss';
6 changes: 3 additions & 3 deletions common/js/actions/todos.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
FETCH_TODOS_REQUEST,
FETCH_TODOS_SUCCESS,
FETCH_TODOS_FAILURE
} from 'constants/index';
import api from 'lib/api';
import generateActionCreator from 'lib/generateActionCreator';
} from '@constants/index';
import api from '@lib/api';
import generateActionCreator from '@lib/generateActionCreator';

export const addTodo = generateActionCreator(ADD_TODO, 'text');
export const removeTodo = generateActionCreator(REMOVE_TODO, 'id');
Expand Down
23 changes: 23 additions & 0 deletions common/js/components/todos/TodoForm/spec/TodoForm.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import TodoForm from '../index';
import { mount } from 'enzyme';

describe('TodoForm', () => {
it('renders correctly', () => {
const component = mount(<TodoForm />);
expect(component).toMatchSnapshot();
});

describe('clicking on submit button', () => {
test('calls the onSubmit prop', () => {
const mockSubmit = jest.fn();
const component = mount(<TodoForm onSubmit={mockSubmit} />);

// test form submission
component.setState({ todoText: 'Foobar' });
component.find('form').simulate('submit');

expect(mockSubmit.mock.calls.length).toEqual(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoForm renders correctly 1`] = `
<TodoForm>
<Form
as="form"
className="todoForm"
onSubmit={[Function]}
>
<form
className="ui form todoForm"
onSubmit={[Function]}
>
<FormGroup>
<div
className="fields"
>
<FormInput
as={[Function]}
control={[Function]}
onChange={[Function]}
placeholder="Add a todo..."
type="text"
value=""
>
<FormField
control={[Function]}
onChange={[Function]}
placeholder="Add a todo..."
type="text"
value=""
>
<div
className="field"
>
<Input
onChange={[Function]}
placeholder="Add a todo..."
type="text"
value=""
>
<div
className="ui input"
>
<input
key="text"
onChange={[Function]}
placeholder="Add a todo..."
type="text"
value=""
/>
</div>
</Input>
</div>
</FormField>
</FormInput>
<FormButton
as={[Function]}
content="Add"
control={[Function]}
icon="plus"
>
<FormField
content="Add"
control={[Function]}
icon="plus"
>
<div
className="field"
>
<Button
as="button"
content="Add"
icon="plus"
>
<button
className="ui button"
onClick={[Function]}
role="button"
>
<Icon
as="i"
key="plus"
name="plus"
>
<i
aria-hidden="true"
className="plus icon"
/>
</Icon>
Add
</button>
</Button>
</div>
</FormField>
</FormButton>
</div>
</FormGroup>
</form>
</Form>
</TodoForm>
`;
10 changes: 10 additions & 0 deletions common/js/components/todos/TodoItem/spec/TodoItem.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import TodoItem from '../index';
import { shallow } from 'enzyme';

describe('TodoItem', () => {
it('renders correctly', () => {
const component = shallow(<TodoItem todo={{ id: 1, text: 'Work' }} />);
expect(component).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoItem renders correctly 1`] = `
<ListItem
className="todo extra"
>
<ListContent
floated="right"
>
<Button
as="button"
icon="remove"
onClick={[Function]}
size="mini"
/>
</ListContent>
<ListContent
floated="left"
>
<Checkbox
onChange={[Function]}
type="checkbox"
/>
</ListContent>
<ListContent
className="text"
>
Work
</ListContent>
</ListItem>
`;
2 changes: 1 addition & 1 deletion common/js/components/todos/TodoList/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { List } from 'semantic-ui-react';
import { TodoItem } from 'components/todos';
import { TodoItem } from '@components/todos';
import classnames from 'classnames';
import css from './index.scss';

Expand Down
27 changes: 27 additions & 0 deletions common/js/components/todos/TodoList/spec/TodoList.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import TodoList from '../index';
import { shallow } from 'enzyme';

describe('TodoList', () => {
let todos = {
isFetched: true,
todos: [
{
id: 1,
text: 'Learn React',
completed: true
},
{
id: 2,
text: 'Learn Redux',
completed: true
}
]
};


it('renders correctly', () => {
const component = shallow(<TodoList todos={todos} />);
expect(component).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoList renders correctly 1`] = `
<List
className="todos"
divided={true}
>
<TodoItem
key="0"
onChange={[Function]}
onRemove={[Function]}
todo={
Object {
"completed": true,
"id": 1,
"text": "Learn React",
}
}
/>
<TodoItem
key="1"
onChange={[Function]}
onRemove={[Function]}
todo={
Object {
"completed": true,
"id": 2,
"text": "Learn Redux",
}
}
/>
</List>
`;
5 changes: 2 additions & 3 deletions common/js/containers/App/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react';
import { Switch } from 'react-router-dom';
import { RouteWithSubRoutes } from 'components/common';
import { Container } from 'semantic-ui-react';
import { Header, Footer } from 'components/common';
import { Header, Footer, RouteWithSubRoutes } from '@components/common';
import { hot } from 'react-hot-loader';
import routes from 'routes';
import routes from '@routes';

const App = () => (
<Container fluid={false}>
Expand Down
Loading