From 3bd318e212db7d1ba8d6abbe72c4f34ca1832f5b Mon Sep 17 00:00:00 2001 From: AllenFang Date: Tue, 13 Jun 2017 21:28:19 +0800 Subject: [PATCH 01/15] module resolve for jest --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8dc29e6..e5eef15 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,10 @@ ], "moduleNameMapper": { "^react-native$": "react-native-web" - } + }, + "modulePaths": [ + "src-react" + ] }, "babel": { "presets": [ From 0106e916800629fcdca2090c7fd6745556536287 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Tue, 13 Jun 2017 21:28:37 +0800 Subject: [PATCH 02/15] add test cases for ungit-config and path reducers --- src-react/__tests__/reducers/path.test.js | 73 +++++++++++++++++++ .../__tests__/reducers/ungit-config.test.js | 32 ++++++++ 2 files changed, 105 insertions(+) create mode 100644 src-react/__tests__/reducers/path.test.js create mode 100644 src-react/__tests__/reducers/ungit-config.test.js diff --git a/src-react/__tests__/reducers/path.test.js b/src-react/__tests__/reducers/path.test.js new file mode 100644 index 0000000..dad2505 --- /dev/null +++ b/src-react/__tests__/reducers/path.test.js @@ -0,0 +1,73 @@ +import * as types from 'constants/action-types'; + +import path from 'reducers/path'; + +describe('path.js reducers', () => { + let initialState; + const DECREASE_PENDING_ACTIONS = [ + types.RECEIVE_UNGIT_CONFIG, + types.RECEIVE_USER_CONFIG, + types.RECEIVE_GIT_VERSION, + types.RECEIVE_LATEST_VERSION + ]; + + beforeEach(() => { + initialState = { + pending: null, + errMessage: [] + }; + }); + + it('should return original state if action doesn\'t match any case' , function() { + const state = path(initialState, { type: 'no-op' }); + expect(state.pending).toEqual(initialState.pending); + expect(state.errMessage.length).toEqual(initialState.errMessage.length); + }); + + describe('when PATH_PAGE_PENDING action dispatch', () => { + it('pending state should be calculated correctly', function() { + const action1 = { type: types.PATH_PAGE_PENDING, payload: 1 }; + const action2 = { type: types.PATH_PAGE_PENDING, payload: 3 }; + + const state1 = path(initialState, action1); + expect(state1.pending).toEqual(initialState.pending + action1.payload); + + const state2 = path(state1, action2); + expect(state2.pending).toEqual(state1.pending + action2.payload); + }); + }); + + DECREASE_PENDING_ACTIONS.forEach(actionName => { + describe(`when ${actionName} action dispatch`, () => { + beforeEach(() => { + initialState = { + pending: 1, + errMessage: [] + }; + }); + it('pending state should be minused one', () => { + const action = { type: actionName }; + + const state = path(initialState, action); + expect(state.pending).toEqual(initialState.pending - 1); + }); + }); + }); + + describe('when PATH_PAGE_API_ERR action dispatch', () => { + it('pending state should be minused one', function() { + const action = { type: types.PATH_PAGE_API_ERR, payload: 'error message' }; + + const state = path(initialState, action); + expect(state.pending).toEqual(initialState.pending - 1); + }); + + it('errMessage state should correct content', function() { + const action = { type: types.PATH_PAGE_API_ERR, payload: 'error message' }; + + const state = path(initialState, action); + expect(state.errMessage.length).toEqual(initialState.errMessage.length + 1); + expect(state.errMessage[0]).toEqual(action.payload); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/reducers/ungit-config.test.js b/src-react/__tests__/reducers/ungit-config.test.js new file mode 100644 index 0000000..1e6eb74 --- /dev/null +++ b/src-react/__tests__/reducers/ungit-config.test.js @@ -0,0 +1,32 @@ +import * as types from 'constants/action-types'; + +import ungitConfig from 'reducers/ungit-config'; + +describe('ungit-config.js reducers', () => { + let initialState; + + it('should return original state if action doesn\'t match any case' , function() { + const state = ungitConfig(initialState, { type: 'no-op' }); + expect(state).toEqual({}); + }); + + describe('when RECEIVE_UNGIT_CONFIG action dispatch', () => { + it('state should have ungit configuration correctly', function() { + // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful + const action = { type: types.RECEIVE_UNGIT_CONFIG, payload: { + config: { + allowCheckoutNodes: false, + autoStashAndPop: true + }, + platform: 'darwin', + userHash: '69d23970c3b39b2a8d68a70c30cab99a2bf46e74', + version: 'dev-1.1.16-071f30a', + pluginApiVersion: '0.2.0' + } }; + + const state = ungitConfig(initialState, action); + expect(JSON.stringify(state)).toEqual(JSON.stringify(action.payload)); + }); + }); + +}); \ No newline at end of file From bfb5ebcb48fcdcef54b653a736af1b47f11e3368 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 17 Jun 2017 23:26:15 +0800 Subject: [PATCH 03/15] fix wrong function name --- src-react/reducers/versions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-react/reducers/versions.js b/src-react/reducers/versions.js index 9ddae17..4de5f54 100644 --- a/src-react/reducers/versions.js +++ b/src-react/reducers/versions.js @@ -1,6 +1,6 @@ import * as types from 'constants/action-types'; -function userConfig(state, action) { +function versions(state, action) { switch(action.type) { case types.RECEIVE_GIT_VERSION: const { payload: gitVersion } = action; @@ -13,4 +13,4 @@ function userConfig(state, action) { } } -export default userConfig; \ No newline at end of file +export default versions; \ No newline at end of file From a9688ac3de07d1b72a2448aa25ab431fa9f2ff75 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 17 Jun 2017 23:26:43 +0800 Subject: [PATCH 04/15] add clicktests to avoid jest to run tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5eef15..426e122 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "/config/polyfills.js" ], "testPathIgnorePatterns": [ - "[/\\\\](build|docs|node_modules|scripts)[/\\\\]" + "[/\\\\](build|docs|node_modules|scripts|clicktests)[/\\\\]" ], "testEnvironment": "node", "testURL": "http://localhost", From 12473f3a1ce3939f8277d7bbf1cff0493bb0a885 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 18 Jun 2017 15:25:33 +0800 Subject: [PATCH 05/15] add sinon and create a fake localStorage for jest --- config/localStorageMock.js | 23 +++++++++++++++++++++++ package.json | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 config/localStorageMock.js diff --git a/config/localStorageMock.js b/config/localStorageMock.js new file mode 100644 index 0000000..8b0b8d4 --- /dev/null +++ b/config/localStorageMock.js @@ -0,0 +1,23 @@ +class LocalStorageMock { + constructor() { + this.store = {}; + } + + clear() { + this.store = {}; + } + + getItem(key) { + return this.store[key]; + } + + setItem(key, value) { + this.store[key] = value.toString(); + } + + removeItem(key) { + delete this.store[key]; + } +}; + +global.localStorage = new LocalStorageMock; diff --git a/package.json b/package.json index 426e122..c8b765d 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "promise": "7.1.1", "react-dev-utils": "^0.5.2", "sass-loader": "^6.0.5", + "sinon": "^2.3.4", "style-loader": "0.13.1", "supertest": "~3.0.0", "url-loader": "0.5.7", @@ -145,7 +146,8 @@ "src/**/*.{js,jsx}" ], "setupFiles": [ - "/config/polyfills.js" + "/config/polyfills.js", + "/config/localStorageMock.js" ], "testPathIgnorePatterns": [ "[/\\\\](build|docs|node_modules|scripts|clicktests)[/\\\\]" From 675f63d382c67dc90f0107f025f524ac506a9da6 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 18 Jun 2017 15:29:27 +0800 Subject: [PATCH 06/15] force to compare the bool value from localStorage, it's make sense and avoid a bug if setItem with false explicit --- src-react/reducers/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src-react/reducers/app.js b/src-react/reducers/app.js index 52c889e..8118eaf 100644 --- a/src-react/reducers/app.js +++ b/src-react/reducers/app.js @@ -5,7 +5,7 @@ function app(state, action, config) { switch(action.type) { case types.RECEIVE_UNGIT_CONFIG: { const { ungitConfig } = config; - const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed'); + const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed') === 'true'; const showBugtrackingNagscreen = !ungitConfig.config.bugtracking && !bugtrackingNagscreenDismissed; return { ...state, showBugtrackingNagscreen }; @@ -14,9 +14,9 @@ function app(state, action, config) { case types.RECEIVE_GIT_VERSION: { const { ungitConfig, versions: { gitVersion } } = config; const gitVersionCheckOverride = ungitConfig.config && ungitConfig.config.gitVersionCheckOverride; - const gitVersionErrorDismissed = localStorage.getItem('gitVersionErrorDismissed'); + const gitVersionErrorDismissed = localStorage.getItem('gitVersionErrorDismissed') === 'true'; const gitVersionError = gitVersion && !gitVersion.satisfied && gitVersion.error; - const gitVersionErrorVisible = !gitVersionCheckOverride && gitVersionError && gitVersionErrorDismissed; + const gitVersionErrorVisible = !gitVersionCheckOverride && gitVersionError && !gitVersionErrorDismissed; return { ...state, gitVersionErrorVisible }; } @@ -32,7 +32,7 @@ function app(state, action, config) { case types.RECEIVE_USER_CONFIG: { const { userConfig } = config; - const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed'); + const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed') === 'true'; const showBugtrackingNagscreen = !userConfig.bugtracking && !bugtrackingNagscreenDismissed; return { ...state, showBugtrackingNagscreen }; From c3c9f16dfbfac91351bbd69751c716077df9bd1a Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 18 Jun 2017 15:31:06 +0800 Subject: [PATCH 07/15] complete reducers test, app, ungitConfig, versions --- src-react/__tests__/reducers/app.js | 231 ++++++++++++++++++ .../__tests__/reducers/user-config.test.js | 26 ++ src-react/__tests__/reducers/versions.test.js | 44 ++++ 3 files changed, 301 insertions(+) create mode 100644 src-react/__tests__/reducers/app.js create mode 100644 src-react/__tests__/reducers/user-config.test.js create mode 100644 src-react/__tests__/reducers/versions.test.js diff --git a/src-react/__tests__/reducers/app.js b/src-react/__tests__/reducers/app.js new file mode 100644 index 0000000..4e10af7 --- /dev/null +++ b/src-react/__tests__/reducers/app.js @@ -0,0 +1,231 @@ +import sinon from 'sinon'; +import * as types from 'constants/action-types'; + +import app from 'reducers/app'; + +describe('app.js reducers', () => { + let initialState; + + beforeEach(() => { + initialState = { + gitVersionErrorVisible: false, + showNewVersionAvailable: false, + showBugtrackingNagscreen: false, + showNPSSurvey: false + }; + }); + + describe('when action doesn\'t match any case' , () => { + + it('should return original state except the showNPSSurvey state', () => { + const state = app(initialState, { type: 'no-op' }); + expect(state.gitVersionErrorVisible).toEqual(initialState.gitVersionErrorVisible); + expect(state.showNewVersionAvailable).toEqual(initialState.showNewVersionAvailable); + expect(state.showBugtrackingNagscreen).toEqual(initialState.showBugtrackingNagscreen); + }); + + describe('but last NPS dismissed >= 6 month and Math.random < 0.01', () => { + beforeEach(() => { + localStorage.setItem('NPSSurveyLastDismissed', 1466227885); + sinon.stub(Math, 'random').returns(0.006); + }); + + it('showNPSSurvey state will be true', () => { + const state = app(initialState, { type: 'no-op' }); + expect(state.showNPSSurvey).toEqual(true); + }); + + afterEach(() => { + Math.random.restore(); + }); + }); + + describe('but last NPS dismissed < 6 month', () => { + beforeEach(() => { + localStorage.setItem('NPSSurveyLastDismissed', Date.now()); + sinon.stub(Math, 'random').returns(0.1); + }); + + it('showNPSSurvey state will be false', () => { + const state = app(initialState, { type: 'no-op' }); + expect(state.showNPSSurvey).toEqual(false); + }); + + afterEach(() => { + Math.random.restore(); + }); + }); + + describe('but Math.random >= 0.01', () => { + beforeEach(() => { + localStorage.setItem('NPSSurveyLastDismissed', 1466227885); + sinon.stub(Math, 'random').returns(0.1); + }); + + it('showNPSSurvey state will be false', () => { + const state = app(initialState, { type: 'no-op' }); + expect(state.showNPSSurvey).toEqual(false); + }); + + afterEach(() => { + Math.random.restore(); + }); + }); + }); + + describe('when RECEIVE_UNGIT_CONFIG action dispatch', () => { + describe('if ungitConfig.config.bugtracking is false and bugtrackingNagscreenDismissed is false', () => { + it('showBugtrackingNagscreen state will be true', () => { + const ungitConfig = { config: { bugtracking: false } }; + const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(true); + }); + }); + + describe('if ungitConfig.config.bugtracking is true', () => { + it('showBugtrackingNagscreen state will be false', () => { + const ungitConfig = { config: { bugtracking: true } }; + const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(false); + }); + }); + + describe('if bugtrackingNagscreenDismissed is true', () => { + it('showBugtrackingNagscreen state will be false', () => { + localStorage.setItem('bugtrackingNagscreenDismissed', true); + const ungitConfig = { config: { bugtracking: false } }; + const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(false); + }); + }); + }); + + describe('when RECEIVE_GIT_VERSION action dispatch', () => { + describe('if ungitConfig.config.gitVersionCheckOverride is false and gitVersionError is defined && gitVersionErrorDismissed is false', () => { + it('gitVersionErrorVisible state will be true', () => { + localStorage.setItem('gitVersionErrorDismissed', false); + const ungitConfig = { config: { gitVersionCheckOverride: false } }; + const gitVersion = { + satisfied: false, + error: 'Failed to parse git version number.' + }; + const config = { ungitConfig, versions: { gitVersion } }; + + const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + expect(state.gitVersionErrorVisible).toEqual(true); + }); + }); + + describe('if ungitConfig.config.gitVersionCheckOverride is true', () => { + it('gitVersionErrorVisible state will be false', () => { + localStorage.setItem('gitVersionErrorDismissed', false); + const ungitConfig = { config: { gitVersionCheckOverride: true } }; + const gitVersion = { + satisfied: false, + error: 'Failed to parse git version number.' + }; + const config = { ungitConfig, versions: { gitVersion } }; + + const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + expect(state.gitVersionErrorVisible).toEqual(false); + }); + }); + + describe('if gitVersionError is not defined', () => { + it('gitVersionErrorVisible state will be false', () => { + localStorage.setItem('gitVersionErrorDismissed', false); + const ungitConfig = { config: { gitVersionCheckOverride: false } }; + const gitVersion = { satisfied: true }; + const config = { ungitConfig, versions: { gitVersion } }; + + const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + expect(state.gitVersionErrorVisible).toEqual(false); + }); + }); + + describe('if gitVersionErrorDismissed is true', () => { + it('gitVersionErrorVisible state will be false', () => { + localStorage.setItem('gitVersionErrorDismissed', true); + const ungitConfig = { config: { gitVersionCheckOverride: false } }; + const gitVersion = { + satisfied: false, + error: 'Failed to parse git version number.' + }; + const config = { ungitConfig, versions: { gitVersion } }; + + const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + expect(state.gitVersionErrorVisible).toEqual(false); + }); + }); + }); + + describe('when RECEIVE_LATEST_VERSION action dispatch', () => { + describe('gitVersionCheckOverride is false and latestVersion.outdated is true', () => { + it('showNewVersionAvailable state will be true', () => { + const ungitConfig = { config: { gitVersionCheckOverride: false } }; + const latestVersion = { outdated: true }; + const config = { ungitConfig, versions: { latestVersion } }; + + const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + expect(state.showNewVersionAvailable).toEqual(true); + }); + }); + + describe('gitVersionCheckOverride is true', () => { + it('showNewVersionAvailable state will be false', () => { + const ungitConfig = { config: { gitVersionCheckOverride: true } }; + const latestVersion = { outdated: true }; + const config = { ungitConfig, versions: { latestVersion } }; + + const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + expect(state.showNewVersionAvailable).toEqual(false); + }); + }); + + describe('latestVersion.outdated is true', () => { + it('showNewVersionAvailable state will be false', () => { + const ungitConfig = { config: { gitVersionCheckOverride: false } }; + const latestVersion = { outdated: false }; + const config = { ungitConfig, versions: { latestVersion } }; + + const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + expect(state.showNewVersionAvailable).toEqual(false); + }); + }); + }); + + describe('when RECEIVE_USER_CONFIG action dispatch', () => { + describe('userConfig.bugtracking = false and bugtrackingNagscreenDismissed = false', () => { + it('showBugtrackingNagscreen state will be true', () => { + const userConfig = { bugtracking: false }; + const ungitConfig = { config: { bugtracking: true } }; + localStorage.setItem('bugtrackingNagscreenDismissed', false); + + const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(true); + }); + }); + + describe('userConfig.bugtracking = true', () => { + it('showBugtrackingNagscreen state will be false', () => { + const userConfig = { bugtracking: true }; + const ungitConfig = { config: { bugtracking: true } }; + localStorage.setItem('bugtrackingNagscreenDismissed', false); + + const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(false); + }); + }); + + describe('bugtrackingNagscreenDismissed = true', () => { + it('showBugtrackingNagscreen state will be false', () => { + const userConfig = { bugtracking: false }; + const ungitConfig = { config: { bugtracking: true } }; + localStorage.setItem('bugtrackingNagscreenDismissed', true); + + const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + expect(state.showBugtrackingNagscreen).toEqual(false); + }); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/reducers/user-config.test.js b/src-react/__tests__/reducers/user-config.test.js new file mode 100644 index 0000000..a7b2f32 --- /dev/null +++ b/src-react/__tests__/reducers/user-config.test.js @@ -0,0 +1,26 @@ +import * as types from 'constants/action-types'; + +import userConfig from 'reducers/user-config'; + +describe('user-config.js reducers', () => { + let initialState; + + it('should return original state if action doesn\'t match any case' , () => { + const state = userConfig(initialState, { type: 'no-op' }); + expect(state).toEqual({}); + }); + + describe('when RECEIVE_USER_CONFIG action dispatch', () => { + it('state should have user configuration correctly', () => { + // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful + const action = { type: types.RECEIVE_USER_CONFIG, payload: { + port: 8080, + bugtracking: true + } }; + + const state = userConfig(initialState, action); + expect(JSON.stringify(state)).toEqual(JSON.stringify(action.payload)); + }); + }); + +}); \ No newline at end of file diff --git a/src-react/__tests__/reducers/versions.test.js b/src-react/__tests__/reducers/versions.test.js new file mode 100644 index 0000000..b8f851d --- /dev/null +++ b/src-react/__tests__/reducers/versions.test.js @@ -0,0 +1,44 @@ +import * as types from 'constants/action-types'; + +import versions from 'reducers/versions'; + +describe('versions.js reducers', () => { + const initialState = { + gitVersion: null, + latestVersion: null + }; + + it('should return original state if action doesn\'t match any case' , () => { + const state = versions(initialState, { type: 'no-op' }); + expect(state).toEqual(initialState); + }); + + describe('when RECEIVE_GIT_VERSION action dispatch', () => { + it('state should have git version configuration correctly', () => { + // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful + const action = { type: types.RECEIVE_GIT_VERSION, payload: { + requiredVersion: '>=1.8.x', + satisfied: true, + version: '2.3.2' + } }; + + const state = versions(initialState, action); + expect(JSON.stringify(state.gitVersion)).toEqual(JSON.stringify(action.payload)); + }); + }); + + describe('when RECEIVE_LATEST_VERSION action dispatch', () => { + it('state should have lastest configuration correctly', () => { + // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful + const action = { type: types.RECEIVE_LATEST_VERSION, payload: { + currentVersion: 'dev-1.1.16-071f30a', + latestVersion: '1.1.19', + outdated: false + } }; + + const state = versions(initialState, action); + expect(JSON.stringify(state.latestVersion)).toEqual(JSON.stringify(action.payload)); + }); + }); + +}); \ No newline at end of file From 86d55e9a1c7078417c28cf5fdca5f561dac1f969 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 18 Jun 2017 18:34:18 +0800 Subject: [PATCH 08/15] use redux-fetch-middleware and configure middleware --- package.json | 1 + src-react/store.js | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c8b765d..a246e7d 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "react-dom": "^15.5.4", "react-redux": "^5.0.4", "redux": "^3.6.0", + "redux-fetch-middleware": "^3.0.2", "redux-thunk": "^2.2.0", "rimraf": "~2.6.1", "semver": "~5.3.0", diff --git a/src-react/store.js b/src-react/store.js index 88a8839..6df607c 100644 --- a/src-react/store.js +++ b/src-react/store.js @@ -1,7 +1,21 @@ import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; +import fetchMiddlewareCreator from 'redux-fetch-middleware'; import ungitApp from './reducers'; +const fetchMiddleware = fetchMiddlewareCreator({ + suffix: ['REQUEST', 'SUCCESS', 'FAILURE'], + debug: process.env.NODE_ENV === 'development', + fetchOptions: { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + } +}); + +const middlewares = [thunk, fetchMiddleware]; + const initialState = { config: { ungitConfig: null, @@ -23,6 +37,6 @@ const initialState = { } }; -const store = createStore(ungitApp, initialState, applyMiddleware(thunk)); +const store = createStore(ungitApp, initialState, applyMiddleware(...middlewares)); export default store; \ No newline at end of file From 0844659dcc8fc172d5f26d5756e9690ee458bc30 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 18 Jun 2017 18:35:54 +0800 Subject: [PATCH 09/15] instead of using redux-thunk, use fetch in middleware by redux-fetch-middleware --- src-react/actions/bootstrap.js | 13 ++++---- src-react/actions/common.js | 16 ---------- src-react/actions/ungit-config.js | 36 +++++++++------------- src-react/actions/user-config.js | 24 +++------------ src-react/actions/version.js | 47 +++++++---------------------- src-react/constants/action-types.js | 26 ++++++++++++---- src-react/reducers/app.js | 8 ++--- src-react/reducers/path.js | 24 +++++++++------ src-react/reducers/ungit-config.js | 4 +-- src-react/reducers/user-config.js | 4 +-- src-react/reducers/versions.js | 8 ++--- 11 files changed, 83 insertions(+), 127 deletions(-) delete mode 100644 src-react/actions/common.js diff --git a/src-react/actions/bootstrap.js b/src-react/actions/bootstrap.js index 582d442..e00fa2d 100644 --- a/src-react/actions/bootstrap.js +++ b/src-react/actions/bootstrap.js @@ -1,12 +1,11 @@ +import * as types from 'constants/action-types'; import { fetchUngitConfig } from './ungit-config'; +import store from 'store'; import { fetchLatestVersion, fetchGitVersion } from './version'; -import { pending } from './common'; export function bootstrap() { - return dispatch => { - dispatch(pending(3)); - dispatch(fetchUngitConfig()); - dispatch(fetchLatestVersion()); - dispatch(fetchGitVersion()); - }; + store.dispatch(fetchUngitConfig()); + store.dispatch(fetchLatestVersion()); + store.dispatch(fetchGitVersion()); + return { type: types.NO_OP }; } \ No newline at end of file diff --git a/src-react/actions/common.js b/src-react/actions/common.js deleted file mode 100644 index de1a3c2..0000000 --- a/src-react/actions/common.js +++ /dev/null @@ -1,16 +0,0 @@ -/* This export common using actionCreator */ -import * as types from 'constants/action-types'; - -export function pending(count) { - return { - type: types.PATH_PAGE_PENDING, - payload: count || 1 - }; -}; - -export function apiError(message) { - return { - type: types.PATH_PAGE_API_ERR, - payload: message - }; -} \ No newline at end of file diff --git a/src-react/actions/ungit-config.js b/src-react/actions/ungit-config.js index de55431..8f19688 100644 --- a/src-react/actions/ungit-config.js +++ b/src-react/actions/ungit-config.js @@ -1,29 +1,21 @@ import * as types from 'constants/action-types'; import { fetchUserConfig } from './user-config'; -import { apiError, pending } from './common'; +import store from 'store'; export function fetchUngitConfig() { - return dispatch => { - // consider wrap API call in separate modules - // it will be easy to stub module's function when testing - fetch('http://localhost:8448/ungit/config') - .then(response => response.json()) - .then(json => { - if (!json.config.bugtracking) { - dispatch(pending()); - dispatch(fetchUserConfig()); + return { + type: types.FETCH_UNGIT_CONFIG, + meta: {}, + $payload: { + url: 'http://localhost:8448/ungit/config', + onResponse: response => { + if (response.status === 200) { + store.dispatch(fetchUserConfig()); + return; + } else { + return false; } - dispatch(receiveUngitConfig(json)); - }) - .catch(e => { - dispatch(apiError(e.message)); - }); + } + } }; }; - -function receiveUngitConfig(ungitConfig) { - return { - type: types.RECEIVE_UNGIT_CONFIG, - payload: ungitConfig - }; -}; \ No newline at end of file diff --git a/src-react/actions/user-config.js b/src-react/actions/user-config.js index 03f71a6..8914f6d 100644 --- a/src-react/actions/user-config.js +++ b/src-react/actions/user-config.js @@ -1,25 +1,11 @@ import * as types from 'constants/action-types'; -import { apiError } from './common'; export function fetchUserConfig() { - return dispatch => { - // consider wrap API call in separate modules - // it will be easy to stub module's function when testing - fetch('http://localhost:8448/api/userconfig') - .then(response => response.json()) - .then(json => { - dispatch(receiveUserConfig(json)); - }) - .catch(e => { - dispatch(apiError(e.message)); - }); - }; -}; - - -function receiveUserConfig(userConfig) { return { - type: types.RECEIVE_USER_CONFIG, - payload: userConfig + type: types.FETCH_USER_CONFIG, + meta: {}, + $payload: { + url: 'http://localhost:8448/api/userconfig' + } }; }; \ No newline at end of file diff --git a/src-react/actions/version.js b/src-react/actions/version.js index a4c53fb..51127ad 100644 --- a/src-react/actions/version.js +++ b/src-react/actions/version.js @@ -1,46 +1,21 @@ import * as types from 'constants/action-types'; -import { apiError } from './common'; export function fetchLatestVersion() { - return dispatch => { - // consider wrap API call in separate modules - // it will be easy to stub module's function when testing - fetch('http://localhost:8448/api/latestversion') - .then(response => response.json()) - .then(json => { - dispatch(receiveLatestVersion(json)); - }) - .catch(e => { - dispatch(apiError(e.message)); - }); + return { + type: types.FETCH_LATEST_VERSION, + meta: {}, + $payload: { + url: 'http://localhost:8448/api/latestversion' + } }; } export function fetchGitVersion() { - return dispatch => { - // consider wrap API call in separate modules - // it will be easy to stub module's function when testing - fetch('http://localhost:8448/api/gitversion') - .then(response => response.json()) - .then(json => { - dispatch(receiveGitVersion(json)); - }) - .catch(e => { - dispatch(apiError(e.message)); - }); - }; -} - -function receiveGitVersion(gitVersion) { return { - type: types.RECEIVE_GIT_VERSION, - payload: gitVersion + type: types.FETCH_GIT_VERSION, + meta: {}, + $payload: { + url: 'http://localhost:8448/api/gitversion' + } }; } - -function receiveLatestVersion(latestVersion) { - return { - type: types.RECEIVE_LATEST_VERSION, - payload: latestVersion - }; -}; \ No newline at end of file diff --git a/src-react/constants/action-types.js b/src-react/constants/action-types.js index 69ec0e4..d872613 100644 --- a/src-react/constants/action-types.js +++ b/src-react/constants/action-types.js @@ -1,6 +1,20 @@ -export const RECEIVE_GIT_VERSION = 'RECEIVE_GIT_VERSION'; -export const RECEIVE_LATEST_VERSION = 'RECEIVE_LATEST_VERSION'; -export const RECEIVE_USER_CONFIG = 'RECEIVE_USER_CONFIG'; -export const RECEIVE_UNGIT_CONFIG = 'RECEIVE_UNGIT_CONFIG'; -export const PATH_PAGE_PENDING = 'PATH_PAGE_PENDING'; -export const PATH_PAGE_API_ERR = 'PATH_PAGE_API_ERR'; \ No newline at end of file +export const NO_OP = 'no-op'; +export const FETCH_USER_CONFIG = 'FETCH_USER_CONFIG'; +export const FETCH_LATEST_VERSION = 'FETCH_LATEST_VERSION'; +export const FETCH_GIT_VERSION = 'FETCH_GIT_VERSION'; +export const FETCH_UNGIT_CONFIG = 'FETCH_UNGIT_CONFIG'; + +export const FETCH_USER_CONFIG_REQUEST = 'FETCH_USER_CONFIG_REQUEST'; +export const FETCH_LATEST_VERSION_REQUEST = 'FETCH_LATEST_VERSION_REQUEST'; +export const FETCH_GIT_VERSION_REQUEST = 'FETCH_GIT_VERSION_REQUEST'; +export const FETCH_UNGIT_CONFIG_REQUEST = 'FETCH_UNGIT_CONFIG_REQUEST'; + +export const FETCH_USER_CONFIG_FAILURE = 'FETCH_USER_CONFIG_FAILURE'; +export const FETCH_LATEST_VERSION_FAILURE = 'FETCH_LATEST_VERSION_FAILURE'; +export const FETCH_GIT_VERSION_FAILURE = 'FETCH_GIT_VERSION_FAILURE'; +export const FETCH_UNGIT_CONFIG_FAILURE = 'FETCH_UNGIT_CONFIG_FAILURE'; + +export const FETCH_USER_CONFIG_SUCCESS = 'FETCH_USER_CONFIG_SUCCESS'; +export const FETCH_LATEST_VERSION_SUCCESS = 'FETCH_LATEST_VERSION_SUCCESS'; +export const FETCH_GIT_VERSION_SUCCESS = 'FETCH_GIT_VERSION_SUCCESS'; +export const FETCH_UNGIT_CONFIG_SUCCESS = 'FETCH_UNGIT_CONFIG_SUCCESS'; \ No newline at end of file diff --git a/src-react/reducers/app.js b/src-react/reducers/app.js index 8118eaf..18e94be 100644 --- a/src-react/reducers/app.js +++ b/src-react/reducers/app.js @@ -3,7 +3,7 @@ import * as types from 'constants/action-types'; function app(state, action, config) { switch(action.type) { - case types.RECEIVE_UNGIT_CONFIG: { + case types.FETCH_UNGIT_CONFIG_SUCCESS: { const { ungitConfig } = config; const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed') === 'true'; const showBugtrackingNagscreen = !ungitConfig.config.bugtracking && !bugtrackingNagscreenDismissed; @@ -11,7 +11,7 @@ function app(state, action, config) { return { ...state, showBugtrackingNagscreen }; } - case types.RECEIVE_GIT_VERSION: { + case types.FETCH_GIT_VERSION_SUCCESS: { const { ungitConfig, versions: { gitVersion } } = config; const gitVersionCheckOverride = ungitConfig.config && ungitConfig.config.gitVersionCheckOverride; const gitVersionErrorDismissed = localStorage.getItem('gitVersionErrorDismissed') === 'true'; @@ -21,7 +21,7 @@ function app(state, action, config) { return { ...state, gitVersionErrorVisible }; } - case types.RECEIVE_LATEST_VERSION: { + case types.FETCH_LATEST_VERSION_SUCCESS: { const { ungitConfig, versions: { latestVersion } } = config; const gitVersionCheckOverride = ungitConfig.config && ungitConfig.config.gitVersionCheckOverride; const outdated = latestVersion && latestVersion.outdated; @@ -30,7 +30,7 @@ function app(state, action, config) { return { ...state, showNewVersionAvailable }; } - case types.RECEIVE_USER_CONFIG: { + case types.FETCH_USER_CONFIG_SUCCESS: { const { userConfig } = config; const bugtrackingNagscreenDismissed = localStorage.getItem('bugtrackingNagscreenDismissed') === 'true'; const showBugtrackingNagscreen = !userConfig.bugtracking && !bugtrackingNagscreenDismissed; diff --git a/src-react/reducers/path.js b/src-react/reducers/path.js index f3d3469..4354d06 100644 --- a/src-react/reducers/path.js +++ b/src-react/reducers/path.js @@ -2,16 +2,22 @@ import * as types from 'constants/action-types'; function path(state, action) { switch(action.type) { - case types.PATH_PAGE_PENDING: - return { ...state, pending: state.pending + action.payload }; - case types.RECEIVE_UNGIT_CONFIG: - case types.RECEIVE_USER_CONFIG: - case types.RECEIVE_GIT_VERSION: - case types.RECEIVE_LATEST_VERSION: + case types.FETCH_USER_CONFIG_REQUEST: + case types.FETCH_LATEST_VERSION_REQUEST: + case types.FETCH_GIT_VERSION_REQUEST: + case types.FETCH_UNGIT_CONFIG_REQUEST: + return { ...state, pending: state.pending + 1 }; + case types.FETCH_USER_CONFIG_FAILURE: + case types.FETCH_LATEST_VERSION_FAILURE: + case types.FETCH_GIT_VERSION_FAILURE: + case types.FETCH_UNGIT_CONFIG_FAILURE: + const { err } = action; + return { ...state, pending: state.pending - 1, errMessage: [ ...state.errMessage, err ] }; + case types.FETCH_USER_CONFIG_SUCCESS: + case types.FETCH_LATEST_VERSION_SUCCESS: + case types.FETCH_GIT_VERSION_SUCCESS: + case types.FETCH_UNGIT_CONFIG_SUCCESS: return { ...state, pending: state.pending - 1 }; - case types.PATH_PAGE_API_ERR: - const { payload: errMessage } = action; - return { ...state, pending: state.pending - 1, errMessage: [ ...state.errMessage, errMessage ] }; default: return { ...state }; } diff --git a/src-react/reducers/ungit-config.js b/src-react/reducers/ungit-config.js index 6bbac31..60c662d 100644 --- a/src-react/reducers/ungit-config.js +++ b/src-react/reducers/ungit-config.js @@ -2,8 +2,8 @@ import * as types from 'constants/action-types'; function ungitConfig(state, action) { switch(action.type) { - case types.RECEIVE_UNGIT_CONFIG: - const { payload: ungitConfig } = action; + case types.FETCH_UNGIT_CONFIG_SUCCESS: + const { data: ungitConfig } = action; return { ...ungitConfig }; default: return { ...state }; diff --git a/src-react/reducers/user-config.js b/src-react/reducers/user-config.js index 23f3b65..e253865 100644 --- a/src-react/reducers/user-config.js +++ b/src-react/reducers/user-config.js @@ -2,8 +2,8 @@ import * as types from 'constants/action-types'; function userConfig(state, action) { switch(action.type) { - case types.RECEIVE_USER_CONFIG: - const { payload: userConfig } = action; + case types.FETCH_USER_CONFIG_SUCCESS: + const { data: userConfig } = action; return { ...userConfig }; default: return { ...state }; diff --git a/src-react/reducers/versions.js b/src-react/reducers/versions.js index 4de5f54..9a3e1b0 100644 --- a/src-react/reducers/versions.js +++ b/src-react/reducers/versions.js @@ -2,11 +2,11 @@ import * as types from 'constants/action-types'; function versions(state, action) { switch(action.type) { - case types.RECEIVE_GIT_VERSION: - const { payload: gitVersion } = action; + case types.FETCH_GIT_VERSION_SUCCESS: + const { data: gitVersion } = action; return { ...state, gitVersion }; - case types.RECEIVE_LATEST_VERSION: - const { payload: latestVersion } = action; + case types.FETCH_LATEST_VERSION_SUCCESS: + const { data: latestVersion } = action; return { ...state, latestVersion }; default: return { ...state }; From 0fdda76393214c796af3db5aa23817c0cf566ffb Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 15:52:50 +0800 Subject: [PATCH 10/15] use redux-api-middleware instead --- package.json | 3 +- src-react/__tests__/reducers/app.js | 42 +++++----- src-react/__tests__/reducers/path.test.js | 78 ++++++++++++------- .../__tests__/reducers/ungit-config.test.js | 6 +- .../__tests__/reducers/user-config.test.js | 6 +- src-react/__tests__/reducers/versions.test.js | 10 +-- src-react/actions/ungit-config.js | 27 ++++--- src-react/actions/user-config.js | 13 +++- src-react/actions/version.js | 32 ++++++-- src-react/constants/action-types.js | 4 - src-react/reducers/path.js | 4 +- src-react/reducers/ungit-config.js | 2 +- src-react/reducers/user-config.js | 2 +- src-react/reducers/versions.js | 4 +- src-react/store.js | 18 +---- 15 files changed, 139 insertions(+), 112 deletions(-) diff --git a/package.json b/package.json index a246e7d..926dbc8 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,7 @@ "react-dom": "^15.5.4", "react-redux": "^5.0.4", "redux": "^3.6.0", - "redux-fetch-middleware": "^3.0.2", - "redux-thunk": "^2.2.0", + "redux-api-middleware": "^1.0.3", "rimraf": "~2.6.1", "semver": "~5.3.0", "serve-static": "~1.12.2", diff --git a/src-react/__tests__/reducers/app.js b/src-react/__tests__/reducers/app.js index 4e10af7..6423dfb 100644 --- a/src-react/__tests__/reducers/app.js +++ b/src-react/__tests__/reducers/app.js @@ -18,7 +18,7 @@ describe('app.js reducers', () => { describe('when action doesn\'t match any case' , () => { it('should return original state except the showNPSSurvey state', () => { - const state = app(initialState, { type: 'no-op' }); + const state = app(initialState, { type: types.NO_OP }); expect(state.gitVersionErrorVisible).toEqual(initialState.gitVersionErrorVisible); expect(state.showNewVersionAvailable).toEqual(initialState.showNewVersionAvailable); expect(state.showBugtrackingNagscreen).toEqual(initialState.showBugtrackingNagscreen); @@ -31,7 +31,7 @@ describe('app.js reducers', () => { }); it('showNPSSurvey state will be true', () => { - const state = app(initialState, { type: 'no-op' }); + const state = app(initialState, { type: types.NO_OP }); expect(state.showNPSSurvey).toEqual(true); }); @@ -47,7 +47,7 @@ describe('app.js reducers', () => { }); it('showNPSSurvey state will be false', () => { - const state = app(initialState, { type: 'no-op' }); + const state = app(initialState, { type: types.NO_OP }); expect(state.showNPSSurvey).toEqual(false); }); @@ -63,7 +63,7 @@ describe('app.js reducers', () => { }); it('showNPSSurvey state will be false', () => { - const state = app(initialState, { type: 'no-op' }); + const state = app(initialState, { type: types.NO_OP }); expect(state.showNPSSurvey).toEqual(false); }); @@ -73,11 +73,11 @@ describe('app.js reducers', () => { }); }); - describe('when RECEIVE_UNGIT_CONFIG action dispatch', () => { + describe('when FETCH_UNGIT_CONFIG_SUCCESS action dispatch', () => { describe('if ungitConfig.config.bugtracking is false and bugtrackingNagscreenDismissed is false', () => { it('showBugtrackingNagscreen state will be true', () => { const ungitConfig = { config: { bugtracking: false } }; - const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + const state = app(initialState, { type: types.FETCH_UNGIT_CONFIG_SUCCESS, payload: ungitConfig }, { ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(true); }); }); @@ -85,7 +85,7 @@ describe('app.js reducers', () => { describe('if ungitConfig.config.bugtracking is true', () => { it('showBugtrackingNagscreen state will be false', () => { const ungitConfig = { config: { bugtracking: true } }; - const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + const state = app(initialState, { type: types.FETCH_UNGIT_CONFIG_SUCCESS, payload: ungitConfig }, { ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(false); }); }); @@ -94,13 +94,13 @@ describe('app.js reducers', () => { it('showBugtrackingNagscreen state will be false', () => { localStorage.setItem('bugtrackingNagscreenDismissed', true); const ungitConfig = { config: { bugtracking: false } }; - const state = app(initialState, { type: types.RECEIVE_UNGIT_CONFIG, payload: ungitConfig }, { ungitConfig }); + const state = app(initialState, { type: types.FETCH_UNGIT_CONFIG_SUCCESS, payload: ungitConfig }, { ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(false); }); }); }); - describe('when RECEIVE_GIT_VERSION action dispatch', () => { + describe('when FETCH_GIT_VERSION_SUCCESS action dispatch', () => { describe('if ungitConfig.config.gitVersionCheckOverride is false and gitVersionError is defined && gitVersionErrorDismissed is false', () => { it('gitVersionErrorVisible state will be true', () => { localStorage.setItem('gitVersionErrorDismissed', false); @@ -111,7 +111,7 @@ describe('app.js reducers', () => { }; const config = { ungitConfig, versions: { gitVersion } }; - const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + const state = app(initialState, { type: types.FETCH_GIT_VERSION_SUCCESS, payload: gitVersion }, config); expect(state.gitVersionErrorVisible).toEqual(true); }); }); @@ -126,7 +126,7 @@ describe('app.js reducers', () => { }; const config = { ungitConfig, versions: { gitVersion } }; - const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + const state = app(initialState, { type: types.FETCH_GIT_VERSION_SUCCESS, payload: gitVersion }, config); expect(state.gitVersionErrorVisible).toEqual(false); }); }); @@ -138,7 +138,7 @@ describe('app.js reducers', () => { const gitVersion = { satisfied: true }; const config = { ungitConfig, versions: { gitVersion } }; - const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + const state = app(initialState, { type: types.FETCH_GIT_VERSION_SUCCESS, payload: gitVersion }, config); expect(state.gitVersionErrorVisible).toEqual(false); }); }); @@ -153,20 +153,20 @@ describe('app.js reducers', () => { }; const config = { ungitConfig, versions: { gitVersion } }; - const state = app(initialState, { type: types.RECEIVE_GIT_VERSION, payload: gitVersion }, config); + const state = app(initialState, { type: types.FETCH_GIT_VERSION_SUCCESS, payload: gitVersion }, config); expect(state.gitVersionErrorVisible).toEqual(false); }); }); }); - describe('when RECEIVE_LATEST_VERSION action dispatch', () => { + describe('when FETCH_LATEST_VERSION_SUCCESS action dispatch', () => { describe('gitVersionCheckOverride is false and latestVersion.outdated is true', () => { it('showNewVersionAvailable state will be true', () => { const ungitConfig = { config: { gitVersionCheckOverride: false } }; const latestVersion = { outdated: true }; const config = { ungitConfig, versions: { latestVersion } }; - const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + const state = app(initialState, { type: types.FETCH_LATEST_VERSION_SUCCESS, payload: latestVersion }, config); expect(state.showNewVersionAvailable).toEqual(true); }); }); @@ -177,7 +177,7 @@ describe('app.js reducers', () => { const latestVersion = { outdated: true }; const config = { ungitConfig, versions: { latestVersion } }; - const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + const state = app(initialState, { type: types.FETCH_LATEST_VERSION_SUCCESS, payload: latestVersion }, config); expect(state.showNewVersionAvailable).toEqual(false); }); }); @@ -188,20 +188,20 @@ describe('app.js reducers', () => { const latestVersion = { outdated: false }; const config = { ungitConfig, versions: { latestVersion } }; - const state = app(initialState, { type: types.RECEIVE_LATEST_VERSION, payload: latestVersion }, config); + const state = app(initialState, { type: types.FETCH_LATEST_VERSION_SUCCESS, payload: latestVersion }, config); expect(state.showNewVersionAvailable).toEqual(false); }); }); }); - describe('when RECEIVE_USER_CONFIG action dispatch', () => { + describe('when FETCH_USER_CONFIG_SUCCESS action dispatch', () => { describe('userConfig.bugtracking = false and bugtrackingNagscreenDismissed = false', () => { it('showBugtrackingNagscreen state will be true', () => { const userConfig = { bugtracking: false }; const ungitConfig = { config: { bugtracking: true } }; localStorage.setItem('bugtrackingNagscreenDismissed', false); - const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + const state = app(initialState, { type: types.FETCH_USER_CONFIG_SUCCESS, payload: userConfig }, { userConfig, ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(true); }); }); @@ -212,7 +212,7 @@ describe('app.js reducers', () => { const ungitConfig = { config: { bugtracking: true } }; localStorage.setItem('bugtrackingNagscreenDismissed', false); - const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + const state = app(initialState, { type: types.FETCH_USER_CONFIG_SUCCESS, payload: userConfig }, { userConfig, ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(false); }); }); @@ -223,7 +223,7 @@ describe('app.js reducers', () => { const ungitConfig = { config: { bugtracking: true } }; localStorage.setItem('bugtrackingNagscreenDismissed', true); - const state = app(initialState, { type: types.RECEIVE_USER_CONFIG, payload: userConfig }, { userConfig, ungitConfig }); + const state = app(initialState, { type: types.FETCH_USER_CONFIG_SUCCESS, payload: userConfig }, { userConfig, ungitConfig }); expect(state.showBugtrackingNagscreen).toEqual(false); }); }); diff --git a/src-react/__tests__/reducers/path.test.js b/src-react/__tests__/reducers/path.test.js index dad2505..892857d 100644 --- a/src-react/__tests__/reducers/path.test.js +++ b/src-react/__tests__/reducers/path.test.js @@ -4,11 +4,26 @@ import path from 'reducers/path'; describe('path.js reducers', () => { let initialState; - const DECREASE_PENDING_ACTIONS = [ - types.RECEIVE_UNGIT_CONFIG, - types.RECEIVE_USER_CONFIG, - types.RECEIVE_GIT_VERSION, - types.RECEIVE_LATEST_VERSION + + const REQUEST_ACTIONS = [ + types.FETCH_USER_CONFIG_REQUEST, + types.FETCH_LATEST_VERSION_REQUEST, + types.FETCH_GIT_VERSION_REQUEST, + types.FETCH_UNGIT_CONFIG_REQUEST + ]; + + const SUCCESS_ACTIONS = [ + types.FETCH_USER_CONFIG_SUCCESS, + types.FETCH_LATEST_VERSION_SUCCESS, + types.FETCH_GIT_VERSION_SUCCESS, + types.FETCH_UNGIT_CONFIG_SUCCESS + ]; + + const FAILURE_ACTIONS = [ + types.FETCH_USER_CONFIG_FAILURE, + types.FETCH_LATEST_VERSION_FAILURE, + types.FETCH_GIT_VERSION_FAILURE, + types.FETCH_UNGIT_CONFIG_FAILURE ]; beforeEach(() => { @@ -19,25 +34,29 @@ describe('path.js reducers', () => { }); it('should return original state if action doesn\'t match any case' , function() { - const state = path(initialState, { type: 'no-op' }); + const state = path(initialState, { type: types.NO_OP }); expect(state.pending).toEqual(initialState.pending); expect(state.errMessage.length).toEqual(initialState.errMessage.length); }); - describe('when PATH_PAGE_PENDING action dispatch', () => { - it('pending state should be calculated correctly', function() { - const action1 = { type: types.PATH_PAGE_PENDING, payload: 1 }; - const action2 = { type: types.PATH_PAGE_PENDING, payload: 3 }; - - const state1 = path(initialState, action1); - expect(state1.pending).toEqual(initialState.pending + action1.payload); + REQUEST_ACTIONS.forEach(actionName => { + describe(`when ${actionName} action dispatch`, () => { + beforeEach(() => { + initialState = { + pending: 1, + errMessage: [] + }; + }); + it('pending state should be plus one', () => { + const action = { type: actionName }; - const state2 = path(state1, action2); - expect(state2.pending).toEqual(state1.pending + action2.payload); + const state = path(initialState, action); + expect(state.pending).toEqual(initialState.pending + 1); + }); }); }); - DECREASE_PENDING_ACTIONS.forEach(actionName => { + SUCCESS_ACTIONS.forEach(actionName => { describe(`when ${actionName} action dispatch`, () => { beforeEach(() => { initialState = { @@ -53,21 +72,22 @@ describe('path.js reducers', () => { }); }); }); - - describe('when PATH_PAGE_API_ERR action dispatch', () => { - it('pending state should be minused one', function() { - const action = { type: types.PATH_PAGE_API_ERR, payload: 'error message' }; - - const state = path(initialState, action); - expect(state.pending).toEqual(initialState.pending - 1); - }); - it('errMessage state should correct content', function() { - const action = { type: types.PATH_PAGE_API_ERR, payload: 'error message' }; + FAILURE_ACTIONS.forEach(actionName => { + describe(`when ${actionName} action dispatch`, () => { + beforeEach(() => { + initialState = { + pending: 1, + errMessage: [] + }; + }); + it('pending state should be minused one', () => { + const action = { type: actionName, payload: { message: 'error' } }; - const state = path(initialState, action); - expect(state.errMessage.length).toEqual(initialState.errMessage.length + 1); - expect(state.errMessage[0]).toEqual(action.payload); + const state = path(initialState, action); + expect(state.pending).toEqual(initialState.pending - 1); + expect(state.errMessage[0]).toEqual(action.payload.message); + }); }); }); }); \ No newline at end of file diff --git a/src-react/__tests__/reducers/ungit-config.test.js b/src-react/__tests__/reducers/ungit-config.test.js index 1e6eb74..f9486c6 100644 --- a/src-react/__tests__/reducers/ungit-config.test.js +++ b/src-react/__tests__/reducers/ungit-config.test.js @@ -6,14 +6,14 @@ describe('ungit-config.js reducers', () => { let initialState; it('should return original state if action doesn\'t match any case' , function() { - const state = ungitConfig(initialState, { type: 'no-op' }); + const state = ungitConfig(initialState, { type: types.NO_OP }); expect(state).toEqual({}); }); - describe('when RECEIVE_UNGIT_CONFIG action dispatch', () => { + describe('when FETCH_UNGIT_CONFIG_SUCCESS action dispatch', () => { it('state should have ungit configuration correctly', function() { // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful - const action = { type: types.RECEIVE_UNGIT_CONFIG, payload: { + const action = { type: types.FETCH_UNGIT_CONFIG_SUCCESS, payload: { config: { allowCheckoutNodes: false, autoStashAndPop: true diff --git a/src-react/__tests__/reducers/user-config.test.js b/src-react/__tests__/reducers/user-config.test.js index a7b2f32..ca16608 100644 --- a/src-react/__tests__/reducers/user-config.test.js +++ b/src-react/__tests__/reducers/user-config.test.js @@ -6,14 +6,14 @@ describe('user-config.js reducers', () => { let initialState; it('should return original state if action doesn\'t match any case' , () => { - const state = userConfig(initialState, { type: 'no-op' }); + const state = userConfig(initialState, { type: types.NO_OP }); expect(state).toEqual({}); }); - describe('when RECEIVE_USER_CONFIG action dispatch', () => { + describe('when FETCH_USER_CONFIG_SUCCESS action dispatch', () => { it('state should have user configuration correctly', () => { // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful - const action = { type: types.RECEIVE_USER_CONFIG, payload: { + const action = { type: types.FETCH_USER_CONFIG_SUCCESS, payload: { port: 8080, bugtracking: true } }; diff --git a/src-react/__tests__/reducers/versions.test.js b/src-react/__tests__/reducers/versions.test.js index b8f851d..2697620 100644 --- a/src-react/__tests__/reducers/versions.test.js +++ b/src-react/__tests__/reducers/versions.test.js @@ -9,14 +9,14 @@ describe('versions.js reducers', () => { }; it('should return original state if action doesn\'t match any case' , () => { - const state = versions(initialState, { type: 'no-op' }); + const state = versions(initialState, { type: types.NO_OP }); expect(state).toEqual(initialState); }); - describe('when RECEIVE_GIT_VERSION action dispatch', () => { + describe('when FETCH_GIT_VERSION_SUCCESS action dispatch', () => { it('state should have git version configuration correctly', () => { // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful - const action = { type: types.RECEIVE_GIT_VERSION, payload: { + const action = { type: types.FETCH_GIT_VERSION_SUCCESS, payload: { requiredVersion: '>=1.8.x', satisfied: true, version: '2.3.2' @@ -27,10 +27,10 @@ describe('versions.js reducers', () => { }); }); - describe('when RECEIVE_LATEST_VERSION action dispatch', () => { + describe('when FETCH_LATEST_VERSION_SUCCESS action dispatch', () => { it('state should have lastest configuration correctly', () => { // TODO: consider to use sinon to mock data and make mock data reuseable and meaningful - const action = { type: types.RECEIVE_LATEST_VERSION, payload: { + const action = { type: types.FETCH_LATEST_VERSION_SUCCESS, payload: { currentVersion: 'dev-1.1.16-071f30a', latestVersion: '1.1.19', outdated: false diff --git a/src-react/actions/ungit-config.js b/src-react/actions/ungit-config.js index 8f19688..ce346fa 100644 --- a/src-react/actions/ungit-config.js +++ b/src-react/actions/ungit-config.js @@ -1,21 +1,24 @@ import * as types from 'constants/action-types'; +import { CALL_API } from 'redux-api-middleware'; import { fetchUserConfig } from './user-config'; import store from 'store'; export function fetchUngitConfig() { return { - type: types.FETCH_UNGIT_CONFIG, - meta: {}, - $payload: { - url: 'http://localhost:8448/ungit/config', - onResponse: response => { - if (response.status === 200) { - store.dispatch(fetchUserConfig()); - return; - } else { - return false; - } - } + [CALL_API]: { + endpoint: 'http://localhost:8448/ungit/config', + method: 'GET', + types: [ + types.FETCH_UNGIT_CONFIG_REQUEST, + { + type: types.FETCH_UNGIT_CONFIG_SUCCESS, + payload: (action, state, res) => { + store.dispatch(fetchUserConfig()); + return res.json().then(json => json); + } + }, + types.FETCH_UNGIT_CONFIG_FAILURE + ] } }; }; diff --git a/src-react/actions/user-config.js b/src-react/actions/user-config.js index 8914f6d..62d0772 100644 --- a/src-react/actions/user-config.js +++ b/src-react/actions/user-config.js @@ -1,11 +1,16 @@ import * as types from 'constants/action-types'; +import { CALL_API } from 'redux-api-middleware'; export function fetchUserConfig() { return { - type: types.FETCH_USER_CONFIG, - meta: {}, - $payload: { - url: 'http://localhost:8448/api/userconfig' + [CALL_API]: { + endpoint: 'http://localhost:8448/api/userconfig', + method: 'GET', + types: [ + types.FETCH_USER_CONFIG_REQUEST, + types.FETCH_USER_CONFIG_SUCCESS, + types.FETCH_USER_CONFIG_FAILURE + ] } }; }; \ No newline at end of file diff --git a/src-react/actions/version.js b/src-react/actions/version.js index 51127ad..e0a28e1 100644 --- a/src-react/actions/version.js +++ b/src-react/actions/version.js @@ -1,21 +1,37 @@ import * as types from 'constants/action-types'; +import { CALL_API } from 'redux-api-middleware'; export function fetchLatestVersion() { + // return { + // type: types.FETCH_LATEST_VERSION, + // meta: {}, + // $payload: { + // url: 'http://localhost:8448/api/latestversion' + // } + // }; return { - type: types.FETCH_LATEST_VERSION, - meta: {}, - $payload: { - url: 'http://localhost:8448/api/latestversion' + [CALL_API]: { + endpoint: 'http://localhost:8448/api/latestversion', + method: 'GET', + types: [ + types.FETCH_LATEST_VERSION_REQUEST, + types.FETCH_LATEST_VERSION_SUCCESS, + types.FETCH_LATEST_VERSION_FAILURE + ] } }; } export function fetchGitVersion() { return { - type: types.FETCH_GIT_VERSION, - meta: {}, - $payload: { - url: 'http://localhost:8448/api/gitversion' + [CALL_API]: { + endpoint: 'http://localhost:8448/api/gitversion', + method: 'GET', + types: [ + types.FETCH_GIT_VERSION_REQUEST, + types.FETCH_GIT_VERSION_SUCCESS, + types.FETCH_GIT_VERSION_FAILURE + ] } }; } diff --git a/src-react/constants/action-types.js b/src-react/constants/action-types.js index d872613..a7c12c1 100644 --- a/src-react/constants/action-types.js +++ b/src-react/constants/action-types.js @@ -1,8 +1,4 @@ export const NO_OP = 'no-op'; -export const FETCH_USER_CONFIG = 'FETCH_USER_CONFIG'; -export const FETCH_LATEST_VERSION = 'FETCH_LATEST_VERSION'; -export const FETCH_GIT_VERSION = 'FETCH_GIT_VERSION'; -export const FETCH_UNGIT_CONFIG = 'FETCH_UNGIT_CONFIG'; export const FETCH_USER_CONFIG_REQUEST = 'FETCH_USER_CONFIG_REQUEST'; export const FETCH_LATEST_VERSION_REQUEST = 'FETCH_LATEST_VERSION_REQUEST'; diff --git a/src-react/reducers/path.js b/src-react/reducers/path.js index 4354d06..42b312d 100644 --- a/src-react/reducers/path.js +++ b/src-react/reducers/path.js @@ -11,8 +11,8 @@ function path(state, action) { case types.FETCH_LATEST_VERSION_FAILURE: case types.FETCH_GIT_VERSION_FAILURE: case types.FETCH_UNGIT_CONFIG_FAILURE: - const { err } = action; - return { ...state, pending: state.pending - 1, errMessage: [ ...state.errMessage, err ] }; + const { payload: { message } } = action; + return { ...state, pending: state.pending - 1, errMessage: [ ...state.errMessage, message ] }; case types.FETCH_USER_CONFIG_SUCCESS: case types.FETCH_LATEST_VERSION_SUCCESS: case types.FETCH_GIT_VERSION_SUCCESS: diff --git a/src-react/reducers/ungit-config.js b/src-react/reducers/ungit-config.js index 60c662d..2debeca 100644 --- a/src-react/reducers/ungit-config.js +++ b/src-react/reducers/ungit-config.js @@ -3,7 +3,7 @@ import * as types from 'constants/action-types'; function ungitConfig(state, action) { switch(action.type) { case types.FETCH_UNGIT_CONFIG_SUCCESS: - const { data: ungitConfig } = action; + const { payload: ungitConfig } = action; return { ...ungitConfig }; default: return { ...state }; diff --git a/src-react/reducers/user-config.js b/src-react/reducers/user-config.js index e253865..6098d30 100644 --- a/src-react/reducers/user-config.js +++ b/src-react/reducers/user-config.js @@ -3,7 +3,7 @@ import * as types from 'constants/action-types'; function userConfig(state, action) { switch(action.type) { case types.FETCH_USER_CONFIG_SUCCESS: - const { data: userConfig } = action; + const { payload: userConfig } = action; return { ...userConfig }; default: return { ...state }; diff --git a/src-react/reducers/versions.js b/src-react/reducers/versions.js index 9a3e1b0..1c53c7c 100644 --- a/src-react/reducers/versions.js +++ b/src-react/reducers/versions.js @@ -3,10 +3,10 @@ import * as types from 'constants/action-types'; function versions(state, action) { switch(action.type) { case types.FETCH_GIT_VERSION_SUCCESS: - const { data: gitVersion } = action; + const { payload: gitVersion } = action; return { ...state, gitVersion }; case types.FETCH_LATEST_VERSION_SUCCESS: - const { data: latestVersion } = action; + const { payload: latestVersion } = action; return { ...state, latestVersion }; default: return { ...state }; diff --git a/src-react/store.js b/src-react/store.js index 6df607c..0b3ac13 100644 --- a/src-react/store.js +++ b/src-react/store.js @@ -1,20 +1,8 @@ import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import fetchMiddlewareCreator from 'redux-fetch-middleware'; +import { apiMiddleware } from 'redux-api-middleware'; import ungitApp from './reducers'; -const fetchMiddleware = fetchMiddlewareCreator({ - suffix: ['REQUEST', 'SUCCESS', 'FAILURE'], - debug: process.env.NODE_ENV === 'development', - fetchOptions: { - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } - } -}); - -const middlewares = [thunk, fetchMiddleware]; +// const middlewares = [thunk, fetchMiddleware]; const initialState = { config: { @@ -37,6 +25,6 @@ const initialState = { } }; -const store = createStore(ungitApp, initialState, applyMiddleware(...middlewares)); +const store = createStore(ungitApp, initialState, applyMiddleware(apiMiddleware)); export default store; \ No newline at end of file From 205ccf061bbdc8cb54ebe0dbfabd85a73a25b069 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 15:57:57 +0800 Subject: [PATCH 11/15] remove useless comments --- src-react/actions/version.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src-react/actions/version.js b/src-react/actions/version.js index e0a28e1..dabde16 100644 --- a/src-react/actions/version.js +++ b/src-react/actions/version.js @@ -2,13 +2,6 @@ import * as types from 'constants/action-types'; import { CALL_API } from 'redux-api-middleware'; export function fetchLatestVersion() { - // return { - // type: types.FETCH_LATEST_VERSION, - // meta: {}, - // $payload: { - // url: 'http://localhost:8448/api/latestversion' - // } - // }; return { [CALL_API]: { endpoint: 'http://localhost:8448/api/latestversion', From 6f5cde5a3a589cbdd193bca994895f33ef7a3919 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 16:25:06 +0800 Subject: [PATCH 12/15] complete actions test --- .../__tests__/actions/ungit-config.test.js | 22 ++++++++++++ .../__tests__/actions/user-config.test.js | 21 +++++++++++ src-react/__tests__/actions/version.test.js | 35 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 src-react/__tests__/actions/ungit-config.test.js create mode 100644 src-react/__tests__/actions/user-config.test.js create mode 100644 src-react/__tests__/actions/version.test.js diff --git a/src-react/__tests__/actions/ungit-config.test.js b/src-react/__tests__/actions/ungit-config.test.js new file mode 100644 index 0000000..db24256 --- /dev/null +++ b/src-react/__tests__/actions/ungit-config.test.js @@ -0,0 +1,22 @@ +import { CALL_API } from 'redux-api-middleware'; +import expect from 'expect.js'; + +import * as types from 'constants/action-types'; +import { fetchUngitConfig } from 'actions/ungit-config'; + +describe('ungit-config.js action', () => { + describe('fetchUngitConfig', () => { + it('returns CALL_API action object', function(){ + const action = fetchUngitConfig(); + + expect(action[CALL_API]).to.be.an('object'); + expect(action[CALL_API].endpoint).to.eql('http://localhost:8448/ungit/config'); + expect(action[CALL_API].method).to.eql('GET'); + expect(action[CALL_API].types).to.be.an('array'); + expect(action[CALL_API].types[0]).to.eql(types.FETCH_UNGIT_CONFIG_REQUEST); + expect(action[CALL_API].types[1]).to.be.an('object'); + expect(action[CALL_API].types[1].type).to.eql(types.FETCH_UNGIT_CONFIG_SUCCESS); + expect(action[CALL_API].types[2]).to.eql(types.FETCH_UNGIT_CONFIG_FAILURE); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/actions/user-config.test.js b/src-react/__tests__/actions/user-config.test.js new file mode 100644 index 0000000..60d25bb --- /dev/null +++ b/src-react/__tests__/actions/user-config.test.js @@ -0,0 +1,21 @@ +import { CALL_API } from 'redux-api-middleware'; +import expect from 'expect.js'; + +import * as types from 'constants/action-types'; +import { fetchUserConfig } from 'actions/user-config'; + +describe('user-config.js action', () => { + describe('fetchUserConfig', () => { + it('returns CALL_API action object', function(){ + const action = fetchUserConfig(); + + expect(action[CALL_API]).to.be.an('object'); + expect(action[CALL_API].endpoint).to.eql('http://localhost:8448/api/userconfig'); + expect(action[CALL_API].method).to.eql('GET'); + expect(action[CALL_API].types).to.be.an('array'); + expect(action[CALL_API].types[0]).to.eql(types.FETCH_USER_CONFIG_REQUEST); + expect(action[CALL_API].types[1]).to.eql(types.FETCH_USER_CONFIG_SUCCESS); + expect(action[CALL_API].types[2]).to.eql(types.FETCH_USER_CONFIG_FAILURE); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/actions/version.test.js b/src-react/__tests__/actions/version.test.js new file mode 100644 index 0000000..a9ac579 --- /dev/null +++ b/src-react/__tests__/actions/version.test.js @@ -0,0 +1,35 @@ +import { CALL_API } from 'redux-api-middleware'; +import expect from 'expect.js'; + +import * as types from 'constants/action-types'; +import { fetchLatestVersion, fetchGitVersion } from 'actions/version'; + +describe('version.js action', () => { + describe('fetchLatestVersion', () => { + it('returns CALL_API action object', function(){ + const action = fetchLatestVersion(); + + expect(action[CALL_API]).to.be.an('object'); + expect(action[CALL_API].endpoint).to.eql('http://localhost:8448/api/latestversion'); + expect(action[CALL_API].method).to.eql('GET'); + expect(action[CALL_API].types).to.be.an('array'); + expect(action[CALL_API].types[0]).to.eql(types.FETCH_LATEST_VERSION_REQUEST); + expect(action[CALL_API].types[1]).to.eql(types.FETCH_LATEST_VERSION_SUCCESS); + expect(action[CALL_API].types[2]).to.eql(types.FETCH_LATEST_VERSION_FAILURE); + }); + }); + + describe('fetchGitVersion', () => { + it('returns CALL_API action object', function(){ + const action = fetchGitVersion(); + + expect(action[CALL_API]).to.be.an('object'); + expect(action[CALL_API].endpoint).to.eql('http://localhost:8448/api/gitversion'); + expect(action[CALL_API].method).to.eql('GET'); + expect(action[CALL_API].types).to.be.an('array'); + expect(action[CALL_API].types[0]).to.eql(types.FETCH_GIT_VERSION_REQUEST); + expect(action[CALL_API].types[1]).to.eql(types.FETCH_GIT_VERSION_SUCCESS); + expect(action[CALL_API].types[2]).to.eql(types.FETCH_GIT_VERSION_FAILURE); + }); + }); +}); \ No newline at end of file From 930a078c1db82a2291503e51f3f3d4a0d6505039 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 17:59:54 +0800 Subject: [PATCH 13/15] add nock and redux-mock-store for testing redux middleware --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 926dbc8..8c2a9b8 100644 --- a/package.json +++ b/package.json @@ -125,12 +125,14 @@ "jest": "18.1.0", "json-loader": "0.5.4", "mocha": "~3.3.0", + "nock": "^9.0.13", "node-sass": "^4.5.3", "object-assign": "4.1.1", "phantomjs-prebuilt": "~2.1.14", "postcss-loader": "1.2.2", "promise": "7.1.1", "react-dev-utils": "^0.5.2", + "redux-mock-store": "^1.2.3", "sass-loader": "^6.0.5", "sinon": "^2.3.4", "style-loader": "0.13.1", From 9283484e2df07db501bce4ef811f0a8e3e7d6ac5 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 18:00:23 +0800 Subject: [PATCH 14/15] complete middlewares test --- .../redux-api/ungit-config.test.js | 79 ++++++++++ .../middlewares/redux-api/user-config.test.js | 75 ++++++++++ .../middlewares/redux-api/version.test.js | 137 ++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 src-react/__tests__/middlewares/redux-api/ungit-config.test.js create mode 100644 src-react/__tests__/middlewares/redux-api/user-config.test.js create mode 100644 src-react/__tests__/middlewares/redux-api/version.test.js diff --git a/src-react/__tests__/middlewares/redux-api/ungit-config.test.js b/src-react/__tests__/middlewares/redux-api/ungit-config.test.js new file mode 100644 index 0000000..50aedbc --- /dev/null +++ b/src-react/__tests__/middlewares/redux-api/ungit-config.test.js @@ -0,0 +1,79 @@ +import nock from 'nock'; +import sinon from 'sinon'; +import expect from 'expect.js'; +import configureStore from 'redux-mock-store'; +import { createStore, applyMiddleware } from 'redux'; +import { apiMiddleware, CALL_API } from 'redux-api-middleware'; + +import * as types from 'constants/action-types'; +import { fetchUngitConfig } from 'actions/ungit-config'; + +const createMockStore = configureStore([ apiMiddleware ]); + +describe('redux-api-middleware::ungit-config', () => { + describe('fetchUngitConfig successfully', () => { + const mockPayload = { message: 'success' }; + + beforeEach(() => { + nock('http://localhost:8448') + .get('/ungit/config') + .reply(200, mockPayload); + + nock('http://localhost:8448') + .get('/api/userconfig') + .reply(200, mockPayload); + }); + + it('should dispatch \'FETCH_UNGIT_CONFIG_REQUEST\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 1) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_UNGIT_CONFIG_REQUEST); + done(); + } + }); + store.dispatch(fetchUngitConfig()); + }); + + it('should dispatch \'FETCH_UNGIT_CONFIG_SUCCESS\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_UNGIT_CONFIG_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_UNGIT_CONFIG_SUCCESS); + expect(dispatchedActions[1].payload).to.eql(mockPayload); + done(); + } + }); + store.dispatch(fetchUngitConfig()); + }); + }); + + describe('fetchUngitConfig fails', () => { + + beforeEach(() => { + nock('http://localhost:8448') + .get('/ungit/config') + .reply(500, null); + }); + + it('should dispatch \'FETCH_UNGIT_CONFIG_FAILURE\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_UNGIT_CONFIG_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_UNGIT_CONFIG_FAILURE); + expect(dispatchedActions[1].payload).to.be.an('object'); + done(); + } + }); + store.dispatch(fetchUngitConfig()); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/middlewares/redux-api/user-config.test.js b/src-react/__tests__/middlewares/redux-api/user-config.test.js new file mode 100644 index 0000000..a284dab --- /dev/null +++ b/src-react/__tests__/middlewares/redux-api/user-config.test.js @@ -0,0 +1,75 @@ +import nock from 'nock'; +import sinon from 'sinon'; +import expect from 'expect.js'; +import configureStore from 'redux-mock-store'; +import { createStore, applyMiddleware } from 'redux'; +import { apiMiddleware, CALL_API } from 'redux-api-middleware'; + +import * as types from 'constants/action-types'; +import { fetchUserConfig } from 'actions/user-config'; + +const createMockStore = configureStore([ apiMiddleware ]); + +describe('redux-api-middleware::user-config', () => { + describe('fetchUserConfig successfully', () => { + const mockPayload = { message: 'success' }; + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/userconfig') + .reply(200, mockPayload); + }); + + it('should dispatch \'FETCH_USER_CONFIG_REQUEST\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 1) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_USER_CONFIG_REQUEST); + done(); + } + }); + store.dispatch(fetchUserConfig()); + }); + + it('should dispatch \'FETCH_USER_CONFIG_SUCCESS\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_USER_CONFIG_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_USER_CONFIG_SUCCESS); + expect(dispatchedActions[1].payload).to.eql(mockPayload); + done(); + } + }); + store.dispatch(fetchUserConfig()); + }); + }); + + describe('fetchUserConfig fails', () => { + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/userconfig') + .reply(500, null); + }); + + it('should dispatch \'FETCH_USER_CONFIG_FAILURE\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_USER_CONFIG_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_USER_CONFIG_FAILURE); + expect(dispatchedActions[1].payload).to.be.an('object'); + done(); + } + }); + store.dispatch(fetchUserConfig()); + }); + }); +}); \ No newline at end of file diff --git a/src-react/__tests__/middlewares/redux-api/version.test.js b/src-react/__tests__/middlewares/redux-api/version.test.js new file mode 100644 index 0000000..fa795b2 --- /dev/null +++ b/src-react/__tests__/middlewares/redux-api/version.test.js @@ -0,0 +1,137 @@ +import nock from 'nock'; +import sinon from 'sinon'; +import expect from 'expect.js'; +import configureStore from 'redux-mock-store'; +import { createStore, applyMiddleware } from 'redux'; +import { apiMiddleware, CALL_API } from 'redux-api-middleware'; + +import * as types from 'constants/action-types'; +import { fetchLatestVersion, fetchGitVersion } from 'actions/version'; + +const createMockStore = configureStore([ apiMiddleware ]); + +describe('redux-api-middleware::version', () => { + describe('fetchLatestVersion successfully', () => { + const mockPayload = { message: 'success' }; + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/latestversion') + .reply(200, mockPayload); + }); + + it('should dispatch \'FETCH_LATEST_VERSION_REQUEST\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 1) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_LATEST_VERSION_REQUEST); + done(); + } + }); + store.dispatch(fetchLatestVersion()); + }); + + it('should dispatch \'FETCH_USER_CONFIG_SUCCESS\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_LATEST_VERSION_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_LATEST_VERSION_SUCCESS); + expect(dispatchedActions[1].payload).to.eql(mockPayload); + done(); + } + }); + store.dispatch(fetchLatestVersion()); + }); + }); + + describe('fetchLatestVersion fails', () => { + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/latestversion') + .reply(500, null); + }); + + it('should dispatch \'FETCH_LATEST_VERSION_FAILURE\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_LATEST_VERSION_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_LATEST_VERSION_FAILURE); + expect(dispatchedActions[1].payload).to.be.an('object'); + done(); + } + }); + store.dispatch(fetchLatestVersion()); + }); + }); + + describe('fetchGitVersion successfully', () => { + const mockPayload = { message: 'success' }; + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/gitversion') + .reply(200, mockPayload); + }); + + it('should dispatch \'FETCH_GIT_VERSION_REQUEST\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 1) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_GIT_VERSION_REQUEST); + done(); + } + }); + store.dispatch(fetchGitVersion()); + }); + + it('should dispatch \'FETCH_GIT_VERSION_SUCCESS\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_GIT_VERSION_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_GIT_VERSION_SUCCESS); + expect(dispatchedActions[1].payload).to.eql(mockPayload); + done(); + } + }); + store.dispatch(fetchGitVersion()); + }); + }); + + describe('fetchGitVersion fails', () => { + + beforeEach(() => { + nock('http://localhost:8448') + .get('/api/gitversion') + .reply(500, null); + }); + + it('should dispatch \'FETCH_GIT_VERSION_FAILURE\'', done => { + const store = createMockStore({}); + + store.subscribe(() => { + const dispatchedActions = store.getActions(); + if (dispatchedActions.length === 2) { + expect(dispatchedActions[0].type).to.eql(types.FETCH_GIT_VERSION_REQUEST); + expect(dispatchedActions[1].type).to.eql(types.FETCH_GIT_VERSION_FAILURE); + expect(dispatchedActions[1].payload).to.be.an('object'); + done(); + } + }); + store.dispatch(fetchGitVersion()); + }); + }); +}); \ No newline at end of file From 81a0dacc4b1d0930266dc824c8e40175eb1eda2b Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 25 Jun 2017 18:08:49 +0800 Subject: [PATCH 15/15] configure travis --- .travis.yml | 16 ++++++++-------- scripts/test.js | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c75977..75bb68a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,10 @@ sudo: required language: node_js node_js: - - "4.1" - - "5.3" - "6.0" branches: only: - - master + - phase-1 env: global: - CXX=g++-4.8 @@ -19,13 +17,15 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 -before_script: +before_install: - npm cache clean - - npm install -g grunt-cli - if [ "$GIT_VERSION" = "edge" ]; then sudo add-apt-repository ppa:git-core/ppa -y && sudo apt-get update -q && sudo apt-get install -y git; fi - git config --global user.email "test@testy.com" - git config --global user.name "Test testy" - git version - - grunt -d -after_script: - - grunt travisnpmpublish +install: + - npm install + +script: + - node -v + - npm run test-react \ No newline at end of file diff --git a/scripts/test.js b/scripts/test.js index 1995442..e7c5843 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -1,4 +1,9 @@ 'use strict'; +const regeneratorRuntime = require('babel-runtime/regenerator'); + +if (!regeneratorRuntime.default) { + regeneratorRuntime.default = regeneratorRuntime; +} process.env.NODE_ENV = 'test'; process.env.PUBLIC_URL = '';