From 077e83dfc961f44c62097c3c70cf3a6841b0c18f Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Wed, 12 Oct 2016 19:19:05 +0200 Subject: [PATCH] Handle entry persisting state in actions and reducer + added tests. --- src/actions/entries.js | 24 ++++++----- src/reducers/__tests__/entries.spec.js | 58 +++++++++++++++++++++++--- src/reducers/entries.js | 18 ++++++++ 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/actions/entries.js b/src/actions/entries.js index f2fae8a5..0269385b 100644 --- a/src/actions/entries.js +++ b/src/actions/entries.js @@ -83,8 +83,8 @@ export function entryPersisting(collection, entry) { return { type: ENTRY_PERSIST_REQUEST, payload: { - collection, - entry, + collectionName: collection.get('name'), + entrySlug: entry.get('slug'), }, }; } @@ -93,17 +93,21 @@ export function entryPersisted(collection, entry) { return { type: ENTRY_PERSIST_SUCCESS, payload: { - collection, - entry, + collectionName: collection.get('name'), + entrySlug: entry.get('slug'), }, }; } export function entryPersistFail(collection, entry, error) { return { - type: ENTRIES_FAILURE, + type: ENTRY_PERSIST_FAILURE, error: 'Failed to persist entry', - payload: error.toString(), + payload: { + collectionName: collection.get('name'), + entrySlug: entry.get('slug'), + error: error.toString(), + }, }; } @@ -211,12 +215,10 @@ export function persistEntry(collection, entry) { return (dispatch, getState) => { const state = getState(); const backend = currentBackend(state.config); - const MediaProxies = entry.get('mediaFiles').map(path => getMedia(state, path)); + const mediaProxies = entry.get('mediaFiles').map(path => getMedia(state, path)); dispatch(entryPersisting(collection, entry)); - backend.persistEntry(state.config, collection, entry, MediaProxies.toJS()).then( - () => { - dispatch(entryPersisted(collection, entry)); - }, + backend.persistEntry(state.config, collection, entry, mediaProxies.toJS()).then( + () => dispatch(entryPersisted(collection, entry)), error => dispatch(entryPersistFail(collection, entry, error)) ); }; diff --git a/src/reducers/__tests__/entries.spec.js b/src/reducers/__tests__/entries.spec.js index 19ea470c..29978765 100644 --- a/src/reducers/__tests__/entries.spec.js +++ b/src/reducers/__tests__/entries.spec.js @@ -1,15 +1,16 @@ -import expect from 'expect'; -import { Map, OrderedMap, fromJS } from 'immutable'; -import { entriesLoading, entriesLoaded } from '../../actions/entries'; +import Immutable, { Map, OrderedMap, fromJS } from 'immutable'; +import * as actions from '../../actions/entries'; import reducer from '../entries'; +let initialState; + describe('entries', () => { it('should mark entries as fetching', () => { const state = OrderedMap({ posts: Map({ name: 'posts' }), }); expect( - reducer(state, entriesLoading(Map({ name: 'posts' }))) + reducer(state, actions.entriesLoading(Map({ name: 'posts' }))) ).toEqual( OrderedMap(fromJS({ posts: { name: 'posts' }, @@ -26,7 +27,7 @@ describe('entries', () => { }); const entries = [{ slug: 'a', path: '' }, { slug: 'b', title: 'B' }]; expect( - reducer(state, entriesLoaded(Map({ name: 'posts' }), entries, 0)) + reducer(state, actions.entriesLoaded(Map({ name: 'posts' }), entries, 0)) ).toEqual( OrderedMap(fromJS( { @@ -45,4 +46,51 @@ describe('entries', () => { )) ); }); + + describe('entry persisting', () => { + beforeEach(() => { + initialState = Immutable.fromJS({ + entities: { + 'posts.slug': { + collection: 'posts', + slug: 'slug', + path: 'content/blog/art-and-wine-festival.md', + partial: false, + raw: '', + data: {}, + metaData: null, + }, + }, + pages: {}, + }); + }); + + it('should handle persisting request', () => { + const newState = reducer( + initialState, + actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' })) + ); + expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBe(true); + }); + + it('should handle persisting success', () => { + let newState = reducer(initialState, + actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' })) + ); + newState = reducer(newState, + actions.entryPersisted(Map({ name: 'posts' }), Map({ slug: 'slug' })) + ); + expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBeUndefined(); + }); + + it('should handle persisting error', () => { + let newState = reducer(initialState, + actions.entryPersisting(Map({ name: 'posts' }), Map({ slug: 'slug' })) + ); + newState = reducer(newState, + actions.entryPersistFail(Map({ name: 'posts' }), Map({ slug: 'slug' }), 'Error message') + ); + expect(newState.getIn(['entities', 'posts.slug', 'isPersisting'])).toBeUndefined(); + }); + }); }); diff --git a/src/reducers/entries.js b/src/reducers/entries.js index c1971af4..4fccb962 100644 --- a/src/reducers/entries.js +++ b/src/reducers/entries.js @@ -2,6 +2,9 @@ import { Map, List, fromJS } from 'immutable'; import { ENTRY_REQUEST, ENTRY_SUCCESS, + ENTRY_PERSIST_REQUEST, + ENTRY_PERSIST_SUCCESS, + ENTRY_PERSIST_FAILURE, ENTRIES_REQUEST, ENTRIES_SUCCESS, SEARCH_ENTRIES_REQUEST, @@ -13,6 +16,10 @@ let loadedEntries; let page; let searchTerm; +function getEntryPath(collectionName, entrySlug) { + return `${ collectionName }.${ entrySlug }`; +} + const entries = (state = Map({ entities: Map(), pages: Map() }), action) => { switch (action.type) { case ENTRY_REQUEST: @@ -24,6 +31,17 @@ const entries = (state = Map({ entities: Map(), pages: Map() }), action) => { fromJS(action.payload.entry) ); + case ENTRY_PERSIST_REQUEST: { + const { collectionName, entrySlug } = action.payload; + return state.setIn(['entities', getEntryPath(collectionName, entrySlug), 'isPersisting'], true); + } + + case ENTRY_PERSIST_SUCCESS: + case ENTRY_PERSIST_FAILURE: { + const { collectionName, entrySlug } = action.payload; + return state.deleteIn(['entities', getEntryPath(collectionName, entrySlug), 'isPersisting']); + } + case ENTRIES_REQUEST: return state.setIn(['pages', action.payload.collection, 'isFetching'], true);