Skip to content

Commit

Permalink
Merge pull request #22 from andela/ft-create-articles-164047027
Browse files Browse the repository at this point in the history
164047027 Users should be able to create articles
  • Loading branch information
Derrickkip committed Apr 25, 2019
2 parents 46a18bb + 4c46785 commit ce2eb62
Show file tree
Hide file tree
Showing 42 changed files with 1,806 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ yarn-error.log*

#idea
.idea/
.css

yarn.lock
package-lock.json
29 changes: 24 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"axios": "^0.18.0",
"coveralls": "^3.0.3",
"dotenv": "^7.0.0",
"draft-js": "^0.10.5",
"draft-js-export-html": "^1.3.3",
"draftjs-to-html": "^0.8.4",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.9.1",
"enzyme-to-json": "^3.3.5",
Expand All @@ -16,14 +19,22 @@
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"jest-fetch-mock": "^2.1.2",
"fetch-mock": "^7.3.1",
"html-react-parser": "^0.7.0",
"html-to-draftjs": "^1.4.0",
"import": "0.0.6",
"jest-mock": "^24.7.0",
"mdbreact": "^4.12.0",
"moxios": "^0.4.0",
"node-sass": "^4.11.0",
"npm-run-all": "^4.1.5",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-bootstrap": "^1.0.0-beta.6",
"react-dnd": "^7.4.5",
"react-dnd-html5-backend": "^7.4.4",
"react-dom": "^16.8.6",
"react-draft-wysiwyg": "^1.13.2",
"react-facebook-login": "^4.1.1",
"react-google-login": "^5.0.4",
"react-native-cloudinary": "^1.0.1",
Expand All @@ -32,9 +43,14 @@
"react-router-dom": "^5.0.0",
"react-scripts": "2.1.8",
"react-social-login": "^3.4.4",
"react-tag-input": "^6.4.0",
"react-tagsinput": "^3.19.0",
"react-testing-library": "^6.1.2",
"reading-time": "^1.2.0",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.8",
"redux-mock-store": "^1.5.3",
"redux-promise-middleware": "^6.1.0",
"redux-thunk": "^2.3.0",
"semantic-ui-react": "^0.86.0",
"watch": "^1.0.2"
Expand Down Expand Up @@ -66,18 +82,21 @@
"not op_mini all"
],
"devDependencies": {
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.12.1",
"enzyme-to-json": "^3.3.5",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^4.1.0",
"redux-mock-store": "^1.5.3",
"prettier": "^1.16.4",
"sinon": "^7.3.1",
"redux-promise-middleware": "^6.1.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"node-sass": "^4.11.0",
"npm-run-all": "^4.1.5"
"npm-run-all": "^4.1.5",
"prettier": "^1.16.4",
"redux-mock-store": "^1.5.3",
"redux-promise-middleware": "^6.1.0",
"sinon": "^7.3.2"
}
}
39 changes: 21 additions & 18 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<title>Authors Haven</title>
</head>

<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="root-content" id="root"></div>

</body>

