From 615773ac9f6c4c9d596ae3a0e990d80278d3572e Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 5 Feb 2016 09:12:44 -0500 Subject: [PATCH 1/3] Posts: Add single site requesting behavior to Redux state --- client/state/action-types.js | 3 + client/state/posts/actions.js | 36 +++++++++ client/state/posts/reducer.js | 32 ++++++++ client/state/posts/selectors.js | 17 +++++ client/state/posts/test/actions.js | 96 +++++++++++++++++++++--- client/state/posts/test/reducer.js | 107 ++++++++++++++++++++++++++- client/state/posts/test/selectors.js | 43 ++++++++++- 7 files changed, 322 insertions(+), 12 deletions(-) diff --git a/client/state/action-types.js b/client/state/action-types.js index a129e1d658625..4d1b3b9e38570 100644 --- a/client/state/action-types.js +++ b/client/state/action-types.js @@ -23,6 +23,9 @@ export const FETCH_SITE_PLANS = 'FETCH_SITE_PLANS'; export const FETCH_SITE_PLANS_COMPLETED = 'FETCH_SITE_PLANS_COMPLETED'; export const FETCH_WPORG_PLUGIN_DATA = 'FETCH_WPORG_PLUGIN_DATA'; export const NEW_NOTICE = 'NEW_NOTICE'; +export const POST_REQUEST = 'POST_REQUEST'; +export const POST_REQUEST_SUCCESS = 'POST_REQUEST_SUCCESS'; +export const POST_REQUEST_FAILURE = 'POST_REQUEST_FAILURE'; export const POSTS_RECEIVE = 'POSTS_RECEIVE'; export const POSTS_REQUEST = 'POSTS_REQUEST'; export const POSTS_REQUEST_FAILURE = 'POSTS_REQUEST_FAILURE'; diff --git a/client/state/posts/actions.js b/client/state/posts/actions.js index fe830c7f64196..9da31adc08655 100644 --- a/client/state/posts/actions.js +++ b/client/state/posts/actions.js @@ -3,6 +3,9 @@ */ import wpcom from 'lib/wp'; import { + POST_REQUEST, + POST_REQUEST_SUCCESS, + POST_REQUEST_FAILURE, POSTS_RECEIVE, POSTS_REQUEST, POSTS_REQUEST_SUCCESS, @@ -68,3 +71,36 @@ export function requestSitePosts( siteId, query = {} ) { } ); }; } + +/** + * Triggers a network request to fetch a specific post from a site. + * + * @param {Number} siteId Site ID + * @param {Number} postId Post ID + * @return {Function} Action thunk + */ +export function requestSitePost( siteId, postId ) { + return ( dispatch ) => { + dispatch( { + type: POST_REQUEST, + siteId, + postId + } ); + + return wpcom.site( siteId ).post( postId ).get().then( ( post ) => { + dispatch( receivePost( post ) ); + dispatch( { + type: POST_REQUEST_SUCCESS, + siteId, + postId + } ); + } ).catch( ( error ) => { + dispatch( { + type: POST_REQUEST_FAILURE, + siteId, + postId, + error + } ); + } ); + }; +} diff --git a/client/state/posts/reducer.js b/client/state/posts/reducer.js index 7d6535603d9d7..c4ade45d81dda 100644 --- a/client/state/posts/reducer.js +++ b/client/state/posts/reducer.js @@ -8,6 +8,9 @@ import indexBy from 'lodash/collection/indexBy'; * Internal dependencies */ import { + POST_REQUEST, + POST_REQUEST_SUCCESS, + POST_REQUEST_FAILURE, POSTS_RECEIVE, POSTS_REQUEST, POSTS_REQUEST_SUCCESS, @@ -67,6 +70,34 @@ export function sitePosts( state = {}, action ) { return state; } +/** + * Returns the updated site post requests state after an action has been + * dispatched. The state reflects a mapping of site ID, post ID pairing to a + * boolean reflecting whether a request for the post is in progress. + * + * @param {Object} state Current state + * @param {Object} action Action payload + * @return {Object} Updated state + */ +export function siteRequests( state = {}, action ) { + switch ( action.type ) { + case POST_REQUEST: + case POST_REQUEST_SUCCESS: + case POST_REQUEST_FAILURE: + return Object.assign( {}, state, { + [ action.siteId ]: Object.assign( {}, state[ action.siteId ], { + [ action.postId ]: POST_REQUEST === action.type + } ) + } ); + + case SERIALIZE: + case DESERIALIZE: + return {}; + } + + return state; +} + /** * Returns the updated post query state after an action has been dispatched. * The state reflects a mapping of site ID to active queries. @@ -142,6 +173,7 @@ export function siteQueriesLastPage( state = {}, action ) { export default combineReducers( { items, sitePosts, + siteRequests, siteQueries, siteQueriesLastPage } ); diff --git a/client/state/posts/selectors.js b/client/state/posts/selectors.js index f7e312a07aebf..020b293ee317c 100644 --- a/client/state/posts/selectors.js +++ b/client/state/posts/selectors.js @@ -158,3 +158,20 @@ export function getSitePostsForQueryIgnoringPage( state, siteId, query ) { return memo.concat( getSitePostsForQuery( state, siteId, pageQuery ) || [] ); }, [] ); } + +/** + * Returns true if a request is in progress for the specified site post, or + * false otherwise. + * + * @param {Object} state Global state tree + * @param {Number} siteId Site ID + * @param {Number} postId Post ID + * @return {Boolean} Whether request is in progress + */ +export function isRequestingSitePost( state, siteId, postId ) { + if ( ! state.posts.siteRequests[ siteId ] ) { + return false; + } + + return !! state.posts.siteRequests[ siteId ][ postId ]; +} diff --git a/client/state/posts/test/actions.js b/client/state/posts/test/actions.js index ac7f7cb4fc8c7..a74b3fb928ab5 100644 --- a/client/state/posts/test/actions.js +++ b/client/state/posts/test/actions.js @@ -10,6 +10,9 @@ import Chai, { expect } from 'chai'; * Internal dependencies */ import { + POST_REQUEST, + POST_REQUEST_SUCCESS, + POST_REQUEST_FAILURE, POSTS_RECEIVE, POSTS_REQUEST, POSTS_REQUEST_SUCCESS, @@ -18,10 +21,25 @@ import { import { receivePost, receivePosts, - requestSitePosts + requestSitePosts, + requestSitePost } from '../actions'; describe( 'actions', () => { + const spy = sinon.spy(); + + before( () => { + Chai.use( sinonChai ); + } ); + + beforeEach( () => { + spy.reset(); + } ); + + after( () => { + nock.restore(); + } ); + describe( '#receivePost()', () => { it( 'should return an action object', () => { const post = { ID: 841, title: 'Hello World' }; @@ -47,11 +65,7 @@ describe( 'actions', () => { } ); describe( '#requestSitePosts()', () => { - const spy = sinon.spy(); - before( () => { - Chai.use( sinonChai ); - nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/sites/2916284/posts' ) @@ -75,12 +89,8 @@ describe( 'actions', () => { } ); } ); - beforeEach( () => { - spy.reset(); - } ); - after( () => { - nock.restore(); + nock.cleanAll(); } ); it( 'should dispatch fetch action when thunk triggered', () => { @@ -153,4 +163,70 @@ describe( 'actions', () => { } ).catch( done ); } ); } ); + + describe( '#requestSitePost()', () => { + before( () => { + nock( 'https://public-api.wordpress.com:443' ) + .persist() + .get( '/rest/v1.1/sites/2916284/posts/413' ) + .reply( 200, { ID: 413, title: 'Ribs & Chicken' } ) + .get( '/rest/v1.1/sites/2916284/posts/420' ) + .reply( 404, { + error: 'unknown_post', + message: 'Unknown post' + } ); + } ); + + after( () => { + nock.cleanAll(); + } ); + + it( 'should dispatch request action when thunk triggered', () => { + requestSitePost( 2916284, 413 )( spy ); + + expect( spy ).to.have.been.calledWith( { + type: POST_REQUEST, + siteId: 2916284, + postId: 413 + } ); + } ); + + it( 'should dispatch posts receive action when request completes', ( done ) => { + requestSitePost( 2916284, 413 )( spy ).then( () => { + expect( spy ).to.have.been.calledWith( { + type: POSTS_RECEIVE, + posts: [ + sinon.match( { ID: 413, title: 'Ribs & Chicken' } ) + ] + } ); + + done(); + } ).catch( done ); + } ); + + it( 'should dispatch posts posts request success action when request completes', ( done ) => { + requestSitePost( 2916284, 413 )( spy ).then( () => { + expect( spy ).to.have.been.calledWith( { + type: POST_REQUEST_SUCCESS, + siteId: 2916284, + postId: 413 + } ); + + done(); + } ).catch( done ); + } ); + + it( 'should dispatch fail action when request fails', ( done ) => { + requestSitePost( 2916284, 420 )( spy ).then( () => { + expect( spy ).to.have.been.calledWith( { + type: POST_REQUEST_FAILURE, + siteId: 2916284, + postId: 420, + error: sinon.match( { message: 'Unknown post' } ) + } ); + + done(); + } ).catch( done ); + } ); + } ); } ); diff --git a/client/state/posts/test/reducer.js b/client/state/posts/test/reducer.js index a85d3f3b27978..a9ee5bb011f68 100644 --- a/client/state/posts/test/reducer.js +++ b/client/state/posts/test/reducer.js @@ -7,6 +7,9 @@ import { expect } from 'chai'; * Internal dependencies */ import { + POST_REQUEST, + POST_REQUEST_SUCCESS, + POST_REQUEST_FAILURE, POSTS_RECEIVE, POSTS_REQUEST, POSTS_REQUEST_FAILURE, @@ -18,7 +21,8 @@ import { items, sitePosts, siteQueries, - siteQueriesLastPage + siteQueriesLastPage, + siteRequests } from '../reducer'; describe( 'reducer', () => { @@ -366,4 +370,105 @@ describe( 'reducer', () => { expect( state ).to.eql( {} ); } ); } ); + + describe( '#siteRequests()', () => { + it( 'should default to an empty object', () => { + const state = siteRequests( undefined, {} ); + + expect( state ).to.eql( {} ); + } ); + + it( 'should map site ID, post ID to true value if request in progress', () => { + const state = siteRequests( undefined, { + type: POST_REQUEST, + siteId: 2916284, + postId: 841 + } ); + + expect( state ).to.eql( { + 2916284: { + 841: true + } + } ); + } ); + + it( 'should accumulate mappings', () => { + const state = siteRequests( Object.freeze( { + 2916284: { + 841: true + } + } ), { + type: POST_REQUEST, + siteId: 2916284, + postId: 413 + } ); + + expect( state ).to.eql( { + 2916284: { + 841: true, + 413: true + } + } ); + } ); + + it( 'should map site ID, post ID to false value if request finishes successfully', () => { + const state = siteRequests( Object.freeze( { + 2916284: { + 841: true + } + } ), { + type: POST_REQUEST_SUCCESS, + siteId: 2916284, + postId: 841 + } ); + + expect( state ).to.eql( { + 2916284: { + 841: false + } + } ); + } ); + + it( 'should map site ID, post ID to false value if request finishes with failure', () => { + const state = siteRequests( Object.freeze( { + 2916284: { + 841: true + } + } ), { + type: POST_REQUEST_FAILURE, + siteId: 2916284, + postId: 841 + } ); + + expect( state ).to.eql( { + 2916284: { + 841: false + } + } ); + } ); + + it( 'never persists state because this is not implemented', () => { + const state = siteRequests( Object.freeze( { + 2916284: { + 841: true + } + } ), { + type: SERIALIZE + } ); + + expect( state ).to.eql( {} ); + } ); + + it( 'never loads persisted state because this is not implemented', () => { + const state = siteRequests( Object.freeze( { + 2916284: { + 841: true + } + } ), { + type: DESERIALIZE + } ); + + expect( state ).to.eql( {} ); + } ); + } ); } ); diff --git a/client/state/posts/test/selectors.js b/client/state/posts/test/selectors.js index 90f549f11d7e8..869bf9aec6530 100644 --- a/client/state/posts/test/selectors.js +++ b/client/state/posts/test/selectors.js @@ -14,7 +14,8 @@ import { isRequestingSitePostsForQuery, getSitePostsLastPageForQuery, isSitePostsLastPageForQuery, - getSitePostsForQueryIgnoringPage + getSitePostsForQueryIgnoringPage, + isRequestingSitePost } from '../selectors'; describe( 'selectors', () => { @@ -361,4 +362,44 @@ describe( 'selectors', () => { ] ); } ); } ); + + describe( '#isRequestingSitePost()', () => { + it( 'should return false if no request has been made', () => { + const isRequesting = isRequestingSitePost( { + posts: { + siteRequests: {} + } + }, 2916284, 841 ); + + expect( isRequesting ).to.be.false; + } ); + + it( 'should return true if a request is in progress', () => { + const isRequesting = isRequestingSitePost( { + posts: { + siteRequests: { + 2916284: { + 841: true + } + } + } + }, 2916284, 841 ); + + expect( isRequesting ).to.be.true; + } ); + + it( 'should return false if a request has finished', () => { + const isRequesting = isRequestingSitePost( { + posts: { + siteRequests: { + 2916284: { + 841: false + } + } + } + }, 2916284, 841 ); + + expect( isRequesting ).to.be.false; + } ); + } ); } ); From fbb511fd04b124db37d0b9f4938bd9475461d81f Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 5 Feb 2016 09:57:11 -0500 Subject: [PATCH 2/3] Posts: Return promise object in Mocha tests --- client/state/posts/test/actions.js | 56 +++++++++++------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/client/state/posts/test/actions.js b/client/state/posts/test/actions.js index a74b3fb928ab5..2e13c3a98a5e7 100644 --- a/client/state/posts/test/actions.js +++ b/client/state/posts/test/actions.js @@ -103,8 +103,8 @@ describe( 'actions', () => { } ); } ); - it( 'should dispatch posts receive action when request completes', ( done ) => { - requestSitePosts( 2916284 )( spy ).then( () => { + it( 'should dispatch posts receive action when request completes', () => { + return requestSitePosts( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POSTS_RECEIVE, posts: [ @@ -112,13 +112,11 @@ describe( 'actions', () => { { ID: 413, title: 'Ribs & Chicken' } ] } ); - - done(); - } ).catch( done ); + } ); } ); - it( 'should dispatch posts posts request success action when request completes', ( done ) => { - requestSitePosts( 2916284 )( spy ).then( () => { + it( 'should dispatch posts posts request success action when request completes', () => { + return requestSitePosts( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, @@ -129,13 +127,11 @@ describe( 'actions', () => { { ID: 413, title: 'Ribs & Chicken' } ] } ); - - done(); - } ).catch( done ); + } ); } ); - it( 'should dispatch posts request success action with query results', ( done ) => { - requestSitePosts( 2916284, { search: 'Hello' } )( spy ).then( () => { + it( 'should dispatch posts request success action with query results', () => { + return requestSitePosts( 2916284, { search: 'Hello' } )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, @@ -145,22 +141,18 @@ describe( 'actions', () => { { ID: 841, title: 'Hello World' } ] } ); - - done(); - } ).catch( done ); + } ); } ); - it( 'should dispatch fail action when request fails', ( done ) => { - requestSitePosts( 77203074 )( spy ).then( () => { + it( 'should dispatch fail action when request fails', () => { + return requestSitePosts( 77203074 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POSTS_REQUEST_FAILURE, siteId: 77203074, query: {}, error: sinon.match( { message: 'User cannot access this private blog.' } ) } ); - - done(); - } ).catch( done ); + } ); } ); } ); @@ -191,42 +183,36 @@ describe( 'actions', () => { } ); } ); - it( 'should dispatch posts receive action when request completes', ( done ) => { - requestSitePost( 2916284, 413 )( spy ).then( () => { + it( 'should dispatch posts receive action when request completes', () => { + return requestSitePost( 2916284, 413 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POSTS_RECEIVE, posts: [ sinon.match( { ID: 413, title: 'Ribs & Chicken' } ) ] } ); - - done(); - } ).catch( done ); + } ); } ); - it( 'should dispatch posts posts request success action when request completes', ( done ) => { - requestSitePost( 2916284, 413 )( spy ).then( () => { + it( 'should dispatch posts posts request success action when request completes', () => { + return requestSitePost( 2916284, 413 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POST_REQUEST_SUCCESS, siteId: 2916284, postId: 413 } ); - - done(); - } ).catch( done ); + } ); } ); - it( 'should dispatch fail action when request fails', ( done ) => { - requestSitePost( 2916284, 420 )( spy ).then( () => { + it( 'should dispatch fail action when request fails', () => { + return requestSitePost( 2916284, 420 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POST_REQUEST_FAILURE, siteId: 2916284, postId: 420, error: sinon.match( { message: 'Unknown post' } ) } ); - - done(); - } ).catch( done ); + } ); } ); } ); } ); From 52ad4f894643e89102e9f618b020dc9c83be0b43 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 5 Feb 2016 10:28:32 -0500 Subject: [PATCH 3/3] Posts: Use deepFreeze in tests, fixing broken tests Bugs in reducer action handlers causing state to be mutated unintentionally. --- client/state/posts/reducer.js | 30 +++++++++--------- client/state/posts/test/reducer.js | 49 +++++++++++++++--------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/client/state/posts/reducer.js b/client/state/posts/reducer.js index c4ade45d81dda..b1b96bdf3b975 100644 --- a/client/state/posts/reducer.js +++ b/client/state/posts/reducer.js @@ -114,25 +114,23 @@ export function siteQueries( state = {}, action ) { const { type, siteId, posts } = action; const query = getSerializedPostsQuery( action.query ); - // Clone state and ensure that site is tracked - state = Object.assign( {}, state ); - if ( ! state[ siteId ] ) { - state[ siteId ] = {}; - } - - if ( ! state[ siteId ][ query ] ) { - state[ siteId ][ query ] = {}; - } + state = Object.assign( {}, state, { + [ siteId ]: Object.assign( {}, state[ siteId ] ) + } ); - // Only the initial request should be tracked as fetching. Success - // or failure types imply that fetching has completed. - state[ siteId ][ query ].fetching = ( POSTS_REQUEST === type ); + state[ siteId ][ query ] = Object.assign( {}, state[ siteId ][ query ], { + // Only the initial request should be tracked as fetching. + // Success or failure types imply that fetching has completed. + fetching: ( POSTS_REQUEST === type ) + } ); // When a request succeeds, map the received posts to state. if ( POSTS_REQUEST_SUCCESS === type ) { state[ siteId ][ query ].posts = posts.map( ( post ) => post.global_ID ); } + return state; + case SERIALIZE: case DESERIALIZE: return {}; @@ -154,15 +152,15 @@ export function siteQueriesLastPage( state = {}, action ) { case POSTS_REQUEST_SUCCESS: const { siteId, found } = action; - state = Object.assign( {}, state ); - if ( ! state[ siteId ] ) { - state[ siteId ] = {}; - } + state = Object.assign( {}, state, { + [ siteId ]: Object.assign( {}, state[ siteId ] ) + } ); const serializedQuery = getSerializedPostsQueryWithoutPage( action.query ); const lastPage = Math.ceil( found / ( action.query.number || DEFAULT_POST_QUERY.number ) ); state[ siteId ][ serializedQuery ] = Math.max( lastPage, 1 ); return state; + case SERIALIZE: case DESERIALIZE: return {}; diff --git a/client/state/posts/test/reducer.js b/client/state/posts/test/reducer.js index a9ee5bb011f68..a1baf0cb54276 100644 --- a/client/state/posts/test/reducer.js +++ b/client/state/posts/test/reducer.js @@ -2,6 +2,7 @@ * External dependencies */ import { expect } from 'chai'; +import deepFreeze from 'deep-freeze'; /** * Internal dependencies @@ -49,7 +50,7 @@ describe( 'reducer', () => { } ); it( 'should accumulate posts', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = items( original, { @@ -64,7 +65,7 @@ describe( 'reducer', () => { } ); it( 'should override previous post of same ID', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = items( original, { @@ -78,7 +79,7 @@ describe( 'reducer', () => { } ); it( 'never persists state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = items( original, { type: SERIALIZE } ); @@ -86,7 +87,7 @@ describe( 'reducer', () => { } ); it( 'never loads persisted state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = items( original, { type: DESERIALIZE } ); @@ -114,7 +115,7 @@ describe( 'reducer', () => { } ); } ); it( 'never persists state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = sitePosts( original, { type: SERIALIZE } ); @@ -122,7 +123,7 @@ describe( 'reducer', () => { } ); it( 'never loads persisted state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { '3d097cb7c5473c169bba0eb8e3c6cb64': { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World' } } ); const state = sitePosts( original, { type: DESERIALIZE } ); @@ -138,7 +139,7 @@ describe( 'reducer', () => { } ); it( 'should track site post query request fetching', () => { - const state = siteQueries( null, { + const state = siteQueries( undefined, { type: POSTS_REQUEST, siteId: 2916284, query: { search: 'Hello' } @@ -154,7 +155,7 @@ describe( 'reducer', () => { } ); it( 'should preserve previous query results when requesting again', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': { fetching: false, @@ -179,7 +180,7 @@ describe( 'reducer', () => { } ); it( 'should accumulate site queries', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': { fetching: true @@ -205,7 +206,7 @@ describe( 'reducer', () => { } ); it( 'should track site post query request success', () => { - const state = siteQueries( null, { + const state = siteQueries( undefined, { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, query: { search: 'Hello' }, @@ -226,7 +227,7 @@ describe( 'reducer', () => { } ); it( 'should track site post query request failure', () => { - const state = siteQueries( null, { + const state = siteQueries( undefined, { type: POSTS_REQUEST_FAILURE, siteId: 2916284, query: { search: 'Hello' }, @@ -243,7 +244,7 @@ describe( 'reducer', () => { } ); it( 'never persists state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': { fetching: true @@ -255,7 +256,7 @@ describe( 'reducer', () => { } ); it( 'never loads persisted state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': { fetching: true @@ -275,7 +276,7 @@ describe( 'reducer', () => { } ); it( 'should track site post query request success last page', () => { - const state = siteQueriesLastPage( null, { + const state = siteQueriesLastPage( undefined, { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, query: { search: '', number: 1 }, @@ -293,7 +294,7 @@ describe( 'reducer', () => { } ); it( 'should track last page regardless of page param', () => { - const state = siteQueriesLastPage( null, { + const state = siteQueriesLastPage( undefined, { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, query: { search: '', number: 1, page: 2 }, @@ -311,7 +312,7 @@ describe( 'reducer', () => { } ); it( 'should consider no results as having last page of 1', () => { - const state = siteQueriesLastPage( null, { + const state = siteQueriesLastPage( undefined, { type: POSTS_REQUEST_SUCCESS, siteId: 2916284, query: { search: 'none', number: 1 }, @@ -327,7 +328,7 @@ describe( 'reducer', () => { } ); it( 'should accumulate site post request success', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': 1 } @@ -351,7 +352,7 @@ describe( 'reducer', () => { } ); it( 'never persists state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': 1 } @@ -361,7 +362,7 @@ describe( 'reducer', () => { } ); it( 'never loads persisted state because this is not implemented', () => { - const original = Object.freeze( { + const original = deepFreeze( { 2916284: { '{"search":"hello"}': 1 } @@ -393,7 +394,7 @@ describe( 'reducer', () => { } ); it( 'should accumulate mappings', () => { - const state = siteRequests( Object.freeze( { + const state = siteRequests( deepFreeze( { 2916284: { 841: true } @@ -412,7 +413,7 @@ describe( 'reducer', () => { } ); it( 'should map site ID, post ID to false value if request finishes successfully', () => { - const state = siteRequests( Object.freeze( { + const state = siteRequests( deepFreeze( { 2916284: { 841: true } @@ -430,7 +431,7 @@ describe( 'reducer', () => { } ); it( 'should map site ID, post ID to false value if request finishes with failure', () => { - const state = siteRequests( Object.freeze( { + const state = siteRequests( deepFreeze( { 2916284: { 841: true } @@ -448,7 +449,7 @@ describe( 'reducer', () => { } ); it( 'never persists state because this is not implemented', () => { - const state = siteRequests( Object.freeze( { + const state = siteRequests( deepFreeze( { 2916284: { 841: true } @@ -460,7 +461,7 @@ describe( 'reducer', () => { } ); it( 'never loads persisted state because this is not implemented', () => { - const state = siteRequests( Object.freeze( { + const state = siteRequests( deepFreeze( { 2916284: { 841: true }