Skip to content

Commit

Permalink
Merge 216426b into 5e445d0
Browse files Browse the repository at this point in the history
  • Loading branch information
Musigwa committed May 7, 2019
2 parents 5e445d0 + 216426b commit e135f7e
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 16 deletions.
72 changes: 72 additions & 0 deletions src/__tests__/__actions__/following.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import moxios from "moxios";
import thunk from "redux-thunk";
import configureMockStore from "redux-mock-store";
import axios from "../../utils/axios";

import { followUser } from "../../redux/actions/followingActions";
import {
FOLLOWING_FAILED,
FOLLOWING_SUCCESS,
WAITING_RESPONSE
} from "../../redux/actionTypes";

const mockStore = configureMockStore([thunk]);
let store;

describe("test the following actions", () => {
beforeEach(() => {
moxios.install(axios);
store = mockStore({});
});

afterEach(() => {
moxios.uninstall(axios);
});

describe("test fetch following", () => {
test("should dispatch the success action after successfully following the user", () => {
store = mockStore({});
const expectedActions = [
{ type: WAITING_RESPONSE },
{ type: FOLLOWING_SUCCESS, payload: true }
];
moxios.stubRequest(`${process.env.API_URL}/profiles/claude/follow`, {
status: 201,
response: { message: "Follow successful" }
});
return store.dispatch(followUser("claude")).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});

test("should dispatch the success action after successfully unfollowing the user", () => {
store = mockStore({});
const expectedActions = [
{ type: WAITING_RESPONSE },
{ type: FOLLOWING_SUCCESS, payload: false }
];
moxios.stubRequest(`${process.env.API_URL}/profiles/claude/follow`, {
status: 202,
response: { message: "You have unfollowed this author" }
});
return store.dispatch(followUser("claude")).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});

test("should dispatch the failed action if there was a problem while following the user", () => {
store = mockStore({});
const expectedActions = [
{ type: WAITING_RESPONSE },
{ type: FOLLOWING_FAILED, payload: false }
];
moxios.stubRequest(`${process.env.API_URL}/profiles/claude/follow`, {
status: 500,
response: { message: "Error while following the user" }
});
return store.dispatch(followUser("claude")).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
});
22 changes: 22 additions & 0 deletions src/__tests__/__mocks__/testData.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export const article3 = {
description: "Hello world"
};
export const props1 = {
following: {
status: true,
isFetching: false
},
followAuthor: jest.fn(),
followUser: jest.fn(),
history: {
push: jest.fn()
},
Expand Down Expand Up @@ -73,6 +79,10 @@ export const props1 = {
deleteOneArticle: jest.fn()
};
export const props2 = {
following: {
status: true,
isFetching: false
},
history: {
push: jest.fn()
},
Expand Down Expand Up @@ -116,6 +126,10 @@ export const props2 = {
deleteOneArticle: jest.fn()
};
export const props3 = {
following: {
status: true,
isFetching: false
},
history: {
push: jest.fn()
},
Expand All @@ -133,6 +147,10 @@ export const props3 = {
deleteOneArticle: jest.fn(() => "hello world")
};
export const props4 = {
following: {
status: true,
isFetching: false
},
history: {
push: jest.fn()
},
Expand All @@ -150,6 +168,10 @@ export const props4 = {
deleteOneArticle: jest.fn()
};
export const props5 = {
following: {
status: true,
isFetching: false
},
history: {
push: jest.fn()
},
Expand Down
18 changes: 18 additions & 0 deletions src/__tests__/__reducers__/authReducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
LOGIN_FAILED,
LOGIN_SUCCESS,
LOGIN_INPUT_CHANGE,
SET_CURRENT_USER,
SUBMITTING_LOGIN_CREDENTIALS,
IS_OPENING_SOCIAL_AUTH_PROVIDER,
CANCEL_SOCIAL_AUTH
Expand All @@ -19,6 +20,7 @@ describe("Login reducers", () => {
...INITIAL_STATE
});
});

it("should handle LOGIN_INPUT_CHANGE", () => {
expect(
loginReducers(INITIAL_STATE, {
Expand All @@ -39,6 +41,7 @@ describe("Login reducers", () => {
[passwordInput.name]: passwordInput.value
});
});

it("should handle SUBMITTING_LOGIN_CREDENTIALS", () => {
expect(
loginReducers(INITIAL_STATE, {
Expand All @@ -49,6 +52,7 @@ describe("Login reducers", () => {
isSubmitting: true
});
});

it("should handle LOGIN_FAILED", () => {
const errorPayload = { message: "Invalid email or password", errors: {} };
expect(
Expand All @@ -61,6 +65,7 @@ describe("Login reducers", () => {
errors: { message: errorPayload.message, ...errorPayload.errors }
});
});

it("should handle LOGIN_SUCCESS", () => {
const successPayload = {
message: "Sign in successfully",
Expand All @@ -77,6 +82,7 @@ describe("Login reducers", () => {
token: successPayload.token
});
});

it("should handle IS_OPENING_SOCIAL_AUTH_PROVIDER", () => {
expect(
loginReducers(INITIAL_STATE, {
Expand All @@ -95,4 +101,16 @@ describe("Login reducers", () => {
isSubmitting: false
});
});

it("should handle SET_CURRENT_USER", () => {
expect(
loginReducers(INITIAL_STATE, {
type: SET_CURRENT_USER,
payload: { name: "musigwa" }
})
).toEqual({
...INITIAL_STATE,
currentUser: { name: "musigwa" }
});
});
});
48 changes: 48 additions & 0 deletions src/__tests__/__reducers__/following.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import followingReducer, {
initialState
} from "../../redux/reducers/followingReducer";
import {
FOLLOWING_FAILED,
FOLLOWING_SUCCESS,
WAITING_RESPONSE
} from "../../redux/actionTypes";

describe("Following reducers", () => {
it("should return initial state", () => {
expect(followingReducer(undefined, {})).toEqual(initialState);
});

it("should handle FOLLOWING", () => {
expect(followingReducer(initialState, { type: WAITING_RESPONSE })).toEqual({
...initialState,
isFetching: true
});
});

it("should handle FOLLOWING SUCCESS, if followed, the status must be true", () => {
expect(
followingReducer(initialState, {
type: FOLLOWING_SUCCESS,
payload: true
})
).toEqual({ ...initialState, status: true });
});

it("should handle FOLLOWING SUCCESS, if unfollowed, the status must be false", () => {
expect(
followingReducer(initialState, {
type: FOLLOWING_SUCCESS,
payload: false
})
).toEqual({ ...initialState, status: false });
});

it("should handle WAITING_RESPONSE FAILED", () => {
expect(
followingReducer(initialState, {
type: FOLLOWING_FAILED,
payload: false
})
).toEqual({ ...initialState, status: false });
});
});
31 changes: 28 additions & 3 deletions src/__tests__/__views__/ReadArticle.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import {
} from "../../views/ReadArticle";
import { props1, props2, props3, props4, props5 } from "../__mocks__/testData";

describe(" Read article", () => {
let inst;
const wrp = shallow(<Article {...props1} />);
beforeAll(() => {
inst = wrp.instance();
jest.spyOn(inst, "followAuthor");
jest.spyOn(inst, "handleDeleteArticle");
});

describe("Read article", () => {
test("render the full component with aside articles", () => {
const wrapper = shallow(<Article {...props1} />);
expect(wrapper.find(".article-container")).toHaveLength(1);
Expand Down Expand Up @@ -45,7 +53,8 @@ describe(" Read article", () => {
asideArticles: {
articles: [{ body: "hello world" }, { body: "hello world" }]
}
}
},
following: { isFetching: true, status: true }
};
expect(mapStateToProps(state)).toEqual({
currentUser: {
Expand All @@ -54,7 +63,8 @@ describe(" Read article", () => {
asideArticles: {
articles: [{ body: "hello world" }, { body: "hello world" }]
},
article: state.fetchedArticle
article: state.fetchedArticle,
following: { isFetching: true, status: true }
});
});

Expand Down Expand Up @@ -85,4 +95,19 @@ describe(" Read article", () => {
});
expect(secondCall).toEqual(false);
});

describe("Follow an author", () => {
test("should map followUser article to props", () => {
const dispatch = jest.fn();
mapDispatchToProps(dispatch).followUser("claude");
expect(dispatch.mock.calls[0][0]).toBeDefined();
});

test("should call followAuthor when the follow button is clicked", () => {
wrp.find(`[data-test="follow_author"]`).simulate("click");
expect(inst.followAuthor).toHaveBeenCalledWith(
props1.article.article.author.username
);
});
});
});
7 changes: 7 additions & 0 deletions src/redux/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,10 @@ export const SET_FORM_INPUT = "SET_FORM_INPUT";
export const SET_SUCCESS = "SET_SUCCESS";
export const SET_IMAGE = "SET_IMAGE";
export const CLEAR_MESSAGE = "CLEAR_MESSAGE";

/**
* @description following action types
*/
export const FOLLOWING_SUCCESS = "FOLLOWING_SUCCESS";
export const WAITING_RESPONSE = "WAITING_RESPONSE";
export const FOLLOWING_FAILED = "FOLLOWING_FAILED";
30 changes: 30 additions & 0 deletions src/redux/actions/followingActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import "@babel/polyfill";
import axios from "../../utils/axios";
import {
FOLLOWING_FAILED,
FOLLOWING_SUCCESS,
WAITING_RESPONSE
} from "../actionTypes";

export const followed = () => ({
type: FOLLOWING_SUCCESS,
payload: true
});

export const unfollowed = () => ({
type: FOLLOWING_SUCCESS,
payload: false
});

export const failed = () => ({ type: FOLLOWING_FAILED, payload: false });

export const followUser = username => async dispatch => {
try {
dispatch({ type: WAITING_RESPONSE });
const { status } = await axios.post(`/profiles/${username}/follow`);
if (status === 201) dispatch(followed());
if (status === 202) dispatch(unfollowed());
} catch (error) {
dispatch(failed());
}
};
34 changes: 34 additions & 0 deletions src/redux/reducers/followingReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
FOLLOWING_FAILED,
FOLLOWING_SUCCESS,
WAITING_RESPONSE
} from "../actionTypes";

export const initialState = {
isFetching: false,
status: null
};

export default (state = initialState, { type, payload }) => {
switch (type) {
case WAITING_RESPONSE:
return {
...state,
isFetching: true
};
case FOLLOWING_SUCCESS:
return {
...state,
isFetching: false,
status: payload
};
case FOLLOWING_FAILED:
return {
...state,
isFetching: false,
status: payload
};
default:
return state;
}
};
4 changes: 3 additions & 1 deletion src/redux/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import socialAuthReducers from "./socialAuthReducers";
import createArticleReducer from "./createArticleReducer";
import readArticleReducer from "./readArticleReducer";
import userReducer from "./userReducer";
import following from "./followingReducer";

export default combineReducers({
auth: loginReducers,
Expand All @@ -15,5 +16,6 @@ export default combineReducers({
updatePassword: updatePasswordReducers,
socialAuth: socialAuthReducers,
fetchedArticle: readArticleReducer,
user: userReducer
user: userReducer,
following
});
Loading

0 comments on commit e135f7e

Please sign in to comment.