describe( 'noticesMiddleware()', () => { let store; useSandbox( ( sandbox ) => { store = createStore( () => 'Hello' ); sandbox.spy( store, 'dispatch' ); } ); before( () => { handlers.DUMMY_TYPE = ( dispatch, action, getState ) => { dispatch( successNotice( `${ getState() } ${ action.target }` ) ); }; } ); after( () => { delete handlers.DUMMY_TYPE; } ); it( 'should trigger the observer corresponding to the dispatched action type', () => { noticesMiddleware( store )( noop )( { type: 'DUMMY_TYPE', target: 'World' } ); expect( store.dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { text: 'Hello World' } } ); } ); } );
describe( 'SearchUrl', () => { let onSearch, onReplace, onPage; useSandbox( sandbox => { onSearch = sandbox.stub(); onReplace = sandbox.stub( page, 'replace' ); onPage = sandbox.stub( page, 'show' ); } ); test( 'should call onSearch if provided', () => { searchUrl( SEARCH_KEYWORD, '', onSearch ); expect( onSearch ).to.have.been.calledWith( SEARCH_KEYWORD ); } ); test( 'should replace existing search keyword', () => { global.window = { location: { href: 'http://example.com/cat' } }; searchUrl( SEARCH_KEYWORD, 'existing' ); expect( onReplace ).to.have.been.calledWith( '/cat?s=' + SEARCH_KEYWORD ); } ); test( 'should set page URL if no existing keyword', () => { global.window = { location: { href: 'http://example.com/cat' } }; searchUrl( SEARCH_KEYWORD ); expect( onPage ).to.have.been.calledWith( '/cat?s=' + SEARCH_KEYWORD ); } ); } );
describe( 'wrap-es6-functions', () => { function assertCall( obj, args, key ) { test( key, () => { if ( isFunction( obj[ key ] ) ) { obj[ key ].apply( obj, args ); assert( console.error.calledOnce ); // eslint-disable-line no-console } } ); } useSandbox( sandbox => { sandbox.stub( console, 'error' ); fromPairs( [ [ Array, [ 'keys', 'entries', 'values', 'findIndex', 'fill', 'find', 'includes' ] ], [ String, [ 'codePointAt', 'normalize', 'repeat', 'startsWith', 'endsWith', 'includes' ] ], ], ( object, keys ) => { keys.forEach( key => { if ( isFunction( object.prototype[ key ] ) ) { sandbox.spy( object.prototype, key ); } } ); } ); require( '../' )(); } ); describe( 'Array', () => { [ 'keys', 'entries', 'values', 'includes' ].forEach( partial( assertCall, Array.prototype, [] ) ); [ 'findIndex', 'find' ].forEach( partial( assertCall, Array.prototype, [ () => true ] ) ); [ 'fill' ].forEach( partial( assertCall, Array.prototype, [ 1 ] ) ); } ); describe( 'String', () => { [ 'codePointAt', 'repeat' ].forEach( partial( assertCall, 'hello', [ 1 ] ) ); [ 'startsWith', 'endsWith', 'includes' ].forEach( partial( assertCall, 'hello', [ 'a' ] ) ); [ 'normalize' ].forEach( partial( assertCall, 'hello', [] ) ); } ); } );
context( 'utility', () => { let dispatch; useSandbox( ( sandbox ) => { dispatch = sandbox.spy(); } ); describe( 'dispatchSuccess()', () => { it( 'should return a function which upon being called dispatches the specified success message', () => { dispatchSuccess( 'Success!' )( dispatch ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-success', text: 'Success!' } } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => { sandbox.stub( console, 'warn' ); } ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'id', 'currencyCode', 'capabilities' ] ); } ); describe( '#id()', () => { it( 'should default to null', () => { const state = id( undefined, {} ); expect( state ).to.be.null; } ); it( 'should set the current user ID', () => { const state = id( null, { type: CURRENT_USER_ID_SET, userId: 73705554 } ); expect( state ).to.equal( 73705554 ); } ); it( 'should validate ID is positive', () => { const state = id( -1, { type: DESERIALIZE } ); expect( state ).to.equal( null ); } ); it( 'should validate ID is a number', () => { const state = id( 'foobar', { type: DESERIALIZE } ); expect( state ).to.equal( null ); } ); it( 'returns valid ID', () => { const state = id( 73705554, { type: DESERIALIZE } ); expect( state ).to.equal( 73705554 ); } ); it( 'will SERIALIZE current user', () => { const state = id( 73705554, { type: SERIALIZE } ); expect( state ).to.equal( 73705554 ); } ); } ); describe( '#currencyCode()', () => { it( 'should default to null', () => { const state = currencyCode( undefined, {} ); expect( state ).to.equal( null ); } ); it( 'should set currency code when plans are received', () => { const state = currencyCode( undefined, { type: PLANS_RECEIVE, plans: [ { product_id: 1001, currency_code: 'USD' } ] } ); expect( state ).to.equal( 'USD' ); } ); it( 'should return current state when we have empty plans', () => { const state = currencyCode( 'USD', { type: PLANS_RECEIVE, plans: [] } ); expect( state ).to.equal( 'USD' ); } ); it( 'should update current state when we receive new plans', () => { const state = currencyCode( 'USD', { type: PLANS_RECEIVE, plans: [ { product_id: 1001, currency_code: 'EUR' } ] } ); expect( state ).to.equal( 'EUR' ); } ); it( 'should return current state when we have empty site plans', () => { const state = currencyCode( 'USD', { type: SITE_PLANS_FETCH_COMPLETED, plans: [] } ); expect( state ).to.equal( 'USD' ); } ); it( 'should set currency code when site plans are received', () => { const state = currencyCode( undefined, { type: SITE_PLANS_FETCH_COMPLETED, plans: [ { productName: 'Free', currencyCode: 'USD' } ] } ); expect( state ).to.equal( 'USD' ); } ); it( 'should update currency code when site plans are received', () => { const state = currencyCode( 'USD', { type: SITE_PLANS_FETCH_COMPLETED, plans: [ { productName: 'Free', currencyCode: 'CAD' } ] } ); expect( state ).to.equal( 'CAD' ); } ); it( 'should persist state', () => { const original = 'JPY'; const state = currencyCode( original, { type: SERIALIZE } ); expect( state ).to.equal( original ); } ); it( 'should restore valid persisted state', () => { const original = 'JPY'; const state = currencyCode( original, { type: DESERIALIZE } ); expect( state ).to.equal( original ); } ); it( 'should ignore invalid persisted state', () => { const original = 1234; const state = currencyCode( original, { type: DESERIALIZE } ); expect( state ).to.equal( null ); } ); } ); describe( 'capabilities()', () => { it( 'should default to an empty object', () => { const state = capabilities( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should track capabilities by single received site', () => { const state = capabilities( undefined, { type: SITE_RECEIVE, site: { ID: 2916284, capabilities: { manage_options: false } } } ); expect( state ).to.eql( { 2916284: { manage_options: false } } ); } ); it( 'should accumulate capabilities by received site', () => { const original = deepFreeze( { 2916284: { manage_options: false } } ); const state = capabilities( original, { type: SITE_RECEIVE, site: { ID: 77203074, capabilities: { manage_options: true } } } ); expect( state ).to.eql( { 2916284: { manage_options: false }, 77203074: { manage_options: true } } ); } ); it( 'should ignore received site if missing capabilities', () => { const state = capabilities( undefined, { type: SITE_RECEIVE, site: { ID: 2916284 } } ); expect( state ).to.eql( {} ); } ); it( 'should track capabilities by multiple received sites', () => { const state = capabilities( undefined, { type: SITES_RECEIVE, sites: [ { ID: 2916284, capabilities: { manage_options: false } } ] } ); expect( state ).to.eql( { 2916284: { manage_options: false } } ); } ); it( 'should ignore received sites if missing capabilities', () => { const state = capabilities( undefined, { type: SITES_RECEIVE, sites: [ { ID: 2916284 } ] } ); expect( state ).to.eql( {} ); } ); it( 'should persist state', () => { const original = deepFreeze( { 2916284: { manage_options: false } } ); const state = capabilities( original, { type: SERIALIZE } ); expect( state ).to.equal( original ); } ); it( 'should restore valid persisted state', () => { const original = deepFreeze( { 2916284: { manage_options: false } } ); const state = capabilities( original, { type: DESERIALIZE } ); expect( state ).to.equal( original ); } ); it( 'should not restore invalid persisted state', () => { const original = deepFreeze( { BAD2916284: { manage_options: false } } ); const state = capabilities( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'actions', () => { let sandbox, spy; useSandbox( newSandbox => { sandbox = newSandbox; spy = sandbox.spy(); } ); describe( '#receiveMediaStorage()', () => { it( 'should return an action object', () => { const siteId = 2916284; const mediaStorage = { max_storage_bytes: -1, storage_used_bytes: -1 }; const action = receiveMediaStorage( mediaStorage, 2916284 ); expect( action ).to.eql( { type: SITE_MEDIA_STORAGE_RECEIVE, siteId, mediaStorage } ); } ); } ); describe( '#receiveMediaStorage()', () => { before( () => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/sites/2916284/media-storage' ) .reply( 200, { max_storage_bytes: 3221225472, storage_used_bytes: 323506 } ) .get( '/rest/v1.1/sites/77203074/media-storage' ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access media-storage.' } ); } ); after( () => { nock.cleanAll(); } ); it( 'should dispatch fetch action when thunk triggered', () => { requestMediaStorage( 2916284 )( spy ); expect( spy ).to.have.been.calledWith( { type: SITE_MEDIA_STORAGE_REQUEST, siteId: 2916284 } ); } ); it( 'should dispatch receive action when request completes', () => { return requestMediaStorage( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: SITE_MEDIA_STORAGE_RECEIVE, mediaStorage: { max_storage_bytes: 3221225472, storage_used_bytes: 323506 }, siteId: 2916284 } ); } ); } ); it( 'should dispatch success action when request completes', () => { return requestMediaStorage( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: SITE_MEDIA_STORAGE_REQUEST_SUCCESS, siteId: 2916284 } ); } ); } ); it( 'should dispatch fail action when request fails', () => { return requestMediaStorage( 77203074 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: SITE_MEDIA_STORAGE_REQUEST_FAILURE, siteId: 77203074, error: sandbox.match( { message: 'An active access token must be used to access media-storage.' } ) } ); } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => { sandbox.stub( console, 'warn' ); } ); it( 'should export expected reducer keys', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'jetpackConnectSite', 'jetpackConnectAuthorize', 'jetpackConnectSessions', 'jetpackSSO', 'jetpackSSOSessions' ] ); } ); describe( '#jetpackConnectSessions()', () => { it( 'should default to an empty object', () => { const state = jetpackConnectSessions( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should add the url slug as a new property when checking a new url', () => { const state = jetpackConnectSessions( undefined, { type: JETPACK_CONNECT_CHECK_URL, url: 'https://website.com' } ); expect( state ).to.have.property( 'website.com' ).to.be.a( 'object' ); } ); it( 'should store a timestamp when checking a new url', () => { const nowTime = ( new Date() ).getTime(); const state = jetpackConnectSessions( undefined, { type: JETPACK_CONNECT_CHECK_URL, url: 'https://website.com' } ); expect( state[ 'website.com' ] ).to.have.property( 'timestamp' ) .to.be.at.least( nowTime ); } ); it( 'should update the timestamp when checking an existent url', () => { const nowTime = ( new Date() ).getTime(); const state = jetpackConnectSessions( { 'website.com': { timestamp: 1 } }, { type: JETPACK_CONNECT_CHECK_URL, url: 'https://website.com' } ); expect( state[ 'website.com' ] ).to.have.property( 'timestamp' ) .to.be.at.least( nowTime ); } ); it( 'should not restore a state with a property without a timestamp', () => { const state = jetpackConnectSessions( { 'website.com': {} }, { type: DESERIALIZE } ); expect( state ).to.be.eql( {} ); } ); it( 'should not restore a state with a property with a non-integer timestamp', () => { const state = jetpackConnectSessions( { 'website.com': { timestamp: '1' } }, { type: DESERIALIZE } ); expect( state ).to.be.eql( {} ); } ); it( 'should not restore a state with a session stored with extra properties', () => { const state = jetpackConnectSessions( { 'website.com': { timestamp: 1, foo: 'bar' } }, { type: DESERIALIZE } ); expect( state ).to.be.eql( {} ); } ); it( 'should restore a valid state', () => { const state = jetpackConnectSessions( { 'website.com': { timestamp: 1 } }, { type: DESERIALIZE } ); expect( state ).to.be.eql( { 'website.com': { timestamp: 1 } } ); } ); it( 'should restore a valid state including dashes, slashes and semicolons', () => { const state = jetpackConnectSessions( { 'https://website.com:3000/test-one': { timestamp: 1 } }, { type: DESERIALIZE } ); expect( state ).to.be.eql( { 'https://website.com:3000/test-one': { timestamp: 1 } } ); } ); } ); describe( '#jetpackSSOSessions()', () => { it( 'should default to an empty object', () => { const state = jetpackConnectAuthorize( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should store an integer timestamp when creating new session', () => { const nowTime = ( new Date() ).getTime(); const state = jetpackSSOSessions( undefined, { type: JETPACK_CONNECT_SSO_AUTHORIZE_SUCCESS, ssoUrl: 'https://website.com?action=jetpack-sso&result=success&sso_nonce={$nonce}&user_id={$user_id}' } ); expect( state ).to.have.property( 'website.com' ) .to.be.a( 'object' ); expect( state[ 'website.com' ] ).to.have.property( 'timestamp' ) .to.be.at.least( nowTime ); } ); } ); describe( '#jetpackConnectAuthorize()', () => { it( 'should default to an empty object', () => { const state = jetpackConnectAuthorize( undefined, {} ); expect( state ).to.eql( {} ); } ); } ); describe( '#jetpackSSO()', () => { it( 'should default to an empty object', () => { const state = jetpackSSO( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should not persist state', () => { const original = deepFreeze( { isAuthorizing: false, site_id: 0, authorizationError: false, ssoUrl: 'http://website.com' } ); const state = jetpackSSO( original, { type: SERIALIZE } ); expect( state ).to.eql( {} ); } ); it( 'should set isValidating to true when validating', () => { const state = jetpackSSO( undefined, { type: JETPACK_CONNECT_SSO_VALIDATION_REQUEST } ); expect( state ).to.have.property( 'isValidating', true ); } ); it( 'should set isAuthorizing to true when authorizing', () => { const state = jetpackSSO( undefined, { type: JETPACK_CONNECT_SSO_AUTHORIZE_REQUEST } ); expect( state ).to.have.property( 'isAuthorizing', true ); } ); it( 'should set isValidating to false after validation', () => { const actions = [ successfulSSOValidation, { type: JETPACK_CONNECT_SSO_VALIDATION_ERROR, error: { statusCode: 400 } } ]; actions.forEach( ( action ) => { const state = jetpackSSO( undefined, action ); expect( state ).to.have.property( 'isValidating', false ); } ); } ); it( 'should store boolean nonceValid after validation', () => { const actions = [ successfulSSOValidation, falseSSOValidation ]; actions.forEach( ( action ) => { const originalAction = deepFreeze( action ); const state = jetpackSSO( undefined, originalAction ); expect( state ).to.have.property( 'nonceValid', originalAction.success ); } ); } ); it( 'should store blog details after successful validation', () => { const successState = jetpackSSO( undefined, successfulSSOValidation ); expect( successState ).to.have.property( 'blogDetails' ) .to.be.eql( successfulSSOValidation.blogDetails ); } ); it( 'should store empty object in blog_details when validation is false', () => { const failedState = jetpackSSO( undefined, falseSSOValidation ); expect( failedState ).to.have.property( 'blogDetails' ) .to.be.eql( {} ); } ); it( 'should set isAuthorizing to false after authorization', () => { const actions = [ { type: JETPACK_CONNECT_SSO_AUTHORIZE_SUCCESS, ssoUrl: 'http://website.com' }, { type: JETPACK_CONNECT_SSO_AUTHORIZE_ERROR, error: { statusCode: 400 }, } ]; actions.forEach( ( action ) => { const state = jetpackSSO( undefined, action ); expect( state ).to.have.property( 'isAuthorizing', false ); } ); } ); it( 'should store sso_url after authorization', () => { const action = deepFreeze( { type: JETPACK_CONNECT_SSO_AUTHORIZE_SUCCESS, ssoUrl: 'http://website.com' } ); const state = jetpackSSO( undefined, action ); expect( state ).to.have.property( 'ssoUrl', action.sso_url ); } ); } ); } );
describe( 'actions', () => { let spy; useSandbox( sandbox => ( spy = sandbox.spy() ) ); const siteId = 123456; const failedSiteId = 456789; const plugins = { data: { is_cache_enabled: true, is_super_cache_enabled: true, }, }; describe( '#receivePlugins()', () => { test( 'should return an action object', () => { const action = receivePlugins( siteId, plugins.data ); expect( action ).to.eql( { type: WP_SUPER_CACHE_RECEIVE_PLUGINS, plugins: plugins.data, siteId, } ); } ); } ); describe( '#requestPlugins()', () => { useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .get( `/rest/v1.1/jetpack-blogs/${ siteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/plugins' } ) .reply( 200, plugins ) .get( `/rest/v1.1/jetpack-blogs/${ failedSiteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/plugins' } ) .reply( 403, { error: 'authorization_required', message: 'User cannot access this private blog.', } ); } ); test( 'should dispatch fetch action when thunk triggered', () => { requestPlugins( siteId )( spy ); expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_REQUEST_PLUGINS, siteId, } ); } ); test( 'should dispatch receive action when request completes', () => { return requestPlugins( siteId )( spy ).then( () => { expect( spy ).to.have.been.calledWith( receivePlugins( siteId, plugins.data ) ); } ); } ); test( 'should dispatch request success action when request completes', () => { return requestPlugins( siteId )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_REQUEST_PLUGINS_SUCCESS, siteId, } ); } ); } ); test( 'should dispatch fail action when request fails', () => { return requestPlugins( failedSiteId )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_REQUEST_PLUGINS_FAILURE, siteId: failedSiteId, error: sinon.match( { message: 'User cannot access this private blog.' } ), } ); } ); } ); } ); describe( 'togglePlugin()', () => { const plugin = 'no_adverts_for_friends'; const apiResponse = { data: { updated: true, }, }; useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .post( `/rest/v1.1/jetpack-blogs/${ siteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/plugins' } ) .reply( 200, apiResponse ) .post( `/rest/v1.1/jetpack-blogs/${ failedSiteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/plugins' } ) .reply( 403, { error: 'authorization_required', message: 'User cannot access this private blog.', } ); } ); test( 'should dispatch toggle action when thunk triggered', () => { togglePlugin( siteId, plugin )( spy ); expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_TOGGLE_PLUGIN, siteId, plugin, } ); } ); test( 'should dispatch receive action when request completes', () => { return togglePlugin( siteId, plugin )( spy ).then( () => { expect( spy ).to.have.been.calledWith( receivePlugins( siteId, apiResponse.data ) ); } ); } ); test( 'should dispatch save success action when request completes', () => { return togglePlugin( siteId, plugin )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_TOGGLE_PLUGIN_SUCCESS, plugin, siteId, } ); } ); } ); test( 'should dispatch fail action when request fails', () => { return togglePlugin( failedSiteId, plugin )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_TOGGLE_PLUGIN_FAILURE, siteId: failedSiteId, plugin, error: sinon.match( { message: 'User cannot access this private blog.' } ), } ); } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( sandbox => { sandbox.stub( console, 'warn' ); } ); test( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'requesting', 'items' ] ); } ); describe( '#requesting()', () => { test( 'should default to an empty object', () => { const state = requesting( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should set shortcode of that site ID to true value if a request is initiated', () => { const state = requesting( {}, { type: SHORTCODE_REQUEST, siteId: 12345678, shortcode: 'test_shortcode', } ); expect( state ).to.eql( { 12345678: { test_shortcode: true, }, } ); } ); test( 'should store the requested site IDs faultlessly if the site previously had no shortcodes', () => { const state = requesting( deepFreeze( { 12345678: {}, } ), { type: SHORTCODE_REQUEST, siteId: 12345678, shortcode: 'another_shortcode', } ); expect( state ).to.eql( { 12345678: { another_shortcode: true, }, } ); } ); test( 'should accumulate the requested site IDs', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: true, }, } ), { type: SHORTCODE_REQUEST, siteId: 87654321, shortcode: 'another_shortcode', } ); expect( state ).to.eql( { 12345678: { test_shortcode: true, }, 87654321: { another_shortcode: true, }, } ); } ); test( 'should accumulate the requested shortcodes of given site IDs', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: false, }, 87654321: { another_shortcode: true, }, } ), { type: SHORTCODE_REQUEST, siteId: 12345678, shortcode: 'some_shortcode', } ); expect( state ).to.eql( { 12345678: { test_shortcode: false, some_shortcode: true, }, 87654321: { another_shortcode: true, }, } ); } ); test( 'should set shortcode of that site ID to false if request finishes successfully', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: true, another_shortcode: true, }, 87654321: { test_shortcode: true, }, } ), { type: SHORTCODE_REQUEST_SUCCESS, siteId: 12345678, shortcode: 'test_shortcode', } ); expect( state ).to.eql( { 12345678: { test_shortcode: false, another_shortcode: true, }, 87654321: { test_shortcode: true, }, } ); } ); test( 'should set shortcode of that site ID to false if request finishes unsuccessfully', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: true, another_shortcode: true, }, 87654321: { test_shortcode: true, }, } ), { type: SHORTCODE_REQUEST_FAILURE, siteId: 12345678, shortcode: 'test_shortcode', error: 'The requested shortcode does not exist.', } ); expect( state ).to.eql( { 12345678: { test_shortcode: false, another_shortcode: true, }, 87654321: { test_shortcode: true, }, } ); } ); test( 'should not persist state', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: true, }, } ), { type: SERIALIZE, } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const state = requesting( deepFreeze( { 12345678: { test_shortcode: true, }, } ), { type: DESERIALIZE, } ); expect( state ).to.eql( {} ); } ); } ); describe( '#items()', () => { const shortcodeData = { result: '<html></html>', shortcode: '[gallery ids="1,2,3"]', scripts: {}, styles: {}, }; test( 'should default to an empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should index shortcodes by site ID', () => { const state = items( {}, { type: SHORTCODE_RECEIVE, siteId: 12345678, shortcode: 'test_shortcode', data: shortcodeData, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, } ); } ); test( 'should index shortcodes by site ID faultlessly if the site previously had no shortcodes', () => { const state = items( { 12345678: {}, }, { type: SHORTCODE_RECEIVE, siteId: 12345678, shortcode: 'test_shortcode', data: shortcodeData, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, } ); } ); test( 'should accumulate sites', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, } ), { type: SHORTCODE_RECEIVE, siteId: 87654321, shortcode: 'test_shortcode', data: { ...shortcodeData, result: '<html></html>' }, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ); } ); test( 'should accumulate shortcodes in sites', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: SHORTCODE_RECEIVE, siteId: 12345678, shortcode: 'another_shortcode', data: { ...shortcodeData, result: '<html><head></head></html>' }, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, another_shortcode: { ...shortcodeData, result: '<html><head></head></html>' }, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ); } ); test( 'should override previous shortcodes of same site ID', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: SHORTCODE_RECEIVE, siteId: 87654321, shortcode: 'test_shortcode', data: shortcodeData, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: shortcodeData, }, } ); } ); test( 'should forget gallery shortcodes when receiving a MEDIA ITEM and the id matches', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: 'FLUX_RECEIVE_MEDIA_ITEM', siteId: 87654321, data: { ID: 1, }, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: {}, } ); } ); test( 'should forget gallery shortcodes when receiving MEDIA ITEMS the ids match', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: 'FLUX_RECEIVE_MEDIA_ITEMS', siteId: 87654321, data: { media: [ { ID: 1 }, { ID: 2 } ], }, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: {}, } ); } ); test( 'should persist state', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: SERIALIZE, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ); } ); test( 'should load valid persisted state', () => { const state = items( deepFreeze( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ), { type: DESERIALIZE, } ); expect( state ).to.eql( { 12345678: { test_shortcode: shortcodeData, }, 87654321: { test_shortcode: { ...shortcodeData, result: '<html></html>' }, }, } ); } ); test( 'should not load invalid persisted state', () => { const state = items( deepFreeze( { 1234567: 'test_shortcode', } ), { type: DESERIALIZE, } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'actions', () => { let spy; useSandbox( ( sandbox ) => spy = sandbox.spy() ); describe( '#receiveCountryStates()', () => { it( 'should return an action object', () => { const action = receiveCountryStates( [ { code: 'AK', name: 'Alaska' }, { code: 'AS', name: 'American Samoa' } ], 'US' ); expect( action ).to.eql( { type: COUNTRY_STATES_RECEIVE, countryCode: 'us', countryStates: [ { code: 'AK', name: 'Alaska' }, { code: 'AS', name: 'American Samoa' } ] } ); } ); } ); describe( '#requestCountryStates()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/domains/supported-states/us' ) .reply( 200, [ { code: 'AK', name: 'Alaska' }, { code: 'AS', name: 'American Samoa' } ] ) .get( '/rest/v1.1/domains/supported-states/ca' ) .reply( 500, { error: 'server_error', message: 'A server error occurred', } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { requestCountryStates( 'us' )( spy ); expect( spy ).to.have.been.calledWith( { type: COUNTRY_STATES_REQUEST, countryCode: 'us' } ); } ); it( 'should dispatch country states receive action when request completes', () => { return requestCountryStates( 'us' )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: COUNTRY_STATES_RECEIVE, countryCode: 'us', countryStates: [ { code: 'AK', name: 'Alaska' }, { code: 'AS', name: 'American Samoa' } ] } ); } ); } ); it( 'should dispatch country states request success action when request completes', () => { return requestCountryStates( 'us' )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: COUNTRY_STATES_REQUEST_SUCCESS, countryCode: 'us' } ); } ); } ); it( 'should dispatch fail action when request fails', () => { return requestCountryStates( 'ca' )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: COUNTRY_STATES_REQUEST_FAILURE, countryCode: 'ca', error: sinon.match( { message: 'A server error occurred' } ) } ); } ); } ); } ); } );
describe( 'reducer', () => { let sandbox; useSandbox( newSandbox => { sandbox = newSandbox; // mute off console warn sandbox.stub( console, 'warn' ); } ); test( 'should export expected reducer keys', () => { expect( plansReducer( undefined, {} ) ).to.have.keys( [ 'items', 'requesting', 'error' ] ); } ); describe( '#items()', () => { test( 'should default to an empty Array', () => { expect( itemsReducer( undefined, [] ) ).to.eql( [] ); } ); test( 'should index items state', () => { const initialState = undefined; const plans = WPCOM_RESPONSE; const action = plansReceiveAction( plans ); const expectedState = plans; deepFreeze( expectedState ); const newState = itemsReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should override plans', () => { const plans = WPCOM_RESPONSE; const initialState = plans; const action = plansReceiveAction( plans ); const expectedState = plans; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = itemsReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should persist state', () => { const plans = WPCOM_RESPONSE; const initialState = plans; const action = { type: 'SERIALIZE' }; const expectedState = plans; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = itemsReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should load persisted state', () => { const plans = WPCOM_RESPONSE; const initialState = plans; const action = { type: 'DESERIALIZE' }; const expectedState = plans; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = itemsReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should not load invalid persisted state', () => { // product_id should be `Number` const plans = [ { product_id: '234234' } ]; const initialState = plans; const action = { type: 'DESERIALIZE' }; deepFreeze( initialState ); const expectedState = []; deepFreeze( expectedState ); const newState = itemsReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); } ); describe( '#requesting()', () => { test( 'should return FALSE when initial state is undefined and action is unknown', () => { expect( requestReducer( undefined, {} ) ).to.eql( false ); } ); test( 'should return TRUE when initial state is undefined and action is REQUEST', () => { const initialState = undefined; const action = requestPlans(); const expectedState = true; deepFreeze( expectedState ); const newState = requestReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should update `requesting` state on SUCCESS', () => { const initialState = true; const action = plansRequestSuccessAction(); const expectedState = false; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = requestReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should update `requesting` state on FAILURE', () => { const initialState = true; const action = plansRequestFailureAction(); const expectedState = false; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = requestReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); } ); describe( '#errors()', () => { test( 'should return FALSE when initial state is undefined and action is unknown', () => { expect( errorReducer( undefined, {} ) ).to.eql( false ); } ); test( 'should set `error` state to TRUE on FAILURE', () => { const initialState = undefined; const action = plansRequestFailureAction(); const expectedState = true; deepFreeze( expectedState ); const newState = errorReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should set `error` state to FALSE on REQUEST', () => { const initialState = true; const action = requestPlans(); const expectedState = false; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = errorReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); test( 'should set `error` state to FALSE on SUCCESS', () => { const initialState = true; const action = plansRequestSuccessAction(); const expectedState = false; deepFreeze( initialState ); deepFreeze( expectedState ); const newState = errorReducer( initialState, action ); expect( newState ).to.eql( expectedState ); } ); } ); } );
describe( 'reducer', () => { useSandbox( sandbox => { sandbox.stub( console, 'warn' ); } ); test( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'requests', 'items' ] ); } ); describe( 'requests()', () => { test( 'should default to an empty object', () => { const state = requests( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should track stats list request fetching', () => { const state = requests( undefined, { type: SITE_STATS_REQUEST, siteId: 2916284, statType: 'statsStreak', query: streakQuery, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-06-01"],["startDate","2015-06-01"]]': { requesting: true, status: 'pending', }, }, }, } ); } ); test( 'should accumulate queries', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': { requesting: true, status: 'pending', }, }, }, } ); const state = requests( original, { type: SITE_STATS_REQUEST, siteId: 2916284, statType: 'statsStreak', query: streakQueryDos, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': { requesting: true, status: 'pending', }, '[["endDate","2015-06-01"],["startDate","2014-06-01"]]': { requesting: true, status: 'pending', }, }, }, } ); } ); test( 'should track stats request success', () => { const today = new Date(); const state = requests( undefined, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsStreak', query: streakQuery, data: streakResponse, date: today, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-06-01"],["startDate","2015-06-01"]]': { requesting: false, status: 'success', date: today, }, }, }, } ); } ); test( 'should track stats request failure', () => { const state = requests( undefined, { type: SITE_STATS_REQUEST_FAILURE, siteId: 2916284, statType: 'statsStreak', query: streakQuery, error: new Error(), } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-06-01"],["startDate","2015-06-01"]]': { requesting: false, status: 'error', }, }, }, } ); } ); test( 'should not persist state', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': { requesting: true, status: 'pending', }, }, }, } ); const state = requests( original, { type: SERIALIZE } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': { requesting: true, status: 'pending', }, }, }, } ); const state = requests( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); describe( 'items()', () => { test( 'should persist state', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SERIALIZE } ); expect( state ).to.eql( original ); } ); test( 'should load valid persisted state', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( original ); } ); test( 'should not load invalid persisted state', () => { const original = deepFreeze( { 2916284: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); test( 'should default to an empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should add received stats', () => { const state = items( undefined, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsStreak', query: streakQuery, data: streakResponse, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-06-01"],["startDate","2015-06-01"]]': streakResponse, }, }, } ); } ); test( 'should accumulate received stats by statType', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsStreak', query: streakQueryDos, data: streakResponseDos, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, '[["endDate","2015-06-01"],["startDate","2014-06-01"]]': streakResponseDos, }, }, } ); } ); test( 'should change root site property when received stats by statType', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsStreak', query: streakQueryDos, data: streakResponseDos, } ); expect( state[ 2916284 ] ).to.not.equal( original[ 2916284 ] ); } ); test( 'should add additional statTypes', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsCountryViews', query: streakQuery, data: {}, } ); expect( state ).to.eql( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, statsCountryViews: { '[["endDate","2016-06-01"],["startDate","2015-06-01"]]': {}, }, }, } ); } ); test( 'should should not change another statTypes property', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SITE_STATS_RECEIVE, siteId: 2916284, statType: 'statsCountryViews', query: streakQuery, data: {}, } ); expect( state[ 2916284 ].statsStreak ).to.equal( original[ 2916284 ].statsStreak ); } ); test( 'should not change another site property', () => { const original = deepFreeze( { 2916284: { statsStreak: { '[["endDate","2016-07-01"],["startDate","2016-06-01"]]': streakResponse, }, }, } ); const state = items( original, { type: SITE_STATS_RECEIVE, siteId: 3916284, statType: 'statsCountryViews', query: streakQuery, data: {}, } ); expect( state[ 2916284 ].statsStreak ).to.equal( original[ 2916284 ].statsStreak ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => { sandbox.stub( console, 'warn' ); } ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'requesting', 'items' ] ); } ); describe( '#requesting()', () => { it( 'should default to an false', () => { const state = requesting( undefined, {} ); expect( state ).to.be.false; } ); it( 'should set requesting to true value if a request is initiated', () => { const state = requesting( undefined, { type: BILLING_TRANSACTIONS_REQUEST, } ); expect( state ).to.be.true; } ); it( 'should set requesting to false if request finishes successfully', () => { const state = requesting( true, { type: BILLING_TRANSACTIONS_REQUEST_SUCCESS, } ); expect( state ).to.eql( false ); } ); it( 'should set requesting to false if request finishes unsuccessfully', () => { const state = requesting( true, { type: BILLING_TRANSACTIONS_REQUEST_FAILURE, } ); expect( state ).to.eql( false ); } ); it( 'should not persist state', () => { const state = requesting( true, { type: SERIALIZE } ); expect( state ).to.eql( false ); } ); it( 'should not load persisted state', () => { const state = requesting( true, { type: DESERIALIZE } ); expect( state ).to.eql( false ); } ); } ); describe( '#items()', () => { const billingTransactions = { past: [ { id: '12345678', amount: '$1.23', } ], upcoming: [ { id: '87654321', amount: '$4.56', } ] }; it( 'should default to empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should store the billing transactions properly', () => { const state = items( null, { type: BILLING_TRANSACTIONS_RECEIVE, ...billingTransactions } ); expect( state ).to.eql( billingTransactions ); } ); it( 'should override previous billing transactions', () => { const state = items( deepFreeze( { past: [ { id: '11223344', amount: '$3.43', desc: 'test' } ], upcoming: [ { id: '88776655', amount: '$1.11', product: 'example' } ] } ), { type: BILLING_TRANSACTIONS_RECEIVE, ...billingTransactions } ); expect( state ).to.eql( billingTransactions ); } ); it( 'should persist state', () => { const state = items( deepFreeze( billingTransactions ), { type: SERIALIZE } ); expect( state ).to.eql( billingTransactions ); } ); it( 'should load valid persisted state', () => { const state = items( deepFreeze( billingTransactions ), { type: DESERIALIZE } ); expect( state ).to.eql( billingTransactions ); } ); it( 'should not load invalid persisted state', () => { const state = items( deepFreeze( { example: 'test' } ), { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'PreferencesActions', function() { let sandbox, PreferencesActions, getSettings, postSettings; const store = { get: noop, set: noop }; const Dispatcher = { handleViewAction: noop, handleServerAction: noop }; useSandbox( ( _sandbox ) => sandbox = _sandbox ); useMockery(); useNock(); before( function() { mockery.registerMock( 'lib/user/utils', { isLoggedIn: () => true } ); mockery.registerMock( 'store', store ); mockery.registerMock( 'dispatcher', Dispatcher ); mockery.registerMock( 'lib/wp', { undocumented: function() { return { me: function() { return { settings: function() { return { get: getSettings, update: postSettings }; } }; } }; } } ); PreferencesActions = require( '../actions' ); } ); beforeEach( function() { sandbox.restore(); sandbox.stub( store, 'get' ); sandbox.stub( store, 'set' ); sandbox.stub( Dispatcher, 'handleViewAction' ); sandbox.stub( Dispatcher, 'handleServerAction' ); getSettings = sandbox.stub().callsArgWithAsync( 0, null, { [ USER_SETTING_KEY ]: DUMMY_PERSISTED_PREFERENCES } ); postSettings = sandbox.stub().callsArgAsync( 1 ); } ); describe( '#fetch()', function() { it( 'should retrieve from localStorage and trigger a request to the REST API', function( done ) { store.get.restore(); sandbox.stub( store, 'get' ).returns( DUMMY_PERSISTED_PREFERENCES ); PreferencesActions.fetch(); expect( store.get ).to.have.been.calledWith( LOCALSTORAGE_KEY ); expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_ME_SETTINGS', data: { [ USER_SETTING_KEY ]: DUMMY_PERSISTED_PREFERENCES } } ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledTwice; expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, DUMMY_PERSISTED_PREFERENCES ); done(); } ); } ); it( 'should not persist to localStorage from remote request if error occurs', function( done ) { sandbox.stub( PreferencesActions, 'mergePreferencesToLocalStorage' ); getSettings = sandbox.stub().callsArgWithAsync( 0, true ); PreferencesActions.fetch(); process.nextTick( function() { expect( PreferencesActions.mergePreferencesToLocalStorage ).to.not.have.been.called; done(); } ); } ); it( 'should not dispatch an empty local store', function( done ) { store.get.restore(); sandbox.stub( store, 'get' ).returns( undefined ); PreferencesActions.fetch(); expect( store.get ).to.have.been.calledWith( LOCALSTORAGE_KEY ); expect( Dispatcher.handleServerAction ).to.not.have.been.called; process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledOnce; expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, DUMMY_PERSISTED_PREFERENCES ); done(); } ); } ); } ); describe( '#set()', function() { it( 'should save to localStorage and trigger a request to the REST API', function( done ) { PreferencesActions.set( 'one', 1 ); expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, { one: 1 } ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'UPDATE_ME_SETTINGS' } ); expect( postSettings ).to.have.been.calledWithMatch( JSON.stringify( { [ USER_SETTING_KEY ]: { one: 1 } } ) ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_ME_SETTINGS' } ); done(); } ); } ); it( 'should add to, not replace, existing values', function() { store.get.restore(); sandbox.stub( store, 'get' ).returns( { one: 1 } ); PreferencesActions.set( 'two', 2 ); expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, { one: 1, two: 2 } ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'UPDATE_ME_SETTINGS', data: { [ USER_SETTING_KEY ]: { two: 2 } } } ); } ); it( 'should assume a null value is to be removed', function() { PreferencesActions.set( 'one', 1 ); PreferencesActions.set( 'one', null ); expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, { one: 1 } ); expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, {} ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'UPDATE_ME_SETTINGS', data: { [ USER_SETTING_KEY ]: { one: null } } } ); } ); it( 'should only dispatch a receive after all pending updates have finished', function( done ) { PreferencesActions.set( 'one', 1 ); PreferencesActions.set( 'one', null ); process.nextTick( function() { expect( postSettings ).to.have.been.calledTwice; expect( Dispatcher.handleServerAction ).to.have.been.calledOnce; expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_ME_SETTINGS' } ); done(); } ); } ); } ); describe( '#remove()', function() { it( 'should remove from localStorage and trigger a request to the REST API', function( done ) { PreferencesActions.set( 'one', 1 ); PreferencesActions.remove( 'one' ); expect( store.set ).to.have.been.calledWith( LOCALSTORAGE_KEY, {} ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'UPDATE_ME_SETTINGS', data: { [ USER_SETTING_KEY ]: { one: 1 } } } ); expect( postSettings ).to.have.been.calledWithMatch( JSON.stringify( { [ USER_SETTING_KEY ]: { one: 1 } } ) ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_ME_SETTINGS' } ); done(); } ); } ); } ); } );
context( 'handlers', () => { let dispatch; useSandbox( ( sandbox ) => { dispatch = sandbox.spy(); } ); describe( 'onPostDeleteFailure()', () => { it( 'should dispatch error notice with truncated title if known', () => { onPostDeleteFailure( dispatch, { type: POST_DELETE_FAILURE, siteId: 2916284, postId: 841 }, () => ( { posts: { queries: { 2916284: new PostQueryManager( { items: { 841: { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World, This Should Be Truncated' } } } ) } } } ) ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-error', text: 'An error occurred while deleting "Hello World, This Sho..."' } } ); } ); it( 'should dispatch error notice with unknown title', () => { onPostDeleteFailure( dispatch, { type: POST_DELETE_FAILURE, siteId: 2916284, postId: 841 }, () => ( { posts: { queries: {} } } ) ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-error', text: 'An error occurred while deleting the post' } } ); } ); } ); describe( 'onPostRestoreFailure()', () => { it( 'should dispatch error notice with truncated title if known', () => { onPostRestoreFailure( dispatch, { type: POST_RESTORE_FAILURE, siteId: 2916284, postId: 841 }, () => ( { posts: { queries: { 2916284: new PostQueryManager( { items: { 841: { ID: 841, site_ID: 2916284, global_ID: '3d097cb7c5473c169bba0eb8e3c6cb64', title: 'Hello World, This Should Be Truncated' } } } ) } } } ) ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-error', text: 'An error occurred while restoring "Hello World, This Sho..."' } } ); } ); it( 'should dispatch error notice with unknown title', () => { onPostRestoreFailure( dispatch, { type: POST_RESTORE_FAILURE, siteId: 2916284, postId: 841 }, () => ( { posts: { queries: {} } } ) ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-error', text: 'An error occurred while restoring the post' } } ); } ); } ); describe( 'onPostSaveSuccess()', () => { it( 'should not dispatch if status has no corresponding text', () => { onPostSaveSuccess( dispatch, { type: POST_SAVE_SUCCESS, post: { title: 'Hello World', status: 'invalid' } } ); expect( dispatch ).to.not.have.been.calledWithMatch( { type: NOTICE_CREATE } ); } ); it( 'should dispatch success notice for trash', () => { onPostSaveSuccess( dispatch, { type: POST_SAVE_SUCCESS, post: { status: 'trash' } } ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-success', text: 'Post successfully moved to trash' } } ); } ); it( 'should dispatch success notice for publish', () => { onPostSaveSuccess( dispatch, { type: POST_SAVE_SUCCESS, post: { status: 'publish' } } ); expect( dispatch ).to.have.been.calledWithMatch( { type: NOTICE_CREATE, notice: { status: 'is-success', text: 'Post successfully published' } } ); } ); } ); } );
describe( 'reducer', () => { const primarySiteId = 123456; const secondarySiteId = 456789; useSandbox( sandbox => { sandbox.stub( console, 'warn' ); } ); describe( 'deleteStatus()', () => { const previousState = deepFreeze( { deleteStatus: { [ primarySiteId ]: { deleting: true, status: 'pending', }, }, } ); test( 'should default to an empty object', () => { const state = reducer( undefined, {} ); expect( state.deleteStatus ).to.eql( {} ); } ); test( 'should set request to true if request in progress', () => { const state = reducer( undefined, { type: WP_SUPER_CACHE_DELETE_CACHE, siteId: primarySiteId, } ); expect( state.deleteStatus ).to.eql( { [ primarySiteId ]: { deleting: true, status: 'pending', }, } ); } ); test( 'should accumulate requesting values', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_DELETE_CACHE, siteId: secondarySiteId, } ); expect( state.deleteStatus ).to.eql( { [ primarySiteId ]: { deleting: true, status: 'pending', }, [ secondarySiteId ]: { deleting: true, status: 'pending', }, } ); } ); test( 'should set request to false if request finishes successfully', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_DELETE_CACHE_SUCCESS, siteId: primarySiteId, } ); expect( state.deleteStatus ).to.eql( { [ primarySiteId ]: { deleting: false, status: 'success', }, } ); } ); test( 'should set request to false if request finishes with failure', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_DELETE_CACHE_FAILURE, siteId: primarySiteId, } ); expect( state.deleteStatus ).to.eql( { [ primarySiteId ]: { deleting: false, status: 'error', }, } ); } ); test( 'should not persist state', () => { const state = reducer( previousState, { type: SERIALIZE, } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const state = reducer( previousState, { type: DESERIALIZE, } ); expect( state.deleteStatus ).to.eql( {} ); } ); } ); describe( 'testing()', () => { const previousState = deepFreeze( { testing: { [ primarySiteId ]: true, }, } ); test( 'should default to an empty object', () => { const state = reducer( undefined, {} ); expect( state.testing ).to.eql( {} ); } ); test( 'should set testing value to true if request in progress', () => { const state = reducer( undefined, { type: WP_SUPER_CACHE_TEST_CACHE, siteId: primarySiteId, } ); expect( state.testing ).to.eql( { [ primarySiteId ]: true, } ); } ); test( 'should accumulate testing values', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE, siteId: secondarySiteId, } ); expect( state.testing ).to.eql( { [ primarySiteId ]: true, [ secondarySiteId ]: true, } ); } ); test( 'should set testing value to false if request finishes successfully', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE_SUCCESS, siteId: primarySiteId, } ); expect( state.testing ).to.eql( { [ primarySiteId ]: false, } ); } ); test( 'should set testing value to false if request finishes with failure', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE_FAILURE, siteId: primarySiteId, } ); expect( state.testing ).to.eql( { [ primarySiteId ]: false, } ); } ); test( 'should not persist state', () => { const state = reducer( previousState, { type: SERIALIZE, } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const state = reducer( previousState, { type: DESERIALIZE, } ); expect( state.testing ).to.eql( {} ); } ); } ); describe( 'preloading()', () => { const previousState = deepFreeze( { preloading: { [ primarySiteId ]: true, }, } ); test( 'should default to an empty object', () => { const state = reducer( undefined, {} ); expect( state.preloading ).to.eql( {} ); } ); test( 'should set preloading value to true if request in progress', () => { const state = reducer( undefined, { type: WP_SUPER_CACHE_PRELOAD_CACHE, siteId: primarySiteId, } ); expect( state.preloading ).to.eql( { [ primarySiteId ]: true, } ); } ); test( 'should accumulate preloading values', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_PRELOAD_CACHE, siteId: secondarySiteId, } ); expect( state.preloading ).to.eql( { [ primarySiteId ]: true, [ secondarySiteId ]: true, } ); } ); test( 'should set preloading value to false if request finishes successfully', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_PRELOAD_CACHE_SUCCESS, siteId: primarySiteId, } ); expect( state.preloading ).to.eql( { [ primarySiteId ]: false, } ); } ); test( 'should set preloading value to false if request finishes with failure', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_PRELOAD_CACHE_FAILURE, siteId: primarySiteId, } ); expect( state.preloading ).to.eql( { [ primarySiteId ]: false, } ); } ); test( 'should not persist state', () => { const state = reducer( previousState, { type: SERIALIZE, } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const state = reducer( previousState, { type: DESERIALIZE, } ); expect( state.preloading ).to.eql( {} ); } ); } ); describe( 'items()', () => { const primaryResults = { attempts: { first: { status: 'OK', }, }, }; const secondaryResults = { attempts: { second: { status: 'FAILED', }, }, }; const previousState = deepFreeze( { items: { [ primarySiteId ]: primaryResults, }, } ); test( 'should default to an empty object', () => { const state = reducer( undefined, {} ); expect( state.items ).to.eql( {} ); } ); test( 'should index cache test results by site ID', () => { const state = reducer( undefined, { type: WP_SUPER_CACHE_TEST_CACHE_SUCCESS, siteId: primarySiteId, data: primaryResults, } ); expect( state.items ).to.eql( { [ primarySiteId ]: primaryResults, } ); } ); test( 'should accumulate cache test results', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE_SUCCESS, siteId: secondarySiteId, data: secondaryResults, } ); expect( state.items ).to.eql( { [ primarySiteId ]: primaryResults, [ secondarySiteId ]: secondaryResults, } ); } ); test( 'should override previous cache test results of same site ID', () => { const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE_SUCCESS, siteId: primarySiteId, data: secondaryResults, } ); expect( state.items ).to.eql( { [ primarySiteId ]: secondaryResults, } ); } ); test( 'should accumulate new cache test results and overwrite existing ones for the same site ID', () => { const newResults = { attempts: { first: { status: 'OK', }, second: { status: 'FAILED', }, }, }; const state = reducer( previousState, { type: WP_SUPER_CACHE_TEST_CACHE_SUCCESS, siteId: primarySiteId, data: newResults, } ); expect( state.items ).to.eql( { [ primarySiteId ]: newResults, } ); } ); test( 'should not persist state', () => { const state = reducer( previousState, { type: SERIALIZE, } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const state = reducer( previousState, { type: DESERIALIZE, } ); expect( state.items ).to.eql( {} ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => sandbox.stub( console, 'warn' ) ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'requesting', 'items' ] ); } ); describe( 'requesting()', () => { it( 'should default to an empty object', () => { const state = requesting( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should set site ID to true value if request in progress', () => { const state = requesting( undefined, { type: PAGE_TEMPLATES_REQUEST, siteId: 2916284 } ); expect( state ).to.eql( { 2916284: true } ); } ); it( 'should accumulate requesting values', () => { const original = deepFreeze( { 2916284: true } ); const state = requesting( original, { type: PAGE_TEMPLATES_REQUEST, siteId: 77203074 } ); expect( state ).to.eql( { 2916284: true, 77203074: true } ); } ); it( 'should set site ID to false if request finishes successfully', () => { const original = deepFreeze( { 2916284: true } ); const state = requesting( original, { type: PAGE_TEMPLATES_REQUEST_SUCCESS, siteId: 2916284 } ); expect( state ).to.eql( { 2916284: false } ); } ); it( 'should set site ID to false if request finishes with failure', () => { const original = deepFreeze( { 2916284: true } ); const state = requesting( original, { type: PAGE_TEMPLATES_REQUEST_FAILURE, siteId: 2916284 } ); expect( state ).to.eql( { 2916284: false } ); } ); it( 'should not persist state', () => { const original = deepFreeze( { 2916284: true } ); const state = requesting( original, { type: SERIALIZE } ); expect( state ).to.eql( {} ); } ); it( 'should not load persisted state', () => { const original = deepFreeze( { 2916284: true } ); const state = requesting( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); describe( 'items()', () => { it( 'should default to an empty object', () => { const state = requesting( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should track page templates by site ID', () => { const state = items( null, { type: PAGE_TEMPLATES_RECEIVE, siteId: 2916284, templates: [ { label: 'Full Width, No Sidebar', file: 'fullwidth-page.php' }, { label: 'Portfolio Page Template', file: 'portfolio-page.php' } ] } ); expect( state ).to.eql( { 2916284: [ { label: 'Full Width, No Sidebar', file: 'fullwidth-page.php' }, { label: 'Portfolio Page Template', file: 'portfolio-page.php' } ] } ); } ); it( 'should accumulate sites', () => { const original = deepFreeze( { 2916284: [ { label: 'Full Width, No Sidebar', file: 'fullwidth-page.php' }, { label: 'Portfolio Page Template', file: 'portfolio-page.php' } ] } ); const state = items( original, { type: PAGE_TEMPLATES_RECEIVE, siteId: 77203074, templates: [ { label: 'Full Width', file: 'fullwidth.php' } ] } ); expect( state ).to.eql( { 2916284: [ { label: 'Full Width, No Sidebar', file: 'fullwidth-page.php' }, { label: 'Portfolio Page Template', file: 'portfolio-page.php' } ], 77203074: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); } ); it( 'should override previous page templates of same site ID', () => { const original = deepFreeze( { 2916284: [ { label: 'Full Width, No Sidebar', file: 'fullwidth-page.php' }, { label: 'Portfolio Page Template', file: 'portfolio-page.php' } ] } ); const state = items( original, { type: PAGE_TEMPLATES_RECEIVE, siteId: 2916284, templates: [ { label: 'Full Width', file: 'fullwidth.php' } ] } ); expect( state ).to.eql( { 2916284: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); } ); it( 'should persist state', () => { const original = deepFreeze( { 2916284: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); const state = items( original, { type: SERIALIZE } ); expect( state ).to.eql( { 2916284: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); } ); it( 'should load valid persisted state', () => { const original = deepFreeze( { 2916284: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( { 2916284: [ { label: 'Full Width', file: 'fullwidth.php' }, ] } ); } ); it( 'should not load invalid persisted state', () => { const original = deepFreeze( { 2916284: [ { label: 'Full Width' }, ] } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); expect( console.warn ).to.have.been.calledTwice; // eslint-disable-line no-console } ); } ); } );
describe( 'actions', () => { let spy; useSandbox( ( sandbox ) => spy = sandbox.spy() ); describe( '#requestPostFormats()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/sites/12345678/post-formats' ) .reply( 200, { formats: { image: 'Image', video: 'Video', link: 'Link' } } ) .get( '/rest/v1.1/sites/87654321/post-formats' ) .reply( 403, { error: 'authorization_required', message: 'User cannot access this private blog.' } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { requestPostFormats( 12345678 )( spy ); expect( spy ).to.have.been.calledWith( { type: POST_FORMATS_REQUEST, siteId: 12345678 } ); } ); it( 'should dispatch receive action when request completes', () => { return requestPostFormats( 12345678 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POST_FORMATS_RECEIVE, siteId: 12345678, formats: { image: 'Image', video: 'Video', link: 'Link' } } ); } ); } ); it( 'should dispatch request success action when request completes', () => { return requestPostFormats( 12345678 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POST_FORMATS_REQUEST_SUCCESS, siteId: 12345678 } ); } ); } ); it( 'should dispatch request failure action when request fails', () => { return requestPostFormats( 87654321 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: POST_FORMATS_REQUEST_FAILURE, siteId: 87654321, error: sinon.match( { message: 'User cannot access this private blog.' } ) } ); } ); } ); } ); } );
describe( 'api', () => { let app, config, localRequest, sandbox; useSandbox( newSandbox => ( sandbox = newSandbox ) ); beforeAll( () => { config = require( 'config' ); sandbox .stub( config, 'isEnabled' ) .withArgs( 'oauth' ) .returns( true ); app = require( '../' )(); localRequest = supertest( app ); } ); afterEach( () => { sandbox.restore(); } ); test( 'should return package version', () => { const version = require( '../../../package.json' ).version; return localRequest.get( '/version' ).then( ( { body, status } ) => { expect( status ).toBe( 200 ); expect( body ).toEqual( { version } ); } ); } ); test( 'should clear oauth cookie and redirect to login_url', () => { return localRequest .get( '/logout' ) .redirects( 0 ) .set( 'cookie', 'wpcom_token=test' ) .then( ( { header, status } ) => { expect( status ).toBe( 302 ); expect( header.location ).toBe( config( 'login_url' ) ); expect( header[ 'set-cookie' ] ).toEqual( [ 'wpcom_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT', ] ); } ); } ); let maybeIt = it; if ( unmodifiedConfig( 'env_id' ) !== 'desktop' ) { maybeIt = maybeIt.skip; } maybeIt( 'should handle incomplete login details', function( done ) { const end = sandbox.stub( request.Request.prototype, 'end' ); end.callsArgWithAsync( 0, { status: 400 }, { body: { error: 'invalid_request' } } ); localRequest.post( '/oauth' ).end( function( err, res ) { expect( res.body.error ).toBe( 'invalid_request' ); expect( res.status ).toBe( 400 ); done(); } ); } ); maybeIt( 'should return a login error with a bad login', function( done ) { const end = sandbox.stub( request.Request.prototype, 'end' ), response = { error: 'invalid_request' }; end.callsArgWithAsync( 0, { status: 400 }, { body: response } ); localRequest .post( '/oauth' ) .send( { username: '******', password: '******' } ) .expect( 400, response, done ); } ); maybeIt( 'should return a 400 needs_2fa if the user has 2FA enabled', function( done ) { const response = { error: 'needs_2fa', error_description: 'Please enter the verification code generated by your authenticator app.', }; const end = sandbox.stub( request.Request.prototype, 'end' ); end.callsArgWithAsync( 0, { status: 400 }, { body: response } ); localRequest .post( '/oauth' ) .send( { username: '******', password: '******' } ) .expect( 400, response, done ); } ); maybeIt( 'should return a successful login', function( done ) { const response = { access_token: '1234' }, end = sandbox.stub( request.Request.prototype, 'end' ); end.callsArgWithAsync( 0, null, { body: response } ); localRequest .post( '/oauth' ) .send( { username: '******', password: '******', auth_code: '123456' } ) .expect( 200, response, done ); } ); maybeIt( 'should return a 408 with no connection', function( done ) { const response = { error: 'invalid_request', error_description: 'The request to localhost failed (code 12345), please check your internet connection and try again.', }; const end = sandbox.stub( request.Request.prototype, 'end' ); end.callsArgWithAsync( 0, { host: 'localhost', code: '12345' }, undefined ); localRequest .post( '/oauth' ) .send( { username: '******', password: '******' } ) .expect( 408, response, done ); } ); } );
describe( 'actions', () => { let spy; useSandbox( sandbox => ( spy = sandbox.spy() ) ); describe( '#fetchShortcode()', () => { const siteId = 12345678; const shortcode = '[gallery ids="1,2,3"]'; describe( 'success', () => { useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .get( `/rest/v1.1/sites/${ siteId }/shortcodes/render` ) .query( { shortcode, } ) .reply( 200, { result: '<html></html>', shortcode: '[gallery ids="1,2,3"]', scripts: {}, styles: {}, } ); } ); test( 'should return a fetch action object when called', () => { return fetchShortcode( siteId, shortcode )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: SHORTCODE_REQUEST, siteId, shortcode, } ); } ); } ); test( 'should return a receive action when request successfully completes', () => { return fetchShortcode( siteId, shortcode )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: SHORTCODE_REQUEST_SUCCESS, siteId, shortcode, } ); expect( spy ).to.have.been.calledWith( { type: SHORTCODE_RECEIVE, siteId, shortcode, data: { result: '<html></html>', shortcode: '[gallery ids="1,2,3"]', scripts: {}, styles: {}, }, } ); } ); } ); } ); describe( 'failure', () => { useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .get( `/rest/v1.1/sites/${ siteId }/shortcodes/render` ) .query( { shortcode, } ) .reply( 400, { error: 'The requested shortcode does not exist.', } ); } ); test( 'should return a receive action when an error occurs', () => { return fetchShortcode( siteId, shortcode )( spy ).catch( () => { expect( spy ).to.have.been.calledWith( { type: SHORTCODE_REQUEST_FAILURE, siteId, shortcode, error: 'The requested shortcode does not exist.', } ); } ); } ); } ); } ); } );
describe( 'actions', () => { let spy; useSandbox( ( sandbox ) => spy = sandbox.spy() ); const siteId = 123456; const failedSiteId = 456789; const url = 'wordpress.com/test'; const stats = { data: { generated: 1493997829, supercache: { cached: 1, cached_list: [ { lower_age: 180347, files: 2, upper_age: 183839, dir: 'wordpress.com/test' } ], expired: 0, expired_list: [], fsize: 59573, }, wpcache: { cached: 0, cached_list: [], expired: 1, expired_list: [ { lower_age: 180347, files: 2, upper_age: 183839, dir: 'wordpress.com/test' } ], fsize: 59573, } } }; describe( '#generateStats()', () => { useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .get( `/rest/v1.1/jetpack-blogs/${ siteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/stats' } ) .reply( 200, stats ) .get( `/rest/v1.1/jetpack-blogs/${ failedSiteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/stats' } ) .reply( 403, { error: 'authorization_required', message: 'User cannot access this private blog.' } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { generateStats( siteId )( spy ); expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_GENERATE_STATS, siteId, } ); } ); it( 'should dispatch request success action when request completes', () => { return generateStats( siteId )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_GENERATE_STATS_SUCCESS, stats: stats.data, siteId, } ); } ); } ); it( 'should dispatch fail action when request fails', () => { return generateStats( failedSiteId )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_GENERATE_STATS_FAILURE, siteId: failedSiteId, } ); } ); } ); } ); describe( '#deleteFile()', () => { useNock( nock => { nock( 'https://public-api.wordpress.com' ) .persist() .post( `/rest/v1.1/jetpack-blogs/${ siteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/cache' } ) .reply( 200, { 'Cache Cleared': true } ) .post( `/rest/v1.1/jetpack-blogs/${ failedSiteId }/rest-api/` ) .query( { path: '/wp-super-cache/v1/cache' } ) .reply( 403, { error: 'authorization_required', message: 'User cannot access this private blog.' } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { deleteFile( siteId, url, true, false )( spy ); expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_DELETE_FILE, siteId, } ); } ); it( 'should dispatch request success action when request completes', () => { return deleteFile( siteId, url, true, false )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_DELETE_FILE_SUCCESS, isSupercache: true, isCached: false, siteId, url, } ); } ); } ); it( 'should dispatch fail action when request fails', () => { return deleteFile( failedSiteId, url, true, false )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WP_SUPER_CACHE_DELETE_FILE_FAILURE, siteId: failedSiteId, } ); } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => { sandbox.stub( console, 'warn' ); } ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'queries', 'queryRequests' ] ); } ); describe( 'queryRequests()', () => { it( 'should default to an empty object', () => { const state = queryRequests( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should track term query request fetching', () => { const state = queryRequests( undefined, { type: TERMS_REQUEST, siteId: 2916284, query: { search: 'Ribs' }, taxonomy: 'category' } ); expect( state ).to.eql( { 2916284: { category: { '{"search":"ribs"}': true } } } ); } ); it( 'should accumulate queries', () => { const original = deepFreeze( { 2916284: { category: { '{"search":"ribs"}': true } } } ); const state = queryRequests( original, { type: TERMS_REQUEST, siteId: 2916284, query: { search: 'AND Chicken' }, taxonomy: 'category' } ); expect( state ).to.eql( { 2916284: { category: { '{"search":"ribs"}': true, '{"search":"and chicken"}': true } } } ); } ); it( 'should track term query request success', () => { const state = queryRequests( undefined, { type: TERMS_REQUEST_SUCCESS, siteId: 2916284, query: { search: 'Ribs' }, found: testTerms.length, terms: testTerms, taxonomy: 'category' } ); expect( state ).to.eql( { 2916284: { category: { '{"search":"ribs"}': false } } } ); } ); it( 'should track term query request failure', () => { const state = queryRequests( undefined, { type: TERMS_REQUEST_FAILURE, siteId: 2916284, query: { search: 'Ribs' }, error: new Error(), taxonomy: 'category' } ); expect( state ).to.eql( { 2916284: { category: { '{"search":"ribs"}': false } } } ); } ); } ); describe( 'queries()', () => { it( 'should default to an empty object', () => { const state = queries( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should track term query request success', () => { const query = { search: 'i' }; const state = queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, found: 2, terms: testTerms, taxonomy: 'category', query } ); expect( state ).to.have.keys( [ '2916284' ] ); expect( state[ 2916284 ] ).to.have.keys( 'category' ); expect( state[ 2916284 ].category ).to.be.an.instanceof( TermQueryManager ); expect( state[ 2916284 ].category.getItems( query ) ).to.eql( testTerms ); expect( state[ 2916284 ].category.getFound( query ) ).to.equal( 2 ); } ); it( 'should track items even if no query is specified', () => { const state = queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, terms: testTerms, taxonomy: 'category' } ); expect( state ).to.have.keys( [ '2916284' ] ); expect( state[ 2916284 ] ).to.have.keys( 'category' ); expect( state[ 2916284 ].category ).to.be.an.instanceof( TermQueryManager ); expect( state[ 2916284 ].category.getItems() ).to.eql( testTerms ); } ); it( 'should return the same state if no changes to received items', () => { const action = { type: TERMS_RECEIVE, siteId: 2916284, found: 2, terms: testTerms, taxonomy: 'category', query: { search: 'i' } }; const original = deepFreeze( queries( undefined, action ) ); const state = queries( original, action ); expect( state ).to.equal( original ); } ); it( 'should accumulate query request success', () => { const original = deepFreeze( queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, found: 2, terms: testTerms, taxonomy: 'category', query: { search: 'i' } } ) ); const state = queries( original, { type: TERMS_RECEIVE, siteId: 2916284, query: { search: 'nom' }, found: 1, terms: [ { ID: 777, name: 'Noms' } ], taxonomy: 'post_tag' } ); expect( state ).to.have.keys( [ '2916284' ] ); expect( state[ 2916284 ] ).to.have.keys( [ 'category', 'post_tag' ] ); } ); it( 'should omit removed term', () => { const original = deepFreeze( queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, terms: testTerms, taxonomy: 'category' } ) ); const state = queries( original, { type: TERM_REMOVE, siteId: 2916284, taxonomy: 'category', termId: testTerms[ 0 ].ID } ); expect( state ).to.have.keys( [ '2916284' ] ); expect( state[ 2916284 ] ).to.have.keys( 'category' ); expect( state[ 2916284 ].category.getItems() ).to.have.length( testTerms.length - 1 ); expect( state[ 2916284 ].category.getItem( testTerms[ 0 ].ID ) ).to.be.undefined; } ); it( 'should persist state', () => { const original = deepFreeze( queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, found: 2, terms: testTerms, taxonomy: 'category', query: { search: 'i' } } ) ); const state = queries( original, { type: SERIALIZE } ); expect( state ).to.have.keys( [ '2916284' ] ); expect( state[ 2916284 ] ).to.have.keys( [ 'category' ] ); expect( state[ 2916284 ].category ).to.have.keys( [ 'data', 'options' ] ); } ); it( 'should load persisted state', () => { const original = deepFreeze( queries( undefined, { type: TERMS_RECEIVE, siteId: 2916284, found: 2, terms: testTerms, taxonomy: 'category', query: { search: 'i' } } ) ); const serialized = queries( original, { type: SERIALIZE } ); const state = queries( serialized, { type: DESERIALIZE } ); expect( state ).to.eql( original ); } ); it( 'should not load invalid persisted state', () => { const state = queries( { 2916284: { category: '{~!--BROKEN' } }, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'actions', () => { let sandbox, spy; useSandbox( newSandbox => { sandbox = newSandbox; spy = sandbox.spy(); } ); describe( 'creators functions', () => { it( '#plansReceiveAction()', () => { const plans = wpcomResponse; const action = plansReceiveAction( plans ); expect( action ).to.eql( ACTION_PLANS_RECEIVE ); } ); it( '#plansRequestAction()', () => { const action = plansRequestAction(); expect( action ).to.eql( ACTION_PLANS_REQUEST ); } ); it( '#plansRequestSuccessAction()', () => { const action = plansRequestSuccessAction(); expect( action ).to.eql( ACTION_PLANS_REQUEST_SUCCESS ); } ); it( '#plansRequestFailureAction()', () => { const action = plansRequestFailureAction( errorResponse ); expect( action ).to.eql( ACTION_PLANS_REQUEST_FAILURE ); } ); } ); describe( '#requestPlans() - success', () => { before( () => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.4/plans' ) .reply( 200, wpcomResponse ); } ); after( () => { nock.cleanAll(); } ); it( 'should dispatch REQUEST action when thunk triggered', () => { const action = plansRequestAction(); requestPlans()( spy ); expect( spy ).to.have.been.calledWith( action ); } ); it( 'should dispatch RECEIVE action when request completes', () => { const plans = wpcomResponse; const action_request = plansRequestAction(); const action_receive = plansReceiveAction( plans ); const promise = requestPlans()( spy ); expect( spy ).to.have.been.calledWith( action_request ); return promise.then( () => { expect( spy ).to.have.been.calledWith( action_receive ); } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( sandbox => { sandbox.stub( console, 'warn' ); } ); test( 'should export expected reducer keys', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'items', 'requesting' ] ); } ); describe( '#items()', () => { test( 'should default to an empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should store connection status when received', () => { const state = items( undefined, { type: SITE_CONNECTION_STATUS_RECEIVE, siteId: 2916284, status: true, } ); expect( state ).to.eql( { 2916284: true, } ); } ); test( 'should accumulate connection statuses when receiving for new sites', () => { const original = deepFreeze( { 2916284: true, } ); const state = items( original, { type: SITE_CONNECTION_STATUS_RECEIVE, siteId: 77203074, status: false, } ); expect( state ).to.eql( { 2916284: true, 77203074: false, } ); } ); test( 'should overwrite connection status when receiving for an existing site', () => { const original = deepFreeze( { 2916284: true, 77203074: false, } ); const state = items( original, { type: SITE_CONNECTION_STATUS_RECEIVE, siteId: 2916284, status: false, } ); expect( state ).to.eql( { 2916284: false, 77203074: false, } ); } ); test( 'should not persist state', () => { const original = deepFreeze( { 2916284: true, } ); const state = items( original, { type: SERIALIZE } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const original = deepFreeze( { 2916284: true, } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); describe( 'requesting()', () => { test( 'should default to an empty object', () => { const state = requesting( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should track connection status request when started', () => { const state = requesting( undefined, { type: SITE_CONNECTION_STATUS_REQUEST, siteId: 2916284, } ); expect( state ).to.eql( { 2916284: true, } ); } ); test( 'should accumulate connection status requests when started', () => { const original = deepFreeze( { 2916284: true, } ); const state = requesting( original, { type: SITE_CONNECTION_STATUS_REQUEST, siteId: 77203074, } ); expect( state ).to.eql( { 2916284: true, 77203074: true, } ); } ); test( 'should track connection status request when succeeded', () => { const original = deepFreeze( { 2916284: true, 77203074: true, } ); const state = requesting( original, { type: SITE_CONNECTION_STATUS_REQUEST_SUCCESS, siteId: 2916284, } ); expect( state ).to.eql( { 2916284: false, 77203074: true, } ); } ); test( 'should track connection status request when failed', () => { const original = deepFreeze( { 2916284: false, 77203074: true, } ); const state = requesting( original, { type: SITE_CONNECTION_STATUS_REQUEST_FAILURE, siteId: 77203074, } ); expect( state ).to.eql( { 2916284: false, 77203074: false, } ); } ); test( 'should not persist state', () => { const original = deepFreeze( { 2916284: true, } ); const state = requesting( original, { type: SERIALIZE } ); expect( state ).to.be.undefined; } ); test( 'should not load persisted state', () => { const original = deepFreeze( { 2916284: true, } ); const state = requesting( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => { sandbox.stub( console, 'warn' ); } ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'items' ] ); } ); describe( '#items()', () => { it( 'should persist state', () => { const original = deepFreeze( { 2916284: { 'jetpack-portfolio': keyedTestTerms } } ); const state = items( original, { type: SERIALIZE } ); expect( state ).to.eql( original ); } ); it( 'should load valid persisted state', () => { const original = deepFreeze( { 2916284: { 'jetpack-portfolio': keyedTestTerms } } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( original ); } ); it( 'should not load invalid persisted state', () => { const original = deepFreeze( { 2916284: { 'jetpack-portfolio': { 111: {} } } } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); it( 'should default to an empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); it( 'should add received terms', () => { const state = items( undefined, { type: TERMS_RECEIVE, siteId: 2916284, taxonomy: 'jetpack-portfolio', terms: testTerms } ); expect( state ).to.eql( { 2916284: { 'jetpack-portfolio': keyedTestTerms } } ); } ); it( 'should accumulate received terms by taxonomy', () => { const original = deepFreeze( { 2916284: { 'jetpack-portfolio': keyedTestTerms } } ); const state = items( original, { type: TERMS_RECEIVE, siteId: 2916284, taxonomy: 'jetpack-portfolio', terms: moreTerms } ); const expectedTerms = merge( {}, keyedTestTerms, keyedMoreTerms ); expect( state ).to.eql( { 2916284: { 'jetpack-portfolio': expectedTerms } } ); } ); it( 'should add additional terms', () => { const original = deepFreeze( { 2916284: { 'jetpack-portfolio': keyedTestTerms } } ); const state = items( original, { type: TERMS_RECEIVE, siteId: 2916284, taxonomy: 'amazing-taxonomy', terms: moreTerms } ); expect( state ).to.eql( { 2916284: { 'amazing-taxonomy': keyedMoreTerms, 'jetpack-portfolio': keyedTestTerms } } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( ( sandbox ) => sandbox.stub( console, 'warn' ) ); it( 'should include expected keys in return value', () => { expect( reducer( undefined, {} ) ).to.have.keys( [ 'requesting', 'items' ] ); } ); describe( 'requesting()', () => { it( 'should default to false', () => { const isRequestingHappinessEngineers = requesting( undefined, {} ); expect( isRequestingHappinessEngineers ).to.be.false; } ); it( 'should return true if request is in progress', () => { const isRequestingHappinessEngineers = requesting( undefined, { type: HAPPINESS_ENGINEERS_FETCH } ); expect( isRequestingHappinessEngineers ).to.be.true; } ); it( 'should return false if request was successful', () => { const isRequestingHappinessEngineers = requesting( undefined, { type: HAPPINESS_ENGINEERS_FETCH_SUCCESS } ); expect( isRequestingHappinessEngineers ).to.be.false; } ); it( 'should return false if request failed', () => { const isRequestingHappinessEngineers = requesting( undefined, { type: HAPPINESS_ENGINEERS_FETCH_FAILURE } ); expect( isRequestingHappinessEngineers ).to.be.false; } ); } ); describe( 'items()', () => { it( 'should default to null', () => { const state = items( undefined, {} ); expect( state ).to.eql( null ); } ); it( 'should save the received happiness engineers', () => { const state = items( null, { type: HAPPINESS_ENGINEERS_RECEIVE, happinessEngineers: [ { avatar_URL: 'test 1' }, { avatar_URL: 'test 2' } ] } ); expect( state ).to.eql( [ 'test 1', 'test 2' ] ); } ); it( 'should rewrite old state with received', () => { const original = deepFreeze( { 'test 1': { avatar_URL: 'test 1' }, 'test 2': { avatar_URL: 'test 2' } } ); const state = items( original, { type: HAPPINESS_ENGINEERS_RECEIVE, happinessEngineers: [ { avatar_URL: 'test 3' } ] } ); expect( state ).to.eql( [ 'test 3' ] ); } ); it( 'should persist state', () => { const original = deepFreeze( [ 'test 3' ] ); const state = items( original, { type: SERIALIZE } ); expect( state ).to.eql( [ 'test 3' ] ); } ); it( 'should load valid persisted state', () => { const original = deepFreeze( [ 'test 3' ] ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( [ 'test 3' ] ); } ); it( 'should not load invalid persisted state', () => { const original = deepFreeze( { 'test 3': { URL: 'test 3' } } ); const state = items( original, { type: DESERIALIZE } ); expect( state ).to.eql( null ); } ); } ); } );
describe( 'actions', () => { let sandbox, spy; useSandbox( newSandbox => { sandbox = newSandbox; spy = sandbox.spy(); } ); describe( '#dismissWordAdsError()', () => { test( 'should return a dismiss error action', () => { expect( dismissWordAdsError( 2916284 ) ).to.eql( { type: WORDADS_SITE_APPROVE_REQUEST_DISMISS_ERROR, siteId: 2916284, } ); } ); } ); describe( '#dismissWordAdsSuccess()', () => { test( 'should return a dismiss Success action', () => { expect( dismissWordAdsSuccess( 2916284 ) ).to.eql( { type: WORDADS_SITE_APPROVE_REQUEST_DISMISS_SUCCESS, siteId: 2916284, } ); } ); } ); describe( '#requestWordAdsApproval()', () => { useNock( nock => { nock( 'https://public-api.wordpress.com:443' ) .persist() .post( '/rest/v1.1/sites/2916284/wordads/approve' ) .reply( 200, { approved: true, } ) .post( '/rest/v1.1/sites/77203074/wordads/approve' ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to approve wordads.', } ); } ); test( 'should dispatch fetch action when thunk triggered', () => { requestWordAdsApproval( 2916284 )( spy ); expect( spy ).to.have.been.calledWith( { type: WORDADS_SITE_APPROVE_REQUEST, siteId: 2916284, } ); } ); test( 'should dispatch success action when request completes', () => { return requestWordAdsApproval( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WORDADS_SITE_APPROVE_REQUEST_SUCCESS, approved: true, siteId: 2916284, } ); } ); } ); test( 'should dispatch fail action when request fails', () => { return requestWordAdsApproval( 77203074 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: WORDADS_SITE_APPROVE_REQUEST_FAILURE, siteId: 77203074, error: sandbox.match( 'An active access token must be used to approve wordads.' ), } ); } ); } ); } ); } );
describe( 'actions', () => { let sandbox, spy; useSandbox( newSandbox => { sandbox = newSandbox; spy = sandbox.spy(); } ); const responseShape = { [ USER_SETTING_KEY ]: DEFAULT_PREFERENCES }; describe( 'receivePreferences()', () => { it( 'should return an action object', () => { const action = receivePreferences( { foo: 'bar' } ); expect( action ).to.eql( { type: PREFERENCES_RECEIVE, values: { foo: 'bar' } } ); } ); } ); describe( 'fetchPreferences()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/me/settings' ) .reply( 200, responseShape ); } ); it( 'should dispatch fetch action when thunk triggered', () => { fetchPreferences()( spy ); expect( spy ).to.have.been.calledWith( { type: PREFERENCES_FETCH } ); } ); it( 'should dispatch success action when request completes', () => { return fetchPreferences()( spy ).then( () => { expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_FETCH_SUCCESS, values: responseShape[ USER_SETTING_KEY ], } ); } ); } ); } ); describe( 'fetchPreferences()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/me/settings' ) .reply( 404 ); } ); it( 'should dispatch fail action when request fails', () => { return fetchPreferences()( spy ).then( () => { expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_FETCH_FAILURE } ); } ); } ); } ); describe( 'setPreference()', () => { it( 'should return PREFERENCES_SET with correct payload', () => { expect( setPreference( 'preferenceKey', 'preferenceValue' ) ).to.deep.equal( { type: PREFERENCES_SET, key: 'preferenceKey', value: 'preferenceValue' } ); } ); } ); describe( 'savePreference()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .post( '/rest/v1.1/me/settings/', { [ USER_SETTING_KEY ]: { preferenceKey: 'preferenceValue' } } ) .reply( 200, responseShape ); nock( 'https://public-api.wordpress.com:443' ) .persist() .post( '/rest/v1.1/me/settings/', { [ USER_SETTING_KEY ]: { loggedOut: true } } ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to query information about the current user.' } ); } ); it( 'should dispatch PREFERENCES_SET action when thunk triggered', () => { savePreference( 'preferenceKey', 'preferenceValue' )( spy ); expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_SET, key: 'preferenceKey', value: 'preferenceValue' } ); } ); it( 'should dispatch PREFERENCES_SAVE action when thunk triggered', () => { savePreference( 'preferenceKey', 'preferenceValue' )( spy ); expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_SAVE, key: 'preferenceKey', value: 'preferenceValue' } ); } ); it( 'should dispatch PREFERENCES_RECEIVE action when request completes', () => { return savePreference( 'preferenceKey', 'preferenceValue' )( spy ).then( () => { expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_RECEIVE, values: responseShape[ USER_SETTING_KEY ] } ); } ); } ); it( 'should dispatch PREFERENCES_SAVE_FAILURE action when request fails', () => { return savePreference( 'loggedOut', true )( spy ).then( () => { expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_SAVE_FAILURE, error: sinon.match( { message: 'An active access token must be used to query information about the current user.' } ) } ); } ); } ); it( 'should dispatch PREFERENCES_SAVE_SUCCESS action when request completes', () => { return savePreference( 'preferenceKey', 'preferenceValue' )( spy ).then( () => { expect( spy ).to.have.been.calledWithMatch( { type: PREFERENCES_SAVE_SUCCESS, key: 'preferenceKey', value: 'preferenceValue' } ); } ); } ); } ); } );
describe( 'reducer', () => { useSandbox( sandbox => { sandbox.stub( console, 'warn' ); } ); describe( '#requesting()', () => { test( 'should default to an empty object', () => { const state = requesting( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should set requesting value to true if request in progress', () => { const state = requesting( undefined, { type: POST_STATS_REQUEST, siteId: 2916284, postId: 2454, fields: [ 'views', 'years' ], } ); expect( state ).to.eql( { 2916284: { 2454: { 'views,years': true, }, }, } ); } ); test( 'should accumulate requesting values (stat)', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: true }, }, } ); const state = requesting( previousState, { type: POST_STATS_REQUEST, siteId: 2916284, postId: 2454, fields: [ 'countComments' ], } ); expect( state ).to.eql( { 2916284: { 2454: { views: true, countComments: true, }, }, } ); } ); test( 'should accumulate requesting values (postId)', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: true }, }, } ); const state = requesting( previousState, { type: POST_STATS_REQUEST, siteId: 2916284, postId: 2455, fields: [ 'views' ], } ); expect( state ).to.eql( { 2916284: { 2454: { views: true }, 2455: { views: true }, }, } ); } ); test( 'should accumulate requesting values (siteId)', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: true }, }, } ); const state = requesting( previousState, { type: POST_STATS_REQUEST, siteId: 2916285, postId: 2454, fields: [ 'views' ], } ); expect( state ).to.eql( { 2916284: { 2454: { views: true }, }, 2916285: { 2454: { views: true }, }, } ); } ); test( 'should set request to false if request finishes successfully', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: true }, }, } ); const state = requesting( previousState, { type: POST_STATS_REQUEST_SUCCESS, siteId: 2916284, postId: 2454, fields: [ 'views' ], } ); expect( state ).to.eql( { 2916284: { 2454: { views: false }, }, } ); } ); test( 'should set request to false if request finishes with failure', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: true }, }, } ); const state = requesting( previousState, { type: POST_STATS_REQUEST_FAILURE, siteId: 2916284, postId: 2454, fields: [ 'views' ], } ); expect( state ).to.eql( { 2916284: { 2454: { views: false }, }, } ); } ); } ); describe( '#items()', () => { test( 'should default to an empty object', () => { const state = items( undefined, {} ); expect( state ).to.eql( {} ); } ); test( 'should index post stats by site ID, post id and stat', () => { const state = items( null, { type: POST_STATS_RECEIVE, siteId: 2916284, postId: 2454, stats: { views: 2, years: [] }, } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2, years: [] }, }, } ); } ); test( 'should accumulate stats', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: POST_STATS_RECEIVE, siteId: 2916284, postId: 2454, stats: { countComments: 3 }, } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2, countComments: 3, }, }, } ); } ); test( 'should accumulate post IDs', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: POST_STATS_RECEIVE, siteId: 2916284, postId: 2455, stats: { views: 3 }, } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2 }, 2455: { views: 3 }, }, } ); } ); test( 'should accumulate site IDs', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: POST_STATS_RECEIVE, siteId: 2916285, postId: 2454, stats: { views: 3 }, } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2 }, }, 2916285: { 2454: { views: 3 }, }, } ); } ); test( 'should override previous stat value of same site ID, post ID and stat key', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: POST_STATS_RECEIVE, siteId: 2916284, postId: 2454, stats: { views: 3 }, } ); expect( state ).to.eql( { 2916284: { 2454: { views: 3 }, }, } ); } ); test( 'should persist state', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: SERIALIZE } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2 }, }, } ); } ); test( 'should load valid persisted state', () => { const previousState = deepFreeze( { 2916284: { 2454: { views: 2 }, }, } ); const state = items( previousState, { type: DESERIALIZE } ); expect( state ).to.eql( { 2916284: { 2454: { views: 2 }, }, } ); } ); test( 'should not load invalid persisted state', () => { const previousInvalidState = deepFreeze( { 2454: { views: 2 }, } ); const state = items( previousInvalidState, { type: DESERIALIZE } ); expect( state ).to.eql( {} ); } ); } ); } );
describe( 'actions', () => { let spy; useSandbox( ( sandbox ) => spy = sandbox.spy() ); describe( '#fetchConnections()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/sites/2916284/publicize-connections' ) .reply( 200, { connections: [ { ID: 2, site_ID: 2916284 } ] } ) .get( '/rest/v1.1/sites/77203074/publicize-connections' ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access publicize connections.' } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { fetchConnections( 2916284 )( spy ); expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTIONS_REQUEST, siteId: 2916284 } ); } ); it( 'should dispatch receive action when request completes', ( done ) => { fetchConnections( 2916284 )( spy ).then( () => { expect( spy ).to.have.been.calledThrice; const action1 = spy.getCall( 1 ).args[ 0 ]; expect( action1.type ).to.equal( PUBLICIZE_CONNECTIONS_RECEIVE ); expect( action1.siteId ).to.equal( 2916284 ); expect( action1.data.connections ).to.eql( [ { ID: 2, site_ID: 2916284 } ] ); const action2 = spy.getCall( 2 ).args[ 0 ]; expect( action2.type ).to.equal( PUBLICIZE_CONNECTIONS_REQUEST_SUCCESS ); expect( action2.siteId ).to.equal( 2916284 ); done(); } ).catch( done ); } ); it( 'should dispatch fail action when request fails', ( done ) => { fetchConnections( 77203074 )( spy ).then( () => { expect( spy ).to.have.been.calledTwice; const action = spy.getCall( 1 ).args[ 0 ]; expect( action.type ).to.equal( PUBLICIZE_CONNECTIONS_REQUEST_FAILURE ); expect( action.siteId ).to.equal( 77203074 ); expect( action.error.message ).to.equal( 'An active access token must be used to access publicize connections.' ); done(); } ).catch( done ); } ); } ); describe( 'fetchConnection()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .persist() .get( '/rest/v1.1/sites/2916284/publicize-connections/2' ) .reply( 200, { ID: 2, site_ID: 2916284 } ) .get( '/rest/v1.1/sites/77203074/publicize-connections/2' ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access publicize connections.' } ); } ); it( 'should dispatch fetch action when thunk triggered', () => { fetchConnection( 2916284, 2 )( spy ); expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTION_REQUEST, connectionId: 2, siteId: 2916284, } ); } ); it( 'should dispatch receive action when request completes', ( done ) => { fetchConnection( 2916284, 2 )( spy ).then( () => { expect( spy ).to.have.been.calledThrice; const action1 = spy.getCall( 1 ).args[ 0 ]; expect( action1.type ).to.equal( PUBLICIZE_CONNECTION_RECEIVE ); expect( action1.siteId ).to.equal( 2916284 ); expect( action1.connection ).to.eql( { ID: 2, site_ID: 2916284 } ); const action2 = spy.getCall( 2 ).args[ 0 ]; expect( action2.type ).to.equal( PUBLICIZE_CONNECTION_REQUEST_SUCCESS ); expect( action2.connectionId ).to.equal( 2 ); expect( action2.siteId ).to.equal( 2916284 ); done(); } ).catch( done ); } ); it( 'should dispatch fail action when request fails', ( done ) => { fetchConnection( 77203074, 2 )( spy ).then( () => { expect( spy ).to.have.been.calledTwice; const action = spy.getCall( 1 ).args[ 0 ]; expect( action.type ).to.equal( PUBLICIZE_CONNECTION_REQUEST_FAILURE ); expect( action.connectionId ).to.equal( 2 ); expect( action.error.message ).to.equal( 'An active access token must be used to access publicize connections.' ); expect( action.siteId ).to.equal( 77203074 ); done(); } ).catch( done ); } ); } ); describe( 'createSiteConnection()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .post( '/rest/v1.1/sites/2916284/publicize-connections/new', { external_user_ID: 1, keyring_connection_ID: 2, shared: false, } ) .reply( 200, { ID: 2, site_ID: 2916284, } ) .post( '/rest/v1.1/sites/77203074/publicize-connections/new', { external_user_ID: 1, keyring_connection_ID: 2, shared: false, } ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access publicize connections.' } ); } ); it( 'should dispatch create action when request completes', () => { createSiteConnection( 2916284, 2, 1 )( spy ).then( () => { const action = spy.getCall( 0 ).args[ 0 ]; expect( action.type ).to.equal( PUBLICIZE_CONNECTION_CREATE ); expect( action.connection ).to.eql( { ID: 2, site_ID: 2916284 } ); } ); } ); it( 'should dispatch fail action when request fails', () => { createSiteConnection( 77203074, 2, 1 )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTION_CREATE_FAILURE, error: sinon.match( { message: 'An active access token must be used to access publicize connections.' } ) } ); } ); } ); } ); describe( 'updateSiteConnection()', () => { const attributes = { shared: true }; useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .post( '/rest/v1.1/sites/2916284/publicize-connections/2', { shared: true, } ) .reply( 200, { ID: 2, site_ID: 2916284, } ) .post( '/rest/v1.1/sites/77203074/publicize-connections/2', { shared: true, } ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access publicize connections.' } ); } ); it( 'should dispatch update action when request completes', () => { updateSiteConnection( { ID: 2, site_ID: 2916284, label: 'Facebook' }, attributes )( spy ).then( () => { const action = spy.getCall( 0 ).args[ 0 ]; expect( action.type ).to.equal( PUBLICIZE_CONNECTION_UPDATE ); expect( action.connection ).to.eql( { ID: 2, site_ID: 2916284 } ); } ); } ); it( 'should dispatch fail action when request fails', () => { updateSiteConnection( { ID: 2, site_ID: 77203074, label: 'Facebook' }, attributes )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTION_UPDATE_FAILURE, error: sinon.match( { message: 'An active access token must be used to access publicize connections.' } ) } ); } ); } ); } ); describe( 'deleteSiteConnection()', () => { useNock( ( nock ) => { nock( 'https://public-api.wordpress.com:443' ) .post( '/rest/v1.1/sites/2916284/publicize-connections/2/delete' ) .reply( 200, { ID: 2, deleted: true, } ) .post( '/rest/v1.1/sites/77203074/publicize-connections/2/delete' ) .reply( 403, { error: 'authorization_required', message: 'An active access token must be used to access publicize connections.' } ); } ); it( 'should dispatch delete action when request completes', () => { deleteSiteConnection( { ID: 2, site_ID: 2916284 } )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTION_DELETE, connection: { ID: 2, site_ID: 2916284, }, } ); } ); } ); it( 'should dispatch fail action when request fails', () => { deleteSiteConnection( { ID: 2, site_ID: 77203074 } )( spy ).then( () => { expect( spy ).to.have.been.calledWith( { type: PUBLICIZE_CONNECTION_DELETE_FAILURE, error: sinon.match( { message: 'An active access token must be used to access publicize connections.' } ) } ); } ); } ); } ); describe( 'deleteConnections()', () => { it( 'should return an action object', () => { const action = deleteConnection( { ID: 2, site_ID: 2916284 } ); expect( action ).to.eql( { type: PUBLICIZE_CONNECTION_DELETE, connection: { ID: 2, site_ID: 2916284, }, } ); } ); } ); describe( 'failCreateConnection()', () => { it( 'should return an action object', () => { const action = failCreateConnection( { message: 'An error occurred' } ); expect( action ).to.eql( { type: PUBLICIZE_CONNECTION_CREATE_FAILURE, error: { message: 'An error occurred', }, } ); } ); } ); describe( '#receiveConnections()', () => { it( 'should return an action object', () => { const data = { connections: [ { ID: 2, site_ID: 2916284 } ] }; const action = receiveConnections( 2916284, data ); expect( action ).to.eql( { type: PUBLICIZE_CONNECTIONS_RECEIVE, siteId: 2916284, data } ); } ); } ); } );