diff --git a/.babelrc b/.babelrc index 56473aa..50653be 100644 --- a/.babelrc +++ b/.babelrc @@ -9,6 +9,9 @@ "locals": ["module"] }] }]] + }, + "test": { + "plugins": ["istanbul"] } } } diff --git a/config.js b/config.js index bb0c6f0..a0cccbd 100644 --- a/config.js +++ b/config.js @@ -9,6 +9,7 @@ var path = require('path'), module.exports = { babel_polyfill : 'node_modules/babel-polyfill/dist/polyfill.js', code_coverage_dir : 'code-coverage-report/', + code_coverage_path : path.resolve(__dirname, 'code-coverage-report'), code_coverage_lcov_sub_dir : 'report-lcov', code_coverage_sub_dir : 'html', dist : 'dist', @@ -20,15 +21,19 @@ module.exports = { json_path : srcPath + '/static/json/*.json', src_path : srcPath, src_static_path : srcPath + '/static', + webpack_actions_path : srcPath + '/modules/actions', webpack_client_regex : /\.js$|\.jsx$/, webpack_components_path : srcPath + '/components', webpack_constants_path : srcPath + '/constants/constants.js', webpack_css_regex : /\.css$/, webpack_dev_config : './webpack.dev.config', webpack_dist_config : './webpack.dist.config', - webpack_entry : srcPath + '/index.jsx', - webpack_resolve_extensions : ['', '.js', '.jsx'], + webpack_entry : ['babel-polyfill', srcPath + '/index.jsx'], webpack_exclude : /node_modules/, + webpack_modules_path : srcPath + '/modules', + webpack_reducers_path : srcPath + '/modules/reducers', + webpack_resolve_extensions : ['', '.js', '.jsx'], + webpack_sagas_path : srcPath + '/modules/sagas', webpack_test_config : './webpack.test.config', webpack_test_context : 'webpack/webpack.test.context.js', webpack_test_utils_path : srcPath + '/utils/testUtils.js', diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 74c7a4e..cd4acb8 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -328,6 +328,11 @@ "from": "babel-plugin-check-es2015-constants@>=6.3.13 <7.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz" }, + "babel-plugin-istanbul": { + "version": "2.0.1", + "from": "babel-plugin-istanbul@*", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-2.0.1.tgz" + }, "babel-plugin-react-transform": { "version": "2.0.2", "from": "babel-plugin-react-transform@*", @@ -2083,11 +2088,6 @@ "from": "file-entry-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz" }, - "file-loader": { - "version": "0.8.5", - "from": "file-loader@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.8.5.tgz" - }, "filename-regex": { "version": "2.0.0", "from": "filename-regex@>=2.0.0 <3.0.0", @@ -2896,10 +2896,15 @@ } } }, - "istanbul-instrumenter-loader": { - "version": "0.2.0", - "from": "istanbul-instrumenter-loader@*", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-0.2.0.tgz" + "istanbul-lib-coverage": { + "version": "1.0.0", + "from": "istanbul-lib-coverage@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.0.tgz" + }, + "istanbul-lib-instrument": { + "version": "1.1.1", + "from": "istanbul-lib-instrument@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.1.1.tgz" }, "jodid25519": { "version": "1.0.2", @@ -4758,10 +4763,10 @@ "from": "redux-mock-store@1.1.4", "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.1.4.tgz" }, - "redux-thunk": { - "version": "2.1.0", - "from": "redux-thunk@*", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.1.0.tgz" + "redux-saga": { + "version": "0.11.1", + "from": "redux-saga@*", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-0.11.1.tgz" }, "regenerate": { "version": "1.3.1", @@ -4897,6 +4902,11 @@ "from": "request-progress@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz" }, + "require-main-filename": { + "version": "1.0.1", + "from": "require-main-filename@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" + }, "require-uncached": { "version": "1.0.2", "from": "require-uncached@>=1.0.2 <2.0.0", @@ -5304,38 +5314,6 @@ "from": "string-hash@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.0.tgz" }, - "string-replace-webpack-plugin": { - "version": "0.0.3", - "from": "string-replace-webpack-plugin@*", - "resolved": "https://registry.npmjs.org/string-replace-webpack-plugin/-/string-replace-webpack-plugin-0.0.3.tgz", - "dependencies": { - "async": { - "version": "0.2.10", - "from": "async@>=0.2.10 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - }, - "css-loader": { - "version": "0.9.1", - "from": "css-loader@>=0.9.1 <0.10.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.9.1.tgz" - }, - "csso": { - "version": "1.3.12", - "from": "csso@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-1.3.12.tgz" - }, - "source-map": { - "version": "0.1.43", - "from": "source-map@>=0.1.38 <0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz" - }, - "style-loader": { - "version": "0.8.3", - "from": "style-loader@>=0.8.3 <0.9.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.8.3.tgz" - } - } - }, "string-width": { "version": "1.0.2", "from": "string-width@>=1.0.1 <2.0.0", @@ -5443,6 +5421,11 @@ "from": "tcomb@>=2.5.1 <3.0.0", "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-2.7.0.tgz" }, + "test-exclude": { + "version": "2.1.2", + "from": "test-exclude@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-2.1.2.tgz" + }, "text-table": { "version": "0.2.0", "from": "text-table@>=0.2.0 <0.3.0", diff --git a/package.json b/package.json index 0939777..03b1a6a 100644 --- a/package.json +++ b/package.json @@ -50,12 +50,13 @@ "react-redux": "^4.4.5", "redux": "^3.6.0", "redux-logger": "^2.6.1", - "redux-thunk": "^2.1.0" + "redux-saga": "^0.11.1" }, "devDependencies": { "autoprefixer": "^6.4.1", "babel-eslint": "^6.1.2", "babel-loader": "^6.2.5", + "babel-plugin-istanbul": "^2.0.1", "babel-plugin-react-transform": "^2.0.2", "babel-polyfill": "^6.13.0", "babel-preset-es2015": "^6.14.0", @@ -76,7 +77,6 @@ "eslint-plugin-react": "^6.2.0", "extract-text-webpack-plugin": "^1.0.1", "html-webpack-plugin": "^2.22.0", - "istanbul-instrumenter-loader": "^0.2.0", "karma": "^1.2.0", "karma-chai": "^0.1.0", "karma-coverage": "^1.1.1", @@ -95,7 +95,6 @@ "react-addons-test-utils": "^15.3.1", "react-transform-hmr": "^1.0.4", "redux-mock-store": "^1.1.4", - "string-replace-webpack-plugin": "0.0.3", "style-loader": "^0.13.1", "webpack": "^1.13.2", "webpack-dashboard": "^0.1.8", diff --git a/src/components/containers/Slideshow/Slideshow.jsx b/src/components/containers/Slideshow/Slideshow.jsx index bb2e4a0..fbbee90 100644 --- a/src/components/containers/Slideshow/Slideshow.jsx +++ b/src/components/containers/Slideshow/Slideshow.jsx @@ -44,12 +44,12 @@ const propTypes = { transition: React.PropTypes.oneOf(['slide', 'fade']).isRequired }, mapStateToProps = state => ({ - backgroundSize: state.SlideshowReducer.backgroundSize, - currentSlideIndex: state.SlideshowReducer.currentSlideIndex, - direction: state.SlideshowReducer.direction, - settingsPanel: state.SlideshowReducer.settingsPanel, - slides: state.SlideshowReducer.slides, - transition: state.SlideshowReducer.transition + backgroundSize: state.slideshowReducer.backgroundSize, + currentSlideIndex: state.slideshowReducer.currentSlideIndex, + direction: state.slideshowReducer.direction, + settingsPanel: state.slideshowReducer.settingsPanel, + slides: state.slideshowReducer.slides, + transition: state.slideshowReducer.transition }); class Slideshow extends React.Component { diff --git a/src/components/containers/Slideshow/SlideshowActions.js b/src/components/containers/Slideshow/SlideshowActions.js index ba8973b..1f5fa47 100644 --- a/src/components/containers/Slideshow/SlideshowActions.js +++ b/src/components/containers/Slideshow/SlideshowActions.js @@ -1,23 +1,10 @@ import { ACTIONS } from 'constants'; -import { jsonLoader } from 'utils'; export default (dispatch) => ({ - onRequestJSON: (filePath) => { - dispatch({ - filePath, - type: ACTIONS.SLIDESHOW_JSON_REQUEST - }); - return jsonLoader(filePath).then( - (parsedJSON) => dispatch({ - parsedJSON, - type: ACTIONS.SLIDESHOW_JSON_RECEIVE - }), - (error) => dispatch({ - error, - type: ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR - }) - ); - }, + onRequestJSON: (filePath) => dispatch({ + filePath, + type: ACTIONS.SLIDESHOW_JSON_REQUEST + }), onToggleSettings: () => dispatch({ type: ACTIONS.SLIDESHOW_SETTINGS_TOGGLE }) diff --git a/src/components/containers/Slideshow/tests/Slideshow.spec.js b/src/components/containers/Slideshow/tests/Slideshow.spec.js index 4d562e1..66c6c1e 100644 --- a/src/components/containers/Slideshow/tests/Slideshow.spec.js +++ b/src/components/containers/Slideshow/tests/Slideshow.spec.js @@ -10,7 +10,7 @@ import Slideshow from '../Slideshow'; describe('Slideshow', () => { const mockState = { noSlide: { - SlideshowReducer: { + slideshowReducer: { backgroundSize: 'cover', currentSlideIndex: 0, direction: 'next', @@ -18,7 +18,7 @@ describe('Slideshow', () => { } }, slide: { - SlideshowReducer: { + slideshowReducer: { backgroundSize: 'cover', currentSlideIndex: 0, direction: 'next', diff --git a/src/components/containers/Slideshow/tests/SlideshowActions.spec.js b/src/components/containers/Slideshow/tests/SlideshowActions.spec.js index 972b763..a11c481 100644 --- a/src/components/containers/Slideshow/tests/SlideshowActions.spec.js +++ b/src/components/containers/Slideshow/tests/SlideshowActions.spec.js @@ -9,7 +9,7 @@ describe('SlideshowActions', () => { store = null; }); - it(`Dispatches \`${ACTIONS.SLIDESHOW_JSON_REQUEST}\` and \`${ACTIONS.SLIDESHOW_JSON_RECEIVE}\` when \`requestJSON\` is called, and json is valid`, () => { + it(`Dispatches \`${ACTIONS.SLIDESHOW_JSON_REQUEST}\` when \`requestJSON\` is called`, (done) => { const filePath = 'src/static/json/mock-valid.json'; store = mockStore({ @@ -17,38 +17,15 @@ describe('SlideshowActions', () => { { filePath, type: ACTIONS.SLIDESHOW_JSON_REQUEST - }, - { - parsedJSON: { mock: 'mock' }, - type: ACTIONS.SLIDESHOW_JSON_RECEIVE } ] }); - return SlideshowActions(store.dispatch) - .onRequestJSON(filePath) - .then(store.testExpectedActions); - }); - - it(`Dispatches \`${ACTIONS.SLIDESHOW_JSON_REQUEST}\` and \`${ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR}\` when \`requestJSON\` is called, but json can not be loaded`, () => { - const filePath = '/mock/path.json'; + SlideshowActions(store.dispatch).onRequestJSON(filePath); - store = mockStore({ - expectedActions: [ - { - filePath, - type: ACTIONS.SLIDESHOW_JSON_REQUEST - }, - { - error: {}, - type: ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR - } - ] - }); + store.testExpectedActions(); - return SlideshowActions(store.dispatch) - .onRequestJSON(filePath) - .then(store.testExpectedActions); + done(); }); it(`Dispatches \`${ACTIONS.SLIDESHOW_SETTINGS_TOGGLE}\` when \`toggleSettings\` is called`, (done) => { diff --git a/src/modules/reducers/README.md b/src/modules/reducers/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/reducers/index.js b/src/modules/reducers/index.js new file mode 100644 index 0000000..c7f6f5e --- /dev/null +++ b/src/modules/reducers/index.js @@ -0,0 +1,5 @@ +import slideshowReducer from './slideshow/slideshowReducer'; + +export default { + slideshowReducer +}; diff --git a/src/components/containers/Slideshow/SlideshowReducer.js b/src/modules/reducers/slideshow/slideshowReducer.js similarity index 97% rename from src/components/containers/Slideshow/SlideshowReducer.js rename to src/modules/reducers/slideshow/slideshowReducer.js index bd4bdf1..78f0915 100644 --- a/src/components/containers/Slideshow/SlideshowReducer.js +++ b/src/modules/reducers/slideshow/slideshowReducer.js @@ -26,7 +26,7 @@ export default function (state = defaultState, action = {}) { if (Array.isArray(action.parsedJSON)) { action.parsedJSON.forEach((slide) => { slide.id = generateId(); - slide.views = 0; + slide.views = 1; // seed with first view }); } diff --git a/src/components/containers/Slideshow/tests/SlideshowReducer.spec.js b/src/modules/reducers/slideshow/slideshowReducer.spec.js similarity index 93% rename from src/components/containers/Slideshow/tests/SlideshowReducer.spec.js rename to src/modules/reducers/slideshow/slideshowReducer.spec.js index 4dc418d..05d8c32 100644 --- a/src/components/containers/Slideshow/tests/SlideshowReducer.spec.js +++ b/src/modules/reducers/slideshow/slideshowReducer.spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { reducerActionHandler } from 'testUtils'; import { ACTIONS } from 'constants'; -import SlideshowReducer, { defaultState } from '../SlideshowReducer'; +import slideshowReducer, { defaultState } from './slideshowReducer'; -describe('SlideshowReducer', () => { +describe('slideshowReducer', () => { const slides = [ { src: 'mock', views: 1, id: 'mock' }, { src: 'mock', views: 2, id: 'mock2' }, @@ -12,14 +12,14 @@ describe('SlideshowReducer', () => { ]; it('Should return initial state, even with bad input', (done) => { - expect(SlideshowReducer()).to.eql(defaultState); - expect(SlideshowReducer(), {}).to.eql(defaultState); + expect(slideshowReducer()).to.eql(defaultState); + expect(slideshowReducer(), {}).to.eql(defaultState); done(); }); it(`Should handle the ${ACTIONS.SLIDESHOW_JSON_REQUEST} action`, (done) => { reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: defaultState, // action to be handeled by the reducer which will modify state @@ -37,7 +37,7 @@ describe('SlideshowReducer', () => { it(`Should handle the ${ACTIONS.SLIDESHOW_JSON_RECEIVE} action`, (done) => { reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: Object.assign({}, defaultState, { loading: true @@ -61,7 +61,7 @@ describe('SlideshowReducer', () => { const mockError = new Error('mock'); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: Object.assign({}, defaultState, { loading: true @@ -90,7 +90,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -112,7 +112,7 @@ describe('SlideshowReducer', () => { it(`Should handle the ${ACTIONS.SLIDESHOW_SETTINGS_TOGGLE} action`, (done) => { reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: defaultState, // action to be handeled by the reducer which will modify state @@ -130,7 +130,7 @@ describe('SlideshowReducer', () => { it(`Should handle the ${ACTIONS.SLIDESHOW_SETTINGS_CHANGE_BACKGROUND_SIZE} action`, (done) => { reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: defaultState, // action to be handeled by the reducer which will modify state @@ -149,7 +149,7 @@ describe('SlideshowReducer', () => { it(`Should handle the ${ACTIONS.SLIDESHOW_SETTINGS_CHANGE_TRANSITION} action`, (done) => { reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: defaultState, // action to be handeled by the reducer which will modify state @@ -174,7 +174,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -198,7 +198,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -223,7 +223,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -248,7 +248,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -274,7 +274,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state @@ -298,7 +298,7 @@ describe('SlideshowReducer', () => { }); reducerActionHandler({ - reducer: SlideshowReducer, + reducer: slideshowReducer, // initial state state: modifiedDefaultState, // action to be handeled by the reducer which will modify state diff --git a/src/modules/sagas/README.md b/src/modules/sagas/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/sagas/index.js b/src/modules/sagas/index.js new file mode 100644 index 0000000..d37c12d --- /dev/null +++ b/src/modules/sagas/index.js @@ -0,0 +1,8 @@ +import { fork } from 'redux-saga/effects'; +import { watchJSONRequest } from './slideshow/slideshowSagas'; + +export default function* root() { + yield [ + fork(watchJSONRequest) + ]; +} diff --git a/src/modules/sagas/slideshow/slideshowSagas.js b/src/modules/sagas/slideshow/slideshowSagas.js new file mode 100644 index 0000000..6e6e71d --- /dev/null +++ b/src/modules/sagas/slideshow/slideshowSagas.js @@ -0,0 +1,25 @@ +/* eslint import/prefer-default-export: 0 */ +import { takeLatest } from 'redux-saga'; +import { call, put } from 'redux-saga/effects'; +import { ACTIONS } from 'constants'; +import { jsonLoader } from 'utils'; + +export function* fetchJSON(action) { + try { + const parsedJSON = yield call(jsonLoader, action.filePath); + + yield put({ + parsedJSON, + type: ACTIONS.SLIDESHOW_JSON_RECEIVE + }); + } catch (error) { + yield put({ + error, + type: ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR + }); + } +} + +export function* watchJSONRequest() { + yield* takeLatest(ACTIONS.SLIDESHOW_JSON_REQUEST, fetchJSON); +} diff --git a/src/modules/sagas/slideshow/slideshowSagas.spec.js b/src/modules/sagas/slideshow/slideshowSagas.spec.js new file mode 100644 index 0000000..7db7ed3 --- /dev/null +++ b/src/modules/sagas/slideshow/slideshowSagas.spec.js @@ -0,0 +1,51 @@ +import { call, put } from 'redux-saga/effects'; +import { expect } from 'chai'; +import { ACTIONS } from 'constants'; +import { jsonLoader } from 'utils'; +import { fetchJSON } from './slideshowSagas'; + +describe('slideshowSagas', () => { + it(`Generator \`fetchJSON\` should take action \`${ACTIONS.SLIDESHOW_JSON_REQUEST}\` load json file and dispatch \`${ACTIONS.SLIDESHOW_JSON_RECEIVE}\` with valid data`, (done) => { + const filePath = 'mock.json', + generator = fetchJSON({ + filePath, + type: ACTIONS.SLIDESHOW_JSON_REQUEST + }), + parsedJSON = { mock: 'mock' }; + + let next = generator.next(); + + expect(next.value).to.eql(call(jsonLoader, filePath)); + + next = generator.next(parsedJSON); + + expect(next.value).to.eql(put({ + parsedJSON, + type: ACTIONS.SLIDESHOW_JSON_RECEIVE + })); + + done(); + }); + + it(`Generator \`fetchJSON\` should take action \`${ACTIONS.SLIDESHOW_JSON_REQUEST}\` load json file and dispatch \`${ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR}\` with invalid data`, (done) => { + const filePath = 'mock.json', + error = 'error', + generator = fetchJSON({ + filePath, + type: ACTIONS.SLIDESHOW_JSON_REQUEST + }); + + let next = generator.next(); + + expect(next.value).to.eql(call(jsonLoader, filePath)); + + next = generator.throw(error); + + expect(next.value).to.eql(put({ + error, + type: ACTIONS.SLIDESHOW_JSON_RECEIVE_ERROR + })); + + done(); + }); +}); diff --git a/src/redux/store.js b/src/redux/store.js index 9ee5d7a..6e1807a 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -1,23 +1,18 @@ import { createStore, applyMiddleware, combineReducers } from 'redux'; - -// individual reducer data -import SlideshowReducer from 'components/containers/Slideshow/SlideshowReducer'; - -// middleware for redux -import thunk from 'redux-thunk'; +import reducers from 'reducers'; +import sagas from 'sagas'; +import createSagaMiddleware from 'redux-saga'; import logger from './middleware/logger'; import exceptionReporter from './middleware/exceptionReporter'; // combine all individual reducers into 1 object -const reducer = combineReducers({ - SlideshowReducer -}); - -// init redux store -let state = {}; +const reducer = combineReducers(reducers), + saga = createSagaMiddleware(), + store = createStore( + reducer, + applyMiddleware(saga, logger, exceptionReporter) + ); -state = reducer(state, { - name: 'CONSTRUCT' -}); +saga.run(sagas); -export default applyMiddleware(thunk, logger, exceptionReporter)(createStore)(reducer, state); +export default store; diff --git a/src/utils/testUtils.js b/src/utils/testUtils.js index 359c615..e7dfb3b 100644 --- a/src/utils/testUtils.js +++ b/src/utils/testUtils.js @@ -2,9 +2,10 @@ import { expect } from 'chai'; import deepSortObject from 'deep-sort-object'; import outdent from 'outdent'; import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import createSagaMiddleware from 'redux-saga'; -const reduxMockStore = configureStore([thunk]); +const saga = createSagaMiddleware(), + reduxMockStore = configureStore([saga]); export function mockStore(opts) { const defaults = { diff --git a/src/utils/tests/utils.spec.js b/src/utils/tests/utils.spec.js index dc537ff..3eb0132 100644 --- a/src/utils/tests/utils.spec.js +++ b/src/utils/tests/utils.spec.js @@ -5,6 +5,8 @@ describe('utils', () => { it('Should contain `generateId` method', (done) => { expect(utils.generateId).to.be.a.function; + utils.generateId(); + done(); }); @@ -25,4 +27,12 @@ describe('utils', () => { done(); }); }); + + it('Should be able to run the `jsonLoader` with invalid json path and have it return the correct error', (done) => { + utils.jsonLoader('mock.json') + .catch((err) => { + expect(err.toString()).to.deep.equal(new Error('Not Found').toString()); + done(); + }); + }); }); diff --git a/webpack/webpack.config.js b/webpack/webpack.config.js index 6bf5f1b..ddbf9ea 100644 --- a/webpack/webpack.config.js +++ b/webpack/webpack.config.js @@ -46,8 +46,12 @@ var configFile = require('../config'), extensions: configFile.webpack_resolve_extensions, root: configFile.src_path, alias: { + actions$: configFile.webpack_actions_path, components: configFile.webpack_components_path, constants$: configFile.webpack_constants_path, + modules$: configFile.webpack_modules_path, + reducers$: configFile.webpack_reducers_path, + sagas$: configFile.webpack_sagas_path, utils$: configFile.webpack_utils_path } } diff --git a/webpack/webpack.test.config.js b/webpack/webpack.test.config.js index 7166581..f0802a4 100644 --- a/webpack/webpack.test.config.js +++ b/webpack/webpack.test.config.js @@ -1,7 +1,7 @@ "use strict"; var configFile = require('../config.js'), - stringReplacePlugin = require('string-replace-webpack-plugin'), + clean = require('clean-webpack-plugin'), warningsPlugin = require('./webpack-karma-warnings-plugin'); // test config @@ -15,47 +15,12 @@ module.exports = { exclude: /node_modules/ } ], - // this loader allows istanbul code coverage reported to ignore code that is added from Babel loaders: [ - { - test: configFile.webpack_client_regex, - exclude: configFile.webpack_exclude, - loader: stringReplacePlugin.replace({ - replacements: [ - { - pattern: /function _/g, - replacement: function() { - return '/* istanbul ignore next */ function _'; - } - }, - { - pattern: /var _createClass/g, - replacement: function() { - return '/* istanbul ignore next */ var _createClass'; - } - }, - { - pattern: /function \(target\)/g, - replacement: function() { - return '/* istanbul ignore next */ function (target)'; - } - } - ] - }) - }, { test: configFile.webpack_css_regex, loader: 'style-loader!css-loader!postcss-loader', exclude: configFile.webpack_exclude } - ], - // this is necessary or else test report will be for entire webpack bundle instead of each component - postLoaders: [ - { - test: configFile.webpack_client_regex, - exclude: configFile.webpack_exclude, - loader: 'istanbul-instrumenter' - } ] }, // eslint config @@ -77,7 +42,9 @@ module.exports = { }, // init string replace plugin for babel omissions above plugins: [ - new stringReplacePlugin(), + new clean([ + configFile.code_coverage_path + ]), new warningsPlugin() ], resolve: {