Skip to content

Commit

Permalink
Merge d48579d into 511e47e
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilome committed Feb 7, 2019
2 parents 511e47e + d48579d commit 55e01d2
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 187 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -5,5 +5,4 @@ cache:
directories:
- node_modules
script:
- npm test
- npm run coveralls
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -48,6 +48,7 @@
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"axios-mock-adapter": "^1.16.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
Expand Down
19 changes: 13 additions & 6 deletions src/actions/index.js
Expand Up @@ -3,7 +3,14 @@ import { toast } from 'react-toastify';

import axios from '../services/axios';
import types from './types';
import { saveToken, getToken, removeToken } from '../utils/localStorage';
import {
saveToken,
getToken,
removeToken,
getCartItems,
updateCartItems,
clearCartItems,
} from '../utils/localStorage';
import jwt from '../utils/jwt';

const errorResponse = ({ response }) => (response ? response.data.message : 'something went wrong');
Expand Down Expand Up @@ -90,10 +97,10 @@ export const getMenu = () => async (dispatch) => {

export const addToCart = ({ foodId, foodName, foodPrice }) => {
const foodDetails = { [foodId]: { foodName, foodPrice } };
const cart = JSON.parse(localStorage.getItem('cart')) || {};
const cart = getCartItems();
if (!Object.keys(cart).includes(`${foodId}`)) {
const updatedCart = { ...cart, ...foodDetails };
localStorage.setItem('cart', JSON.stringify(updatedCart));
updateCartItems(updatedCart);
toast.success(`${foodName} added to cart 🚀`);
return { type: types.ADD_TO_CART, payload: foodDetails };
}
Expand All @@ -102,9 +109,9 @@ export const addToCart = ({ foodId, foodName, foodPrice }) => {
};

export const removeFromCart = (foodId) => {
const cart = JSON.parse(localStorage.getItem('cart')) || {};
const cart = getCartItems();
const updatedCart = omit(cart, foodId);
localStorage.setItem('cart', JSON.stringify(updatedCart));
updateCartItems(updatedCart);
return { type: types.REMOVE_FROM_CART, payload: { foodId } };
};

Expand All @@ -114,7 +121,7 @@ export const checkout = foodIds => async (dispatch) => {
dispatch(startFetching());
try {
await axios.post('/orders', { foodIds });
localStorage.removeItem('cart');
clearCartItems();
dispatch({ type: types.CHECKOUT });
toast.success('Order placed! 🎉');
return dispatch(stopFetching());
Expand Down
2 changes: 1 addition & 1 deletion src/components/Cart.jsx
Expand Up @@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import CheckOutCard from './CheckOutCard';
import { removeFromCart, checkout } from '../actions';

export class Cart extends Component {
class Cart extends Component {
onCheckoutClick = async () => {
const { cart, checkout: placeOrder, history } = this.props;
const foodItems = Object.keys(cart).map(Number);
Expand Down
6 changes: 3 additions & 3 deletions src/components/OrderCard.jsx
Expand Up @@ -48,11 +48,11 @@ const OrderCard = ({
.join('/')}
</p>
</div>
<div className="order-status">
<div className={`order-status-${status}`} />
<div>
<div className={`order-status order-status-${status}`} />
<div className="cancel-order">
<button className="small" type="button" onClick={cancelOrderCallback}>
cancel
cancel order
</button>
</div>
</div>
Expand Down
12 changes: 10 additions & 2 deletions src/index.css
Expand Up @@ -306,6 +306,7 @@ fieldset > div {
display: table;
margin: 20px 15px;
padding: 20px 0;
position: relative;
width: 300px;
}

Expand All @@ -314,8 +315,7 @@ fieldset > div {
margin: 5px auto;
}

.order-card > *:last-child,
.order-card > *:nth-last-child(2) {
.order-card > *:last-child {
border-bottom: none;
}

Expand Down Expand Up @@ -363,6 +363,14 @@ fieldset > div {
text-align: left;
}

.order-status {
padding: 0 10px;
position: absolute;
right: 10px;
top: -10px;
width: auto;
}

.order-status-new {
background: #333;
}
Expand Down
17 changes: 13 additions & 4 deletions src/tests/actions/cart.test.js
@@ -1,8 +1,11 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '../../services/axios';
import {
addToCart, getCart, removeFromCart, checkout,
} from '../../actions';

const axiosMock = new MockAdapter(axios);

describe('addToCart()', () => {
const foodDetails = { foodId: 1, foodName: 'Kiwi', foodPrice: 320 };
it('should dispatch action for adding to cart success', () => {
Expand Down Expand Up @@ -36,22 +39,28 @@ describe('removeFromCart()', () => {
});

describe('checkout()', () => {
afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});

afterAll(() => axiosMock.restore);

const dispatch = jest.fn();
const response = { data: { message: 'placed' } };

it('should place order successfully', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => Promise.resolve({ status: 201 }));
axiosMock.onPost().replyOnce(201);
await checkout([1, 2])(dispatch);
expect(dispatch).toHaveBeenCalledTimes(3);
expect(dispatch.mock.calls[1][0]).toEqual({ type: 'CHECKOUT' });
});

it('should fail to place order', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => Promise.reject(response));
axiosMock.onPost().replyOnce(400, { message: 'unable to place order' });
await checkout([1, 2])(dispatch);
expect(dispatch).toHaveBeenLastCalledWith({
type: 'STOP_FETCHING',
payload: { error: true, message: 'something went wrong' },
payload: { error: true, message: 'unable to place order' },
});
});
});
19 changes: 14 additions & 5 deletions src/tests/actions/getMenu.test.js
@@ -1,13 +1,22 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '../../services/axios';
import { getMenu } from '../../actions';

const axiosMock = new MockAdapter(axios);

describe('Get Food Menu', () => {
afterEach(() => jest.resetAllMocks());
afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});

afterAll(() => axiosMock.restore);

const dispatch = jest.fn();
const response = { data: { menu: [{ foodName: 'Rice?' }] } };
const response = { menu: [{ foodName: 'Rice?' }] };

it('should get all food items', async () => {
jest.spyOn(axios, 'get').mockImplementation(() => Promise.resolve(response));
axiosMock.onGet().replyOnce(200, response);
await getMenu()(dispatch);

expect(dispatch).toHaveBeenCalledTimes(3);
Expand All @@ -18,13 +27,13 @@ describe('Get Food Menu', () => {
});

it('should fail to get food items', async () => {
jest.spyOn(axios, 'get').mockImplementation(() => Promise.reject(response));
axiosMock.onGet().replyOnce(400, { message: 'unable to fetch items' });
await getMenu()(dispatch);

expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith({
type: 'STOP_FETCHING',
payload: { error: true, message: 'something went wrong' },
payload: { error: true, message: 'unable to fetch items' },
});
});
});
20 changes: 13 additions & 7 deletions src/tests/actions/logInUser.test.js
@@ -1,18 +1,26 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '../../services/axios';
import { logInUser } from '../../actions';
import jwt from '../../utils/jwt';

const axiosMock = new MockAdapter(axios);

describe('logInUser()', () => {
afterEach(() => jest.resetAllMocks());
afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});

afterAll(() => axiosMock.restore);

const dispatch = jest.fn();
const response = { data: { status: 'success', user: { token: 'something' } } };
const response = { status: 'success', user: { token: 'something' } };
jest
.spyOn(jwt, 'decode')
.mockImplementation(() => ({ userName: 'jamjum', userStatus: 'customer' }));

it('should login user successfully', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => Promise.resolve(response));
axiosMock.onPost().replyOnce(200, response);
await logInUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(4);
expect(dispatch.mock.calls[1][0]).toEqual({
Expand All @@ -22,14 +30,12 @@ describe('logInUser()', () => {
});

it('should fail to login user if errors exist', async () => {
jest
.spyOn(axios, 'post')
.mockImplementation(() => Promise.reject(new Error('something went wrong')));
axiosMock.onPost().replyOnce(400, { message: 'nope, not working' });

await logInUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith({
payload: { error: true, message: 'something went wrong' },
payload: { error: true, message: 'nope, not working' },
type: 'STOP_FETCHING',
});
});
Expand Down
30 changes: 22 additions & 8 deletions src/tests/actions/orders.test.js
@@ -1,18 +1,26 @@
import MockAdapter from 'axios-mock-adapter';
import { getUserOrderHistory, cancelOrder } from '../../actions';
import jwt from '../../utils/jwt';
import axios from '../../services/axios';

const axiosMock = new MockAdapter(axios);

afterAll(() => jest.restoreAllMocks());

describe('getUserOrderHistory', () => {
jest.spyOn(jwt, 'decode').mockImplementation(() => ({ userId: 1 }));
const dispatch = jest.fn();
const response = { data: { orders: [] } };
const response = { orders: [] };

afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});

afterEach(() => jest.resetAllMocks());
afterAll(() => axiosMock.restore);

it('should dispatch correct action for getUserOrderHistory success', async () => {
jest.spyOn(axios, 'get').mockImplementation(() => Promise.resolve(response));
axiosMock.onGet().replyOnce(200, response);
await getUserOrderHistory()(dispatch);

expect(jwt.decode).toHaveBeenCalled();
Expand All @@ -24,7 +32,7 @@ describe('getUserOrderHistory', () => {
});

it('should dispatch correct action for getUserOrderHistory failure', async () => {
jest.spyOn(axios, 'get').mockImplementation(() => Promise.reject(response));
axiosMock.onGet().replyOnce(400);
await getUserOrderHistory()(dispatch);

expect(dispatch).toHaveBeenCalledTimes(3);
Expand All @@ -40,11 +48,17 @@ describe('getUserOrderHistory', () => {

describe('cancelOrder()', () => {
const dispatch = jest.fn();
const errResponse = { response: { data: { message: 'failed to cancel order' } } };
afterEach(() => jest.resetAllMocks());
const errResponse = { message: 'failed to cancel order' };

afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});

afterAll(() => axiosMock.restore);

it('should dispatch correct action to cancel an order', async () => {
jest.spyOn(axios, 'delete').mockImplementation(() => Promise.resolve());
axiosMock.onDelete().replyOnce(204);
await cancelOrder(7)(dispatch);

expect(dispatch).toHaveBeenCalledTimes(3);
Expand All @@ -59,7 +73,7 @@ describe('cancelOrder()', () => {
});

it('should dispatch correct action for order cancellation failure', async () => {
jest.spyOn(axios, 'delete').mockImplementation(() => Promise.reject(errResponse));
axiosMock.onDelete().replyOnce(400, errResponse);
await cancelOrder(2)(dispatch);

expect(dispatch).toHaveBeenCalledTimes(3);
Expand Down
19 changes: 12 additions & 7 deletions src/tests/actions/signUpUser.test.js
@@ -1,29 +1,34 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '../../services/axios';
import { signUpUser } from '../../actions';
import jwt from '../../utils/jwt';

const axiosMock = new MockAdapter(axios);

describe('signUpUser()', () => {
afterEach(() => jest.resetAllMocks());
afterEach(() => {
jest.resetAllMocks();
axiosMock.reset();
});
afterAll(() => axiosMock.restore);

const dispatch = jest.fn();
const response = { data: { status: 'success', user: { token: 'something' } } };
const response = { status: 'success', user: { token: 'something' } };
jest.spyOn(jwt, 'decode').mockImplementation(() => ({ userName: 'kay', userStatus: 'admin' }));

it('should sign up user successfully', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => Promise.resolve(response));
axiosMock.onPost().replyOnce(200, response);
await signUpUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(3);
});

it('should fail to sign up user if errors exist', async () => {
jest
.spyOn(axios, 'post')
.mockImplementation(() => Promise.reject(new Error('something went wrong')));
axiosMock.onPost().replyOnce(400, { message: 'lol my brother' });

await signUpUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith({
payload: { error: true, message: 'something went wrong' },
payload: { error: true, message: 'lol my brother' },
type: 'STOP_FETCHING',
});
});
Expand Down

0 comments on commit 55e01d2

Please sign in to comment.