<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Authors Haven</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="root-content"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
11 changes: 11 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.App-header {
background-color: #F6FAF9;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: black; }
.App-header label {
color: black; }
12 changes: 12 additions & 0 deletions src/App.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.App-header {
background-color: #F6FAF9;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: black;
label {color: black};
}

19 changes: 19 additions & 0 deletions src/actions/editArticle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from 'axios';
import { UPDATE_ARTICLE, ACTION_FAILED } from './types';
import axiosHeader from '../axios_config';

const apiURL2 = 'https://aholympian.herokuapp.com/api/articles/';

export const editArticle = (slug, postData) => async (dispatch) => {
await axios
.put(`${apiURL2}${slug}`, postData, axiosHeader)
.then((result) => {
dispatch({ type: UPDATE_ARTICLE, payload: result.data });
})
.catch((error) => {
dispatch({
type: ACTION_FAILED,
payload: error,
});
});
};
19 changes: 19 additions & 0 deletions src/actions/getArticles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from 'axios';
import axiosHeader from '../axios_config';
import { FETCH_ARTICLES, ACTION_FAILED } from './types';

const apiURL = 'https://aholympian.herokuapp.com/api/';

export const getArticles = () => async (dispatch) => {
await axios
.get(`${apiURL}articles`, axiosHeader)
.then((result) => {
dispatch({ type: FETCH_ARTICLES, payload: result.data });
})
.catch((error) => {
dispatch({
type: ACTION_FAILED,
payload: error,
});
});
};
33 changes: 33 additions & 0 deletions src/actions/postArticles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'axios';
import { FETCH_ARTICLE, NEW_ARTICLE, ACTION_FAILED } from './types';
import axiosHeader from '../axios_config';

const apiURL2 = 'https://aholympian.herokuapp.com/api/articles/';

export const createArticle = postData => async (dispatch) => {
await axios
.post(`${apiURL2}`, postData, axiosHeader)
.then((result) => {
dispatch({ type: NEW_ARTICLE, payload: result.data });
})
.catch((error) => {
dispatch({
type: ACTION_FAILED,
payload: error,
});
});
};

export const fetchArticle = slug => async (dispatch) => {
await axios
.get(`${apiURL2}${slug}`, axiosHeader)
.then((result) => {
dispatch({ type: FETCH_ARTICLE, payload: result.data });
})
.catch((error) => {
dispatch({
type: ACTION_FAILED,
payload: error,
});
});
};
48 changes: 48 additions & 0 deletions src/actions/tests/editarticle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import mockAxios from 'axios';

import { UPDATE_ARTICLE, ACTION_FAILED } from '../types';
import { editArticle } from '../editArticle';


describe('testing update article', () => {
it('tests editing an existing article', () => {
const testStore = configureMockStore([thunk]);
const store = testStore({});
mockAxios.put.mockResolvedValue({
data: {},
});

const expectedAction = [
{
type: UPDATE_ARTICLE,
payload: {},
},
];

return store.dispatch(editArticle({})).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});
it("dispatches UPDATE_ARTICLE action and returns an error", async () => {
const testStore = configureMockStore([thunk]);
const store = testStore({});
mockAxios.put.mockImplementationOnce(() =>
Promise.reject({
error: "Something bad happened"
})
);

try {
await store.dispatch(editArticle());
} catch {
const actions = store.getActions();

expect.assertions(3);
expect(actions[0].type).toEqual("UPDATE_ARTICLE");
expect(actions[1].type).toEqual("ACTION_FAILED");
expect(actions[1].payload.error).toEqual("Something bad happened");
}
});
});
34 changes: 34 additions & 0 deletions src/actions/tests/getarticles.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';

import { FETCH_ARTICLES } from '../types';
import { getArticles } from '../getArticles';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('getArticles actions', () => {
it('creates FETCH_ARTICLES after successfuly fetching articles', () => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: {},
});
});

const expectedActions = [{
type: FETCH_ARTICLES,
payload: {},
},
];

const store = mockStore({ payload: {} });

return store.dispatch(getArticles()).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
});
47 changes: 47 additions & 0 deletions src/actions/tests/postactions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import mockAxios from 'axios';

import { NEW_ARTICLE, FETCH_ARTICLE } from '../types';
import { createArticle, fetchArticle } from '../postArticles';

jest.mock('axios');

describe('testing new article', () => {
it('tests posting a new article', () => {
const testStore = configureMockStore([thunk]);
const store = testStore({});
mockAxios.post.mockResolvedValue({
data: {},
});

const expectedAction = [
{
type: NEW_ARTICLE,
payload: {},
},
];

return store.dispatch(createArticle({})).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});

it('tests fetching a single article', () => {
const testStore = configureMockStore([thunk]);
const store = testStore({});

mockAxios.get.mockResolvedValue({
response: {
data: {},
},
});

const expectedAction = [{
type: FETCH_ARTICLE,
}];
return store.dispatch(fetchArticle({})).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});
});
8 changes: 7 additions & 1 deletion src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@ export const FAIL_SEND = 'FAIL_SEND';
export const RESET_REQUEST = 'RESET_REQUEST';
export const RESET_SUCCESSFUL = 'RESET_SUCCESSFUL';
export const RESET_FAILURE = 'RESET_FAILURE';

export const BASE_URL = 'https://aholympian.herokuapp.com/api/';
export const FETCH_ARTICLES = 'FETCH_ARTICLES';
export const FETCH_ARTICLES_SUCCESS = 'FETCH_ARTICLES_SUCCESS';
export const FETCH_ARTICLE = 'FETCH_ARTICLE';
export const NEW_ARTICLE = 'NEW_ARTICLE';
export const UPDATE_ARTICLE = 'UPDATE_ARTICLE';
export const AUTHENTICATION_FAILED = 'AUTHENTICATION_FAILED';
export const ACTION_FAILED = 'ACTION_FAILED';
12 changes: 9 additions & 3 deletions src/axios_config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import axios from 'axios';

let user = JSON.parse(localStorage.getItem('user'));

let userToken;
try {
const user = JSON.parse(localStorage.getItem('user'));
user = JSON.parse(localStorage.getItem('user'));
const { token } = user.user;
userToken = token;
} catch (err) {
userToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTQsImV4cCI6MTU2MDc5MDQwMX0.IiKq2Fzbuhu424D3xBQrdSkZKzVx46bkALCgE4B4NaA';
const error = err.message;
}


const axiosHeader = {
headers: {
'Content-Type': 'application/json',
Authorization: `Token ${userToken}`,
},
};

export default axiosHeader;
export default axiosHeader;
Loading

0 comments on commit ce2eb62

Please sign in to comment.