From 0bcb7950fae5265fbe2e92e505cbe7ed331feb35 Mon Sep 17 00:00:00 2001 From: David Costa Date: Thu, 18 Jul 2019 19:42:01 -0300 Subject: [PATCH 1/2] Add Lint configs --- .editorconfig | 12 ++++++++++++ .eslintrc.js | 15 +++++++++++++++ .prettierrc | 5 +++++ .travis.yml | 2 ++ package.json | 9 ++++++++- 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .prettierrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5760be5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..73640fe --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + extends: ['airbnb-base', 'prettier'], + plugins: ['prettier'], + rules: { + 'prettier/prettier': 'error', + 'arrow-body-style': ['error', 'as-needed'], + 'no-param-reassign': 'off', + 'no-console': ['error', { allow: ['error'] }], + }, + env: { + es6: true, + jest: true, + browser: true, + }, +}; diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5e2863a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.travis.yml b/.travis.yml index 4310121..551b416 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,5 @@ node_js: - "current" script: - npm run coveralls +before_script: + - yarn run lint diff --git a/package.json b/package.json index b952182..9b82798 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls", + "lint": "eslint lib/** __tests__/**", "test": "jest", "test_debug": "node --inspect node_modules/.bin/jest" }, @@ -31,8 +32,14 @@ "@babel/preset-env": "^7.5.4", "@babel/runtime": "^7.5.4", "coveralls": "^3.0.5", + "eslint": "^6.0.1", + "eslint-config-airbnb-base": "^13.2.0", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-import": "^2.18.0", + "eslint-plugin-prettier": "^3.1.0", "jest": "^24.8.0", - "jest-fetch-mock": "^2.1.2" + "jest-fetch-mock": "^2.1.2", + "prettier": "^1.18.2" }, "jest": { "automock": false, From edb9ab83e977a41a0f23e9ab889fffd915095bb3 Mon Sep 17 00:00:00 2001 From: David Costa Date: Thu, 18 Jul 2019 19:42:19 -0300 Subject: [PATCH 2/2] Fix lint on files --- __tests__/api.test.js | 36 ++++++------ __tests__/middleware.test.js | 106 +++++++++++++++++------------------ index.js | 4 +- lib/api.js | 28 ++++----- lib/middleware.js | 84 ++++++++++++--------------- 5 files changed, 121 insertions(+), 137 deletions(-) diff --git a/__tests__/api.test.js b/__tests__/api.test.js index 79d1259..fdb1e04 100644 --- a/__tests__/api.test.js +++ b/__tests__/api.test.js @@ -1,4 +1,4 @@ -import { fetchFromApi } from '../lib/api'; +import fetchFromApi from '../lib/api'; describe('fetchFromApi', () => { beforeEach(() => { @@ -8,66 +8,66 @@ describe('fetchFromApi', () => { it('should resolve fetch and return data with error', async () => { fetch.mockResolvedValue({ status: 400, - message: 'The name is empty' + message: 'The name is empty', }); const requestData = { method: 'POST', - body: { name: '' } + body: { name: '' }, }; try { await fetchFromApi('http://localhost:3000/products', requestData); } catch (e) { expect(e).toEqual({ status: 400, - message: 'The name is empty' + message: 'The name is empty', }); } }); it('should resolve fetch and return data', async () => { fetch.mockResolvedValue({ - data: [{ name: 'Xbox' }] + data: [{ name: 'Xbox' }], }); const response = await fetchFromApi('http://localhost:3000/products'); expect(response).toEqual({ - data: [{ name: 'Xbox' }] + data: [{ name: 'Xbox' }], }); }); it('should define application/json as default headers', async () => { fetch.mockResolvedValue({ - data: [{ name: 'Xbox' }] + data: [{ name: 'Xbox' }], }); - let requestData = {}; + const requestData = {}; - const response = await fetchFromApi('http://localhost:3000/products', requestData); + await fetchFromApi('http://localhost:3000/products', requestData); expect(requestData).toEqual({ headers: { - 'content-type': 'application/json' - } + 'content-type': 'application/json', + }, }); }); it('should not overwrite content type if specified in requestData', async () => { fetch.mockResolvedValue({ - data: [{ name: 'Xbox' }] + data: [{ name: 'Xbox' }], }); - let requestData = { + const requestData = { headers: { - 'content-type': 'application/x-www-form-urlencoded' - } + 'content-type': 'application/x-www-form-urlencoded', + }, }; - const response = await fetchFromApi('http://localhost:3000/products', requestData); + await fetchFromApi('http://localhost:3000/products', requestData); expect(requestData).toEqual({ headers: { - 'content-type': 'application/x-www-form-urlencoded' - } + 'content-type': 'application/x-www-form-urlencoded', + }, }); }); diff --git a/__tests__/middleware.test.js b/__tests__/middleware.test.js index d0fa3a4..f0bfe7b 100644 --- a/__tests__/middleware.test.js +++ b/__tests__/middleware.test.js @@ -1,15 +1,15 @@ - -import { apiMiddleware } from '../lib/middleware'; +import apiMiddleware from '../lib/middleware'; describe('apiMiddleware', () => { - - let dispatch, getState, next; + let dispatch; + let getState; + let next; beforeEach(() => { dispatch = jest.fn(); getState = jest.fn(); next = jest.fn(); - }) + }); it('Should dispatch action success and return the data body - no json', async () => { function get() { @@ -18,17 +18,17 @@ describe('apiMiddleware', () => { const apiCallFunction = jest.fn().mockResolvedValue({ headers: { - get - } + get, + }, }); const action = { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' + failure: 'FAILURE', }, - apiCallFunction + apiCallFunction, }; const response = await apiMiddleware({ dispatch, getState })(next)(action); @@ -38,17 +38,17 @@ describe('apiMiddleware', () => { const expectedResponse = { headers: { - get - } + get, + }, }; expect(dispatch).toBeCalledWith({ extraData: {}, - type: 'REQUEST' + type: 'REQUEST', }); expect(dispatch).toBeCalledWith({ extraData: {}, response: expectedResponse, - type: 'SUCCESS' + type: 'SUCCESS', }); expect(response).toEqual(expectedResponse); }); @@ -59,7 +59,7 @@ describe('apiMiddleware', () => { } const body = { - data: [{ id: 1, name: 'Xbox' }] + data: [{ id: 1, name: 'Xbox' }], }; function json() { @@ -68,18 +68,18 @@ describe('apiMiddleware', () => { const apiCallFunction = jest.fn().mockResolvedValue({ headers: { - get + get, }, - json + json, }); const action = { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' + failure: 'FAILURE', }, - apiCallFunction + apiCallFunction, }; const response = await apiMiddleware({ dispatch, getState })(next)(action); @@ -90,18 +90,18 @@ describe('apiMiddleware', () => { const expectedResponse = { data: body, headers: { - get + get, }, - json + json, }; expect(dispatch).toBeCalledWith({ extraData: {}, - type: 'REQUEST' + type: 'REQUEST', }); expect(dispatch).toBeCalledWith({ extraData: {}, response: expectedResponse, - type: 'SUCCESS' + type: 'SUCCESS', }); expect(response).toEqual(expectedResponse); }); @@ -113,7 +113,7 @@ describe('apiMiddleware', () => { const body = { status: 500, - message: 'Internal Error' + message: 'Internal Error', }; function json() { @@ -122,25 +122,25 @@ describe('apiMiddleware', () => { const apiCallFunction = jest.fn().mockRejectedValue({ headers: { - get + get, }, - json + json, }); const action = { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' + failure: 'FAILURE', }, - apiCallFunction + apiCallFunction, }; const expectedResponse = { headers: { - get + get, }, - json + json, }; try { @@ -153,13 +153,13 @@ describe('apiMiddleware', () => { expect(getState).toBeCalled(); expect(dispatch).toBeCalledWith({ extraData: {}, - type: 'REQUEST' + type: 'REQUEST', }); expect(dispatch).toBeCalledWith({ extraData: {}, response: expectedResponse, error: expectedResponse, - type: 'FAILURE' + type: 'FAILURE', }); }); @@ -170,7 +170,7 @@ describe('apiMiddleware', () => { const body = { status: 500, - message: 'Internal Error' + message: 'Internal Error', }; function json() { @@ -179,26 +179,26 @@ describe('apiMiddleware', () => { const apiCallFunction = jest.fn().mockRejectedValue({ headers: { - get + get, }, - json + json, }); const action = { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' + failure: 'FAILURE', }, - apiCallFunction + apiCallFunction, }; const expectedResponse = { data: body, headers: { - get + get, }, - json + json, }; try { @@ -211,19 +211,19 @@ describe('apiMiddleware', () => { expect(getState).toBeCalled(); expect(dispatch).toBeCalledWith({ extraData: {}, - type: 'REQUEST' + type: 'REQUEST', }); expect(dispatch).toBeCalledWith({ extraData: {}, response: expectedResponse, error: expectedResponse, - type: 'FAILURE' + type: 'FAILURE', }); }); it("Should catch error when it doesn't pass all types actions", async () => { const action = { - types: {} + types: {}, }; try { @@ -231,15 +231,15 @@ describe('apiMiddleware', () => { } catch (error) { expect(error).toEqual( new Error( - 'Expected action.types to be an object/dict with three keys (request, success and failure), and the values should be strings.' - ) + 'Expected action.types to be an object/dict with three keys (request, success and failure), and the values should be strings.', + ), ); } }); it('Should pass action forwarn if no types are defined', async () => { const action = { - type: 'REQUEST' + type: 'REQUEST', }; apiMiddleware({ dispatch, getState })(next)(action); expect(next).toBeCalledWith(action); @@ -250,16 +250,14 @@ describe('apiMiddleware', () => { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' - } + failure: 'FAILURE', + }, }; try { await apiMiddleware({ dispatch, getState })(next)(action); } catch (error) { - expect(error).toEqual( - new Error('Expected `apiCallFunction` to be a function.') - ); + expect(error).toEqual(new Error('Expected `apiCallFunction` to be a function.')); } }); @@ -269,7 +267,7 @@ describe('apiMiddleware', () => { } const body = { - data: [{ id: 1, name: 'Xbox' }] + data: [{ id: 1, name: 'Xbox' }], }; function json() { @@ -278,19 +276,19 @@ describe('apiMiddleware', () => { const apiCallFunction = jest.fn().mockResolvedValue({ headers: { - get + get, }, - json + json, }); const action = { types: { request: 'REQUEST', success: 'SUCCESS', - failure: 'FAILURE' + failure: 'FAILURE', }, apiCallFunction, - shouldCallApi: () => false + shouldCallApi: () => false, }; apiMiddleware({ dispatch, getState })(next)(action); diff --git a/index.js b/index.js index 5f31030..3d4f346 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import { fetchFromApi } from './lib/api'; -import { apiMiddleware } from './lib/middleware'; +import fetchFromApi from './lib/api'; +import apiMiddleware from './lib/middleware'; export { fetchFromApi, apiMiddleware }; diff --git a/lib/api.js b/lib/api.js index d609803..dbd204e 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,32 +1,28 @@ +function buildRequest(url, requestData) { + const request = new Request(url, { ...requestData }); + return request; +} -export function fetchFromApi(url, requestData={ method: 'GET' }) { - +function fetchFromApi(url, requestData = { method: 'GET' }) { if (!requestData.headers) { requestData.headers = {}; } - if(!requestData.headers['content-type']){ - requestData.headers['content-type'] = 'application/json' + if (!requestData.headers['content-type']) { + requestData.headers['content-type'] = 'application/json'; } return new Promise((resolve, reject) => { - fetch(buildRequest(url, requestData)) .then(response => { - // here, we prepare fetch to reject when the status is 4xx or above - if (response.status >= 400){ - return reject(response) + if (response.status >= 400) { + return reject(response); } return resolve(response); - - }).catch(err => reject(err)); - + }) + .catch(err => reject(err)); }); - } -export function buildRequest(url, requestData) { - const request = new Request(url, { ...requestData }); - return request; -} +export default fetchFromApi; diff --git a/lib/middleware.js b/lib/middleware.js index a49d370..64342b1 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -1,12 +1,25 @@ +const validateAction = action => { + const { types, apiCallFunction } = action; + const expectedTypes = ['request', 'success', 'failure']; + + if ( + Object.values(types).length !== 3 || + !Object.keys(types).every(type => expectedTypes.includes(type)) || + !Object.values(types).every(type => typeof type === 'string') + ) { + throw new Error( + 'Expected action.types to be an object/dict with three keys (request, success and failure), and the values should be strings.', + ); + } -export function apiMiddleware({ dispatch, getState }) { + if (typeof apiCallFunction !== 'function') { + throw new Error('Expected `apiCallFunction` to be a function.'); + } +}; + +function apiMiddleware({ dispatch, getState }) { return next => action => { - const { - types, - apiCallFunction, - shouldCallApi = () => true, - extraData = {} - } = action; + const { types, apiCallFunction, shouldCallApi = () => true, extraData = {} } = action; if (!types) { // if this is a normal use of redux, we just pass on the action @@ -19,7 +32,7 @@ export function apiMiddleware({ dispatch, getState }) { // then, to prevent accidental api calls, // we can check the state before calling if (!shouldCallApi(getState())) { - return new Promise(() => {}); // so we can still call .then when we dispatch + return new Promise(() => {}); // so we can still call .then when we dispatch } // we dispatch the request action, so the interface can react to it @@ -27,58 +40,35 @@ export function apiMiddleware({ dispatch, getState }) { // at last, we return a promise with the proper api call return new Promise((resolve, reject) => { - apiCallFunction(dispatch).then( - response => { + apiCallFunction(dispatch) + .then(response => { // if it's a json response, we unpack and parse it if (response.headers.get('content-type') === 'application/json') { - response.json().then( - data => { - response.data = data; // from backend response - dispatch({ extraData, response, type: types.success }); - resolve(response); - } - ); + response.json().then(data => { + response.data = data; // from backend response + dispatch({ extraData, response, type: types.success }); + resolve(response); + }); } else { dispatch({ extraData, response, type: types.success }); resolve(response); } - } - ).catch( - error => { + }) + .catch(error => { // if it's a json response, we unpack and parse it if (error.headers && error.headers.get('content-type') === 'application/json') { - error.json().then( - data => { - error.data = data; // form backend error - dispatch({ extraData, error, response: error, type: types.failure }); - reject(error); - } - ) + error.json().then(data => { + error.data = data; // form backend error + dispatch({ extraData, error, response: error, type: types.failure }); + reject(error); + }); } else { dispatch({ extraData, error, response: error, type: types.failure }); reject(error); } - } - ); + }); }); }; } - -const validateAction = (action) => { - const { types, apiCallFunction } = action; - const expectedTypes = ['request', 'success', 'failure']; - - if ( - Object.values(types).length !== 3 - || !Object.keys(types).every(type => expectedTypes.includes(type)) - || !Object.values(types).every(type => typeof type === 'string') - ) { - throw new Error('Expected action.types to be an object/dict with three keys (request, success and failure), and the values should be strings.'); - } - - if (typeof apiCallFunction !== 'function') { - throw new Error('Expected `apiCallFunction` to be a function.'); - } - -} +export default apiMiddleware;