describe( '#tryAndCustomizeTheme', () => { const pageSpy = sinon.spy(); const isJetpackSiteSpy = ( state, siteId ) => { if ( siteId === 2211667 ) { return true; } return false; }; const getCanonicalThemeSpy = ( state, siteId, themeId ) => { if ( themeId === 'karuna' ) { return { theme: themeId }; } return null; }; // we import it again with different name because we want to mock functions inside. let _tryAndCustomize; useMockery( ( mockery ) => { mockery.registerMock( 'page', pageSpy ); mockery.registerMock( 'state/sites/selectors', { isJetpackSite: isJetpackSiteSpy } ); mockery.registerMock( './selectors', { getThemeCustomizeUrl: () => 'customizer/url', getCanonicalTheme: getCanonicalThemeSpy, } ); _tryAndCustomize = require( '../actions' ).tryAndCustomizeTheme; } ); it( 'page should be called, when theme is available', () => { _tryAndCustomize( 'karuna-wpcom', 2211667 )( spy, () => {} ); expect( pageSpy.calledWith( 'customizer/url' ) ).to.be.true; } ); } );
describe( 'flows', function() { let flows, user; useFakeDom(); useFilesystemMocks( __dirname ); useMockery( ( mockery ) => { mockery.registerMock( 'lib/abtest', { abtest: () => '' } ); } ); before( () => { user = require( 'lib/user' )(); flows = require( 'signup/config/flows' ); sinon.stub( flows, 'getFlows' ).returns( mockedFlows ); } ); it( 'should return the full flow when the user is not logged in', function() { assert.deepEqual( flows.getFlow( 'main' ).steps, [ 'user', 'site' ] ); } ); it( 'should remove the user step from the flow when the user is not logged in', function() { user.setLoggedIn( true ); assert.deepEqual( flows.getFlow( 'main' ).steps, [ 'site' ] ); user.setLoggedIn( false ); } ); } );
describe( '#accept()', function() { let accept; useFakeDom(); useMockery(); before( function() { mockery.registerSubstitute( 'event', 'component-event' ); mockery.registerSubstitute( 'matches-selector', 'component-matches-selector' ); mockery.registerSubstitute( 'query', 'component-query' ); accept = require( '../' ); } ); beforeEach( function() { document.body.innerHTML = ''; } ); it( 'should render a dialog to the document body', function() { var message = 'Are you sure?', dialog; accept( message, function() {} ); dialog = document.querySelector( '.accept-dialog' ); expect( dialog ).to.be.an.instanceof( window.Element ); expect( dialog.textContent ).to.equal( message ); } ); it( 'should trigger the callback with an accepted prompt', function( done ) { accept( 'Are you sure?', function( accepted ) { expect( accepted ).to.be.be.true; done(); } ); document.querySelector( '.button.is-primary' ).click(); } ); it( 'should trigger the callback with a denied prompt', function( done ) { accept( 'Are you sure?', function( accepted ) { expect( accepted ).to.be.be.false; done(); } ); document.querySelector( '.button:not( .is-primary )' ).click(); } ); it( 'should clean up after itself once the prompt is closed', function( done ) { accept( 'Are you sure?', function() { process.nextTick( function() { expect( document.querySelector( '.accept-dialog' ) ).to.be.null; done(); } ); } ); document.querySelector( '.button.is-primary' ).click(); } ); } );
describe( 'getABTestFilteredFlow', () => { let flows; useFakeDom(); useFilesystemMocks( __dirname ); const ABTestMock = { abtest: noop, getABTestVariation: () => { return null; }, }; const getABTestVariationSpy = sinon.stub( ABTestMock, 'getABTestVariation' ); getABTestVariationSpy.onCall( 0 ).returns( 'notSiteTitle' ); getABTestVariationSpy.onCall( 1 ).returns( 'notSiteTitle' ); getABTestVariationSpy.onCall( 2 ).returns( 'showSiteTitleStep' ); getABTestVariationSpy.onCall( 3 ).returns( 'showSiteTitleStep' ); useMockery( ( mockery ) => { mockery.registerMock( 'lib/abtest', ABTestMock ); } ); before( () => { flows = require( 'signup/config/flows' ); sinon.stub( flows, 'getFlows' ).returns( mockedFlows ); sinon.stub( flows, 'insertStepIntoFlow', ( stepName, flow ) => { return flow; } ); sinon.stub( flows, 'removeStepFromFlow', ( stepName, flow ) => { return flow; } ); } ); it( 'should return flow unmodified if not in main flow', () => { assert.equal( flows.getABTestFilteredFlow( 'test', 'testflow' ), 'testflow' ); assert.equal( flows.insertStepIntoFlow.callCount, 0 ); assert.equal( flows.removeStepFromFlow.callCount, 0 ); } ); it( 'should check AB variation in main flow', () => { assert.equal( flows.getABTestFilteredFlow( 'main', 'testflow' ), 'testflow' ); assert.equal( getABTestVariationSpy.callCount, 1 ); assert.equal( flows.insertStepIntoFlow.callCount, 0 ); } ); it( 'should return flow unmodified if variation is not valid', () => { const myFlow = { name: 'test flow name', steps: [ 1, 2, 3 ] }; assert.equal( flows.getABTestFilteredFlow( 'main', myFlow ), myFlow ); assert.equal( getABTestVariationSpy.callCount, 2 ); assert.equal( flows.insertStepIntoFlow.callCount, 0 ); } ); } );
describe( 'UserUtils', () => { let UserUtils, user, configMock; useFakeDom(); useMockery( mockery => { configMock = sinon.stub(); configMock.isEnabled = sinon.stub(); mockery.registerMock( 'config', configMock ); } ); before( () => { user = require( 'lib/user' )(); UserUtils = require( '../utils' ); } ); beforeEach( () => { configMock.returns( '/url-with-|subdomain|' ); } ); context( 'without logout url', () => { before( () => { configMock.isEnabled .withArgs( 'always_use_logout_url' ) .returns( false ); } ); it( 'uses userData.logout_URL when available', () => { sinon.stub( user, 'get' ).returns( { logout_URL: '/userdata' } ); expect( UserUtils.getLogoutUrl() ).to.equal( '/userdata' ); user.get.restore(); } ); } ); context( 'with logout url', () => { before( () => { configMock.isEnabled .withArgs( 'always_use_logout_url' ) .returns( true ); } ); it( 'works when |subdomain| is not present', () => { configMock.returns( '/url-without-domain' ); expect( UserUtils.getLogoutUrl() ).to.equal( '/url-without-domain' ); } ); it( 'replaces |subdomain| when present and have domain', () => { sinon.stub( user, 'get' ).returns( { localeSlug: 'es' } ); expect( UserUtils.getLogoutUrl() ).to.equal( '/url-with-es.' ); user.get.restore(); } ); it( 'replaces |subdomain| when present but no domain', () => { expect( UserUtils.getLogoutUrl() ).to.equal( '/url-with-' ); } ); } ); } );
describe( 'mapped-domain', () => { let React, MappedDomain, props, TestUtils; before( () => { props = { selectedSite: { slug: 'neverexpires.wordpress.com', domain: 'neverexpires.com' }, domain: { name: 'neverexpires.com', expirationMoment: null }, settingPrimaryDomain: false, translate: identity }; } ); useFakeDom.withContainer(); useMockery( mockery => { mockery.registerMock( 'lib/analytics', {} ); } ); before( () => { React = require( 'react' ); TestUtils = require( 'react-addons-test-utils' ); const ReactClass = require( 'react/lib/ReactClass' ); ReactClass.injection.injectMixin( require( 'i18n-calypso' ).mixin ); MappedDomain = require( '../mapped-domain.jsx' ).MappedDomain; } ); it( 'should render when props.domain.expirationMoment is null', () => { const renderer = TestUtils.createRenderer(); renderer.render( <MappedDomain { ...props } /> ); const out = renderer.getRenderOutput(); assert( out ); } ); it( 'should use selectedSite.slug for URLs', sinon.test( function() { const paths = require( 'my-sites/domains/paths' ); const dnsStub = this.stub( paths, 'domainManagementDns' ); const emailStub = this.stub( paths, 'domainManagementEmail' ); const renderer = TestUtils.createRenderer(); renderer.render( <MappedDomain { ...props } /> ); renderer.getRenderOutput(); assert( dnsStub.calledWith( 'neverexpires.wordpress.com', 'neverexpires.com' ) ); assert( emailStub.calledWith( 'neverexpires.wordpress.com', 'neverexpires.com' ) ); } ) ); } );
describe( 'store', function() { let Dispatcher, PostFormatsStore, handler; // makes sure we always load fresh instance of Dispatcher useMockery(); before( function() { Dispatcher = require( 'dispatcher' ); spy( Dispatcher, 'register' ); PostFormatsStore = require( '../store' ); handler = Dispatcher.register.lastCall.args[ 0 ]; } ); beforeEach( function() { PostFormatsStore._formats = {}; } ); after( function() { Dispatcher.register.restore(); } ); function dispatchReceivePostFormats() { handler( { action: { type: 'RECEIVE_POST_FORMATS', siteId: DUMMY_SITE_ID, data: DUMMY_POST_FORMATS } } ); } describe( '#get()', function() { it( 'should return undefined for a site where data does not exist', function() { expect( PostFormatsStore.get( DUMMY_SITE_ID ) ).to.be.undefined; } ); it( 'should return an array of post format objects for a site where data has been received', function() { dispatchReceivePostFormats(); expect( PostFormatsStore.get( DUMMY_SITE_ID ) ).to.eql( [ { slug: 'image', label: 'Image' } ] ); } ); } ); describe( '.dispatchToken', function() { it( 'should emit a change event when receiving updates', function( done ) { PostFormatsStore.on( 'change', done ); dispatchReceivePostFormats(); } ); } ); } );
describe( 'selectors', () => { let hasUserRegisteredBefore; useFakeDom(); useMockery( mockery => { mockery.registerSubstitute( 'layout/guided-tours/config', 'state/ui/guided-tours/test/fixtures/config' ); const contexts = require( '../contexts' ); hasUserRegisteredBefore = contexts.hasUserRegisteredBefore; } ); describe( '#hasUserRegisteredBefore', () => { const cutoff = new Date( '2015-10-18T17:14:52+00:00' ); const oldUser = { currentUser: { id: 73705554 }, users: { items: { 73705554: { ID: 73705554, login: '******', date: '2014-10-18T17:14:52+00:00' } } }, }; const newUser = { currentUser: { id: 73705554 }, users: { items: { 73705554: { ID: 73705554, login: '******', date: '2016-10-18T17:14:52+00:00' } } }, }; it( 'should return true for users registered before the cut-off date', () => { expect( hasUserRegisteredBefore( cutoff )( oldUser ) ).to.be.true; } ); it( 'should return false for users registered after the cut-off date', () => { expect( hasUserRegisteredBefore( cutoff )( newUser ) ).to.be.false; } ); } ); } );
describe( 'abtest', () => { let abtest; let mockedUser = {}; let ABTests; const setSpy = sinon.spy( () => {} ); useFakeDom(); useMockery( () => { require( 'lib/local-storage' )( global ); mockery.registerMock( 'lib/abtest/active-tests', { mockedTest: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', allowExistingUsers: false }, mockedTestAllowAnyLocale: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', localeTargets: 'any', }, multipeLocaleNotEn: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', localeTargets: [ 'fr', 'de' ], }, mockedTestFrLocale: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', localeTargets: [ 'fr' ], }, mockedTestIlCountryCodeTarget: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', countryCodeTarget: 'IL', }, mockedTestAllowExisting: { datestamp: '20160627', variations: { hide: 50, show: 50 }, defaultVariation: 'hide', allowExistingUsers: true }, } ); mockery.registerMock( 'store', { enabled: () => true, get: () => { return ABTests; }, set: setSpy, } ); mockery.registerMock( 'lib/user', () => { return { get: () => mockedUser }; } ); abtest = require( 'lib/abtest' ).abtest; } ); describe( 'stored value', () => { beforeEach( () => { ABTests = { mockedTest_20160627: 'show' }; setSpy.reset(); } ); it( 'should return stored value and skip store.set for existing users', () => { mockedUser = { localeSlug: 'en', date: DATE_BEFORE }; navigator = { language: 'en', languages: [ 'en' ] }; expect( abtest( 'mockedTest' ) ).to.equal( 'show' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should return stored value and skip store.set for any user, including new, non-English users', () => { mockedUser = { localeSlug: 'de', date: DATE_AFTER }; expect( abtest( 'mockedTest' ) ).to.equal( 'show' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should return stored value and skip store.set for a logged-out user', () => { mockedUser = null; expect( abtest( 'mockedTest' ) ).to.equal( 'show' ); expect( setSpy ).not.to.have.been.called; } ); } ); describe( 'no stored value', () => { beforeEach( () => { ABTests = {}; setSpy.reset(); } ); describe( 'existing users', () => { beforeEach( () => { mockedUser = { localeSlug: 'en', date: DATE_BEFORE }; } ); it( 'should return default and skip store.set when allowExistingUsers is false', () => { expect( abtest( 'mockedTest' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should call store.set when allowExistingUsers is true', () => { abtest( 'mockedTestAllowExisting' ); expect( setSpy ).to.have.been.calledOnce; } ); } ); describe( 'new users', () => { beforeEach( () => { mockedUser = { localeSlug: 'en', date: DATE_AFTER }; } ); describe( 'English only users allowed (default)', () => { it( 'should call store.set for new users with English settings', () => { abtest( 'mockedTest' ); expect( setSpy ).to.have.been.calledOnce; } ); it( 'should call store.set for new users with English-Canada settings', () => { mockedUser.localeSlug = 'en-ca'; abtest( 'mockedTest' ); expect( setSpy ).to.have.been.calledOnce; } ); it( 'should return default and skip store.set for new users with non-English settings', () => { mockedUser.localeSlug = 'de'; expect( abtest( 'mockedTest' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); } ); describe( 'all locales allowed', () => { it( 'should call store.set for new users with English settings', () => { abtest( 'mockedTestAllowAnyLocale' ); expect( setSpy ).to.have.been.calledOnce; } ); it( 'should call store.set for new users with non-English settings', () => { mockedUser.localeSlug = 'de'; abtest( 'mockedTestAllowAnyLocale' ); expect( setSpy ).to.have.been.calledOnce; } ); } ); describe( 'specific locales only', () => { it( 'should return default and skip store.set for new users with English settings', () => { expect( abtest( 'multipeLocaleNotEn' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should call store.set for new users with non-English settings', () => { mockedUser.localeSlug = 'de'; abtest( 'multipeLocaleNotEn' ); expect( setSpy ).to.have.been.calledOnce; } ); } ); describe( 'fr locale only', () => { it( 'should return default and skip store.set for new users with non-french settings', () => { mockedUser.localeSlug = 'de'; expect( abtest( 'mockedTestFrLocale' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should call store.set for new users with fr settings', () => { mockedUser.localeSlug = 'fr'; abtest( 'mockedTestFrLocale' ); expect( setSpy ).to.have.been.calledOnce; } ); } ); describe( 'IL countryCodeTarget only', () => { it( 'should return default and skip store.set for new users with no GeoLocation value', () => { abtest( 'mockedTestIlCountryCodeTarget', null ); expect( setSpy ).not.to.have.been.called; } ); it( 'should throw error if countryCodeTarget is set but geoLocation is not passed', () => { expect( () => abtest( 'mockedTestIlCountryCodeTarget' ) ).to.throw( 'Test config has geoTarget, but no geoLocation passed to abtest function' ); } ); it( 'should return default and skip store.set for new users not from Israel', () => { const geoLocation = 'US'; expect( abtest( 'mockedTestIlCountryCodeTarget', geoLocation ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should call store.set for new users with from Israel', () => { const geoLocation = 'IL'; abtest( 'mockedTestIlCountryCodeTarget', geoLocation ); expect( setSpy ).to.have.been.calledOnce; } ); } ); } ); describe( 'new-user-no-locale', () => { beforeEach( () => { mockedUser = { localeSlug: false, date: DATE_AFTER }; } ); it( 'should call store.set for new users with no locale for en only test', () => { abtest( 'mockedTest' ); expect( setSpy ).to.have.been.calledOnce; } ); it( 'show return default and skip store.set for new users with no locale for fr test', () => { navigator.language = 'de'; expect( abtest( 'mockedTestFrLocale' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); } ); describe( 'logged-out users', () => { beforeEach( () => { mockedUser = false; setSpy.reset(); navigator = { language: 'en', languages: [ 'en' ] }; } ); it( 'should call store.set for logged-out users with English locale', () => { abtest( 'mockedTest' ); expect( setSpy ).to.have.been.calledOnce; } ); it( 'show return default and skip store.set for non-English navigator.language', () => { navigator.language = 'de'; expect( abtest( 'mockedTest' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should return default and skip store.set for non-English navigator.languages primary preference', () => { navigator.languages = [ 'de' ]; expect( abtest( 'mockedTest' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); it( 'should return default and skip store.set for non-English IE10 userLanguage setting', () => { navigator = { userLanguage: 'de' }; expect( abtest( 'mockedTest' ) ).to.equal( 'hide' ); expect( setSpy ).not.to.have.been.called; } ); } ); } ); } );
describe( 'analytics dispatching', () => { const mockAnalytics = spy(); const mockAdTracking = spy(); let dispatch; useMockery( mockery => { mockery.registerMock( 'lib/analytics', analyticsMock( mockAnalytics ) ); mockery.registerMock( 'lib/analytics/ad-tracking', adTrackingMock( mockAdTracking ) ); dispatch = require( '../middleware.js' ).dispatcher; } ); beforeEach( () => { mockAnalytics.reset(); mockAdTracking.reset(); } ); it( 'should call mc.bumpStat', () => { dispatch( bumpStat( 'test', 'value' ) ); expect( mockAnalytics ).to.have.been.calledWith( 'mc.bumpStat' ); } ); it( 'should call tracks.recordEvent', () => { dispatch( recordTracksEvent( 'test', { name: 'value' } ) ); expect( mockAnalytics ).to.have.been.calledWith( 'tracks.recordEvent' ); } ); it( 'should call pageView.record', () => { dispatch( recordPageView( 'path', 'title' ) ); expect( mockAnalytics ).to.have.been.calledWith( 'pageView.record' ); } ); it( 'should call ga.recordEvent', () => { dispatch( recordGoogleEvent( 'category', 'action' ) ); expect( mockAnalytics ).to.have.been.calledWith( 'ga.recordEvent' ); } ); it( 'should call ga.recordPageView', () => { dispatch( recordGooglePageView( 'path', 'title' ) ); expect( mockAnalytics ).to.have.been.calledWith( 'ga.recordPageView' ); } ); it( 'should call tracks.recordEvent', () => { dispatch( recordTracksEvent( 'event', { name: 'value' } ) ); expect( mockAnalytics ).to.have.been.calledWith( 'tracks.recordEvent' ); } ); it( 'should call trackCustomFacebookConversionEvent', () => { dispatch( recordCustomFacebookConversionEvent( 'event', { name: 'value' } ) ); expect( mockAdTracking ).to.have.been.calledWith( 'trackCustomFacebookConversionEvent' ); } ); it( 'should call trackCustomAdWordsRemarketingEvent', () => { dispatch( recordCustomAdWordsRemarketingEvent( { name: 'value' } ) ); expect( mockAdTracking ).to.have.been.calledWith( 'trackCustomAdWordsRemarketingEvent' ); } ); it( 'should call analytics events with wrapped actions', () => { dispatch( withAnalytics( bumpStat( 'name', 'value' ), { type: 'TEST_ACTION' } ) ); expect( mockAnalytics ).to.have.been.calledWith( 'mc.bumpStat' ); } ); } );
describe( 'isEligibleForFreeToPaidUpsell', () => { const state = 'state'; const moment = 'moment'; const siteId = 'siteId'; let canCurrentUser; let isMappedDomainSite; let isSiteOnFreePlan; let isUserRegistrationDaysWithinRange; let isVipSite; let isEligibleForFreeToPaidUpsell; useMockery( mockery => { canCurrentUser = stub(); isMappedDomainSite = stub(); isSiteOnFreePlan = stub(); isUserRegistrationDaysWithinRange = stub(); isVipSite = stub(); mockery.registerMock( 'state/selectors/can-current-user', canCurrentUser ); mockery.registerMock( 'state/selectors/is-mapped-domain-site', isMappedDomainSite ); mockery.registerMock( 'state/selectors/is-site-on-free-plan', isSiteOnFreePlan ); mockery.registerMock( 'state/selectors/is-user-registration-days-within-range', isUserRegistrationDaysWithinRange ); mockery.registerMock( 'state/selectors/is-vip-site', isVipSite ); } ); before( () => { isEligibleForFreeToPaidUpsell = require( '../is-eligible-for-free-to-paid-upsell' ); } ); const meetAllConditions = () => { canCurrentUser.withArgs( state, siteId, 'manage_options' ).returns( true ); isMappedDomainSite.withArgs( state, siteId ).returns( false ); isSiteOnFreePlan.withArgs( state, siteId ).returns( true ); isUserRegistrationDaysWithinRange.withArgs( state, moment, 0, 180 ).returns( true ); isVipSite.withArgs( state, siteId ).returns( false ); }; it( 'should return false when user can not manage options', () => { meetAllConditions(); canCurrentUser.withArgs( state, siteId, 'manage_options' ).returns( false ); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.false; } ); it( 'should return false when site has mapped domain', () => { meetAllConditions(); isMappedDomainSite.withArgs( state, siteId ).returns( true ); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.false; } ); it( 'should return false when site is not on a free plan', () => { meetAllConditions(); isSiteOnFreePlan.withArgs( state, siteId ).returns( false ); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.false; } ); it( 'should return false when user registration days is not within range', () => { meetAllConditions(); isUserRegistrationDaysWithinRange.withArgs( state, moment, 0, 180 ).returns( false ); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.false; } ); it( 'should return false when site is a vip site', () => { meetAllConditions(); isVipSite.withArgs( state, siteId ).returns( true ); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.false; } ); it( 'should return true when all conditions are met', () => { meetAllConditions(); expect( isEligibleForFreeToPaidUpsell( state, siteId, moment ) ).to.be.true; } ); } );
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 = rewire( '../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 ) { const mergePreferencesToLocalStorage = sinon.stub(); getSettings = sandbox.stub().callsArgWithAsync( 0, true ); PreferencesActions.__with__( { mergePreferencesToLocalStorage: mergePreferencesToLocalStorage } )( function() { PreferencesActions.fetch(); process.nextTick( function() { expect( 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(); } ); } ); } ); } );
describe( 'MediaLibrarySelectedStore', function() { let Dispatcher, sandbox, MediaLibrarySelectedStore, handler, MediaStore; useFakeDom(); useMockery( mockery => { mockery.registerMock( 'lib/wp', { me: () => ( { get: noop } ), site: noop } ); } ); before( function() { sandbox = sinon.sandbox.create(); Dispatcher = require( 'dispatcher' ); sandbox.spy( Dispatcher, 'register' ); sandbox.stub( Dispatcher, 'waitFor' ).returns( true ); MediaStore = require( '../store' ); sandbox.stub( MediaStore, 'get', function( siteId, itemId ) { if ( siteId === DUMMY_SITE_ID ) { return DUMMY_OBJECTS[ itemId ]; } } ); MediaLibrarySelectedStore = require( '../library-selected-store' ); handler = Dispatcher.register.lastCall.args[ 0 ]; } ); beforeEach( function() { MediaLibrarySelectedStore._media = {}; } ); after( function() { sandbox.restore(); } ); function dispatchSetLibrarySelectedItems( action ) { handler( { action: assign( { type: 'SET_MEDIA_LIBRARY_SELECTED_ITEMS', siteId: DUMMY_SITE_ID, data: [ DUMMY_TRANSIENT_MEDIA_OBJECT ] }, action ) } ); } function dispatchReceiveMediaItem() { handler( { action: { type: 'RECEIVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, data: DUMMY_MEDIA_OBJECT, id: DUMMY_TRANSIENT_MEDIA_OBJECT.ID } } ); } function dispatchRemoveMediaItem( error ) { handler( { action: { error: error, type: 'REMOVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, data: DUMMY_TRANSIENT_MEDIA_OBJECT } } ); } describe( '#get()', function() { it( 'should return a single value', function() { dispatchSetLibrarySelectedItems(); expect( MediaLibrarySelectedStore.get( DUMMY_SITE_ID, DUMMY_TRANSIENT_MEDIA_OBJECT.ID ) ).to.eql( DUMMY_TRANSIENT_MEDIA_OBJECT ); } ); it( 'should return undefined for an item that does not exist', function() { expect( MediaLibrarySelectedStore.get( DUMMY_SITE_ID, DUMMY_TRANSIENT_MEDIA_OBJECT.ID + 1 ) ).to.be.undefined; } ); } ); describe( '#getAll()', function() { it( 'should return all selected media', function() { dispatchSetLibrarySelectedItems(); expect( MediaLibrarySelectedStore.getAll( DUMMY_SITE_ID ) ).to.eql( [ DUMMY_TRANSIENT_MEDIA_OBJECT ] ); } ); it( 'should return an empty array for a site with no selected items', function() { expect( MediaLibrarySelectedStore.getAll( DUMMY_SITE_ID ) ).to.eql( [] ); } ); } ); describe( '.dispatchToken', function() { it( 'should expose its dispatcher ID', function() { expect( MediaLibrarySelectedStore.dispatchToken ).to.not.be.undefined; } ); it( 'should emit a change event when library items have been set', function( done ) { MediaLibrarySelectedStore.once( 'change', done ); dispatchSetLibrarySelectedItems(); } ); it( 'should emit a change event when receiving updates', function( done ) { MediaLibrarySelectedStore.once( 'change', done ); dispatchReceiveMediaItem(); } ); it( 'should replace an item when its ID has changed', function() { dispatchSetLibrarySelectedItems(); dispatchReceiveMediaItem(); expect( MediaLibrarySelectedStore.getAll( DUMMY_SITE_ID ) ).to.eql( [ DUMMY_MEDIA_OBJECT ] ); } ); it( 'should remove an item when REMOVE_MEDIA_ITEM is dispatched', function() { dispatchSetLibrarySelectedItems(); dispatchRemoveMediaItem(); expect( MediaLibrarySelectedStore._media[ DUMMY_SITE_ID ] ).to.be.empty; } ); it( 'should clear selected items when CHANGE_MEDIA_SOURCE is dispatched', function() { dispatchSetLibrarySelectedItems(); handler( { action: { type: 'CHANGE_MEDIA_SOURCE', siteId: DUMMY_SITE_ID, } } ); expect( MediaLibrarySelectedStore._media[ DUMMY_SITE_ID ] ).to.be.empty; } ); } ); } );
export default function( dirpath ) { useMockery( findQuickMocks.bind( null, dirpath ), null ); }
describe( 'cache-index', () => { useMockery( ( mockery ) => { mockery.registerMock( 'localforage', localforageMock ); ( { cacheIndex } = require( '../cache-index' ) ); } ); beforeEach( clearLocal ); // also do inside nested blocks with >1 test describe( '#getAll', () => { beforeEach( clearLocal ); it( 'should return undefined for empty localforage', ( done ) => { return cacheIndex.getAll() .then( res => { try { expect( res ).to.equal( undefined ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); it( 'should return index from localforage and nothing else', ( done ) => { const { recordsList } = testData; setLocalData( { someStoredRecord: 1, someOtherRecord: 2, [ RECORDS_LIST_KEY ]: recordsList } ); return cacheIndex.getAll() .then( res => { try { expect( res ).to.equal( recordsList ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#addItem', () => { beforeEach( clearLocal ); // also do inside nested blocks with >1 test it( 'should add item to empty index', ( done ) => { const key = 'unique-key'; return cacheIndex.addItem( key ) .then( () => { try { const currentIndex = localforageMock.getLocalData()[ RECORDS_LIST_KEY ]; expect( currentIndex ).to.be.an( 'array' ); expect( currentIndex[0] ).to.have.property( 'key', key ); expect( currentIndex[0] ).to.have.property( 'timestamp' ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); it( 'should store a pageSeriesKey when passed as third parameter', ( done ) => { const { postListKey, postListParams, postListPageSeriesKey } = testData; const normalizedParams = normalizeRequestParams( postListParams ); return cacheIndex.addItem( postListKey, normalizedParams, postListPageSeriesKey ) .then( () => { try { const currentIndex = localforageMock.getLocalData()[ RECORDS_LIST_KEY ]; expect( currentIndex ).to.be.an( 'array' ); expect( currentIndex[0] ).to.have.property( 'key', postListKey ); expect( currentIndex[0] ).to.have.property( 'pageSeriesKey', postListPageSeriesKey ); expect( currentIndex[0] ).to.have.property( 'timestamp' ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#removeItem', () => { it( 'should remove item from a populated index', ( done ) => { const { postListKey, localDataFull } = testData; setLocalData( localDataFull ); return cacheIndex.removeItem( postListKey ) .then( () => { try { const currentIndex = localData()[ RECORDS_LIST_KEY ]; expect( currentIndex.length ).to.eql( 2 ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#pruneStaleRecords', () => { it( 'should prune old records', ( done ) => { const { postListKey, postListWithSearchKey, postListLocalRecord, postListWithSearchLocalRecord, } = testData; const now = Date.now(); const yesterday = now - ms( '1 day' ); setLocalData( { [ postListKey ]: postListLocalRecord, [ postListWithSearchKey ]: postListWithSearchLocalRecord, [ RECORDS_LIST_KEY ]: [ { key: postListKey, timestamp: now }, { key: postListWithSearchKey, timestamp: yesterday }, ] } ); return cacheIndex.pruneStaleRecords( '1 hour' ) .then( () => { try { const freshData = localData(); const currentIndex = freshData[ RECORDS_LIST_KEY ]; expect( currentIndex ).to.eql( [ { key: postListKey, timestamp: now } ] ); expect( freshData ).to.have.property( postListKey, postListLocalRecord ); expect( freshData ).to.have.property( RECORDS_LIST_KEY ); expect( freshData ).to.not.have.property( postListWithSearchKey ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#clearAll', () => { it( 'should clear all sync records and nothing else', ( done ) => { const { localDataFull } = testData; setLocalData( Object.assign( { someRecord: 1 }, localDataFull ) ); return cacheIndex.clearAll() .then( () => { try { const freshData = localData(); expect( freshData ).to.eql( { someRecord: 1 } ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#clearPageSeries', () => { it( 'should clear records with matching pageSeriesKey and leave other records intact', ( done ) => { const { postListKey, postListNextPageKey, postListWithSearchKey, postListPageSeriesKey, postListDifferentPageSeriesKey, postListLocalRecord, postListNextPageParams, postListNextPageLocalRecord, } = testData; const now = Date.now(); setLocalData( { someOtherRecord: 1, [ postListKey ]: postListLocalRecord, [ postListNextPageKey ]: postListNextPageLocalRecord, [ postListWithSearchKey ]: Object.assign( {}, postListLocalRecord ), [ RECORDS_LIST_KEY ]: [ { key: postListKey, pageSeriesKey: postListPageSeriesKey, timestamp: now }, { key: postListNextPageKey, pageSeriesKey: postListPageSeriesKey, timestamp: now }, { key: postListWithSearchKey, pageSerieKey: postListDifferentPageSeriesKey, timestamp: now }, ] } ); return cacheIndex.clearPageSeries( postListNextPageParams ) .then( () => { try { const freshData = localData(); expect( freshData ).to.eql( { someOtherRecord: 1, [ postListWithSearchKey ]: postListLocalRecord, [ RECORDS_LIST_KEY ]: [ { key: postListWithSearchKey, pageSerieKey: postListDifferentPageSeriesKey, timestamp: now } ] } ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); describe( '#clearRecordsByParamFilter', () => { it( 'should clear records with reqParams that matches filter and leave other records intact', ( done ) => { const { postListKey, postListParams, postListLocalRecord, postListDifferentSiteKey, postListDifferentSiteParams, postListDifferentSiteLocalRecord } = testData; const now = Date.now(); setLocalData( { [ postListKey ]: postListLocalRecord, [ postListDifferentSiteKey ]: postListDifferentSiteLocalRecord, [ RECORDS_LIST_KEY ]: [ { key: postListKey, reqParams: postListParams, pageSeriesKey: 'doesnotmatter', timestamp: now }, { key: postListDifferentSiteKey, reqParams: postListDifferentSiteParams, pageSeriesKey: 'stilldoesnotmatter', timestamp: now } ] } ); const matchSiteFilter = ( reqParams ) => { return reqParams.path === '/sites/bobinprogress2.wordpress.com/posts'; }; return cacheIndex.clearRecordsByParamFilter( matchSiteFilter ) .then( () => { try { const freshData = localData(); expect( freshData ).to.have.property( postListKey ); expect( freshData ).to.not.have.property( postListDifferentSiteKey ); expect( freshData[ RECORDS_LIST_KEY ].length ).to.eql( 1 ); done(); } catch ( err ) { done( err ); } }, ( err ) => done( err ) ); } ); } ); } );
describe( 'WPcom Data Actions', () => { let actions; useMockery( mockery => { mockery.registerMock( 'lib/wp', mockedWpcom ); mockery.registerMock( 'lib/analytics', { mc: { bumpStat: noop }, tracks: { recordEvent: noop } } ); } ); beforeEach( () => { actions = require( 'lib/plugins/actions' ); actions.resetQueue(); mockedWpcom.undocumented().reset(); } ); useFakeDom(); it( 'Actions should be an object', () => { assert.isObject( actions ); } ); it( 'Actions should have method installPlugin', () => { assert.isFunction( actions.installPlugin ); } ); it( 'when installing a plugin, it should send a install request to .com', done => { actions.installPlugin( siteData, 'test', noop ) .then( () => { assert.equal( mockedWpcom.getActivity().pluginsInstallCalls, 1 ); done(); } ) .catch( done ); } ); it( 'when installing a plugin, it should send an activate request to .com', done => { actions.installPlugin( siteData, 'test', noop ) .then( () => { assert.equal( mockedWpcom.getActivity().pluginsActivateCalls, 1 ); done(); } ) .catch( done ); } ); it( 'when installing a plugin, it should not send a request to .com when the site doesn\'t allow us to update its files', () => { actions.installPlugin( { canUpdateFiles: false }, 'test', noop ); assert.equal( mockedWpcom.getActivity().pluginsInstallCalls, 0 ); } ); it( 'when installing a plugin, it should return a rejected promise if the site files can\'t be updated', done => { actions.installPlugin( { canUpdateFiles: false, capabilities: { manage_options: true }, }, 'test', noop ) .then( () => done( 'Promise should be rejected' ) ) .catch( () => done() ); } ); it( 'when installing a plugin, it should return a rejected promise if user can\'t manage the site', done => { actions.installPlugin( { canUpdateFiles: false, capabilities: { manage_options: true }, }, 'test', noop ) .then( () => done( 'Promise should be rejected' ) ) .catch( () => done() ); } ); it( 'Actions should have method removePlugin', () => { assert.isFunction( actions.removePlugin ); } ); it( 'when removing a plugin, it should send a remove request to .com', done => { actions.removePlugin( { canUpdateFiles: true, user_can_manage: true, capabilities: { manage_options: true }, }, {}, noop ) .then( () => { assert.equal( mockedWpcom.getActivity().pluginsRemoveCalls, 1 ); done(); } ) .catch( done ); } ); it( 'when removing a plugin, it should send an deactivate request to .com', done => { actions.removePlugin( { canUpdateFiles: true, user_can_manage: true, capabilities: { manage_options: true }, jetpack: true }, { active: true }, noop ) .then( () => { assert.equal( mockedWpcom.getActivity().pluginsDeactivateCalls, 1 ); done(); } ) .catch( done ); } ); it( 'when removing a plugin, it should not send a request to .com when the site doesn\'t allow us to update its files', () => { actions.removePlugin( { canUpdateFiles: false }, {}, noop ); assert.equal( mockedWpcom.getActivity().pluginsRemoveCalls, 0 ); } ); } );
describe( 'SitesList', () => { let SitesList, Site, data; let sitesList, originalData, initializedSites; useMockery(); useFakeDom(); before( () => { Site = require( 'lib/site' ); SitesList = require( '../list' ); data = require( './fixtures/data' ); } ); beforeEach( () => { originalData = cloneDeep( data.original ); sitesList = SitesList(); sitesList.initialize( originalData ); initializedSites = sitesList.get(); } ); describe( 'initialization', () => { it( 'should create the correct number of sites', () => { assert.equal( initializedSites.length, originalData.length ); } ); it( 'should create Site objects', () => { forEach( initializedSites, site => { assert.instanceOf( site, Site ) } ); } ); it( 'should set attributes properly', () => { const site = initializedSites[ 0 ]; const origSite = originalData[ 0 ]; forEach( origSite, ( value, prop ) => { assert.deepEqual( value, site[ prop ] ); } ); } ); it( 'should add change handlers', () => { forEach( initializedSites, ( site ) => { assert.isDefined( site.listeners( 'change' ) ); } ); } ); } ); describe( 'updating', () => { let updatedData, originalList; before( () => { updatedData = cloneDeep( data.updated ); originalList = sitesList.initialize( originalData ); } ); it( 'updating should not create new Site instances', () => { sitesList.update( updatedData ); const updatedList = sitesList.get(); forEach( originalList, ( site, index ) => { assert.strictEqual( site, updatedList[ index ] ); } ); } ); it( 'should update attributes properly', () => { sitesList.update( updatedData ); const site = sitesList.get()[ 0 ]; const updatedSite = updatedData[ 0 ]; forEach( updatedSite, ( updatedValue, prop ) => { assert.deepEqual( updatedValue, site[ prop ] ); } ); } ); } ); describe( 'change propagation', () => { it( 'should trigger change when site is updated', () => { const siteId = originalData[0].ID; const changeCallback = sinon.spy(); sitesList.initialize( cloneDeep( data.original ) ); sitesList.once( 'change', changeCallback ); const site = sitesList.getSite( siteId ); site.set( { description: 'Calypso rocks!' } ); assert.isTrue( changeCallback.called ); } ); } ); } );
describe( 'sync-handler', () => { useMockery( ( mockery ) => { mockery.registerMock( 'localforage', localforageMock ); ( { cacheIndex } = require( '../cache-index' ) ); const handlerMock = ( params, callback ) => { const key = generateKey( params ); callback( null, responseData[ key ] ); return responseData[ key ]; }; ( { SyncHandler, hasPaginationChanged, syncOptOut } = require( '../' ) ); wpcom = new SyncHandler( handlerMock ); } ); beforeEach( () => { responseData = {}; setLocalData( {} ); } ); it( 'should call callback with local response', () => { const { postListKey, postListParams, postListLocalRecord, postListResponseBody } = testData; const callback = spy(); setLocalData( { [ postListKey ]: postListLocalRecord } ); wpcom( postListParams, callback ); expect( callback.calledWith( null, postListResponseBody ) ); } ); it( 'should call callback with request response', ( done ) => { const { postListKey, postListParams, postListResponseBody } = testData; const callback = spy(); responseData[ postListKey ] = postListResponseBody; wpcom( postListParams, callback ); defer( () => { expect( callback ).to.have.been.calledOnce; expect( callback.calledWith( null, postListResponseBody ) ); done(); } ); } ); it( 'should call callback twice with local and request responses', ( done ) => { const { postListKey, postListParams, postListLocalRecord, postListResponseBody, postListFreshResponseBody } = testData; const callback = spy(); setLocalData( { [ postListKey ]: postListLocalRecord } ); responseData[ postListKey ] = postListFreshResponseBody; wpcom( postListParams, callback ); defer( () => { expect( callback ).to.have.been.calledTwice; expect( callback.calledWith( null, postListResponseBody ) ); expect( callback.calledWith( null, postListFreshResponseBody ) ); done(); } ); } ); it( 'should store cacheIndex records with matching pageSeriesKey for paginated responses', ( done ) => { const { postListKey, postListNextPageKey, postListParams, postListNextPageParams, postListResponseBody, postListNextPageResponseBody, } = testData; responseData = { [ postListKey ]: postListResponseBody, [ postListNextPageKey ]: postListNextPageResponseBody, }; wpcom( postListParams, () => {} ); defer( () => { try { wpcom( postListNextPageParams, () => {} ); defer( () => { try { const freshData = localforageMock.getLocalData(); const firstRecord = freshData[ RECORDS_LIST_KEY ][ 0 ]; const secondRecord = freshData[ RECORDS_LIST_KEY ][ 1 ]; expect( firstRecord.key ).to.not.equal( secondRecord.key ); expect( firstRecord.pageSeriesKey ).to.equal( secondRecord.pageSeriesKey ); done(); } catch ( err ) { done( err ); } } ); } catch ( err ) { done( err ); } } ); } ); it( 'should call clearPageSeries when getting a response with an updated page_handle', ( done ) => { const { postListParams, postListLocalRecord, postListFreshResponseBody, postListKey, } = testData; setLocalData( { [ postListKey ]: postListLocalRecord } ); responseData[ postListKey ] = postListFreshResponseBody; spy( cacheIndex, 'clearPageSeries' ); wpcom( postListParams, () => {} ); defer( () => { try { expect( cacheIndex.clearPageSeries.called ).to.be.true; cacheIndex.clearPageSeries.restore(); done(); } catch ( err ) { done( err ); } } ); } ); describe( 'generateKey', () => { beforeEach( () => { responseData = {}; setLocalData( {} ); } ); it( 'should return the same key for identical request', () => { const { postListParams } = testData; const secondRequest = Object.assign( {}, postListParams ); const key1 = generateKey( postListParams ); const key2 = generateKey( secondRequest ); expect( typeof key1 ).to.equal( 'string' ); expect( key1 ).to.equal( key2 ); } ); it( 'should return unique keys for different requests', () => { const { postListParams } = testData; const secondRequest = Object.assign( {}, postListParams, { query: '?filter=test' } ); const key1 = generateKey( postListParams ); const key2 = generateKey( secondRequest ); expect( typeof key1 ).to.equal( 'string' ); expect( key1 ).to.not.equal( key2 ); } ); it( 'should return the same key if parameters are in different order', () => { const { postListParams, postListDifferentOrderParams } = testData; const key1 = generateKey( postListParams ); const key2 = generateKey( postListDifferentOrderParams ); expect( typeof key1 ).to.equal( 'string' ); expect( key1 ).to.equal( key2 ); } ); } ); describe( 'hasPaginationChanged', () => { it( 'should return false if requestResponse has no page handle', () => { const { postListNoHandleResponseBody } = testData; const result = hasPaginationChanged( postListNoHandleResponseBody, null ); expect( result ).to.equal( false ); } ); it( 'should return false for call with identical response', () => { const { postListResponseBody } = testData; const identicalResponse = Object.assign( {}, postListResponseBody ); const result = hasPaginationChanged( postListResponseBody, identicalResponse ); expect( result ).to.equal( false ); } ); it( 'should return true if page handle is different', () => { const { postListResponseBody, postListFreshResponseBody } = testData; const result = hasPaginationChanged( postListResponseBody, postListFreshResponseBody ); expect( result ).to.equal( true ); } ); it( 'should return false with empty local response', () => { const { postListResponseBody } = testData; const result = hasPaginationChanged( postListResponseBody, null ); expect( result ).to.equal( false ); } ); } ); describe( 'syncOptOut', () => { let wpcomOptOut; before( () => { wpcomOptOut = syncOptOut( wpcomUndocumented( wpcom ) ); } ); it( 'should call callback with network response even when local response exists', ( done ) => { const { postListKey, postListSiteId, postListParams, postListNextPageLocalRecord, postListResponseBody } = testData; const callback = spy(); setLocalData( { [ postListKey ]: postListNextPageLocalRecord } ); responseData[ postListKey ] = postListResponseBody; wpcomOptOut.skipLocalSyncResult().site( postListSiteId ).postsList( querystring.parse( postListParams.query ), callback ); defer( () => { expect( callback ).to.have.been.calledOnce; expect( callback ).to.have.been.calledWith( null, postListResponseBody ); done(); } ); } ); } ); } );
describe( 'utils', function() { let postUtils; useFakeDom(); useMockery( mockery => { mockery.registerMock( 'lib/wp', { me: () => ( { get: noop } ) } ); } ); before( () => { postUtils = require( '../utils' ); } ); describe( '#getEditURL', function() { it( 'should return correct path type=post is supplied', function() { const url = postUtils.getEditURL( { ID: 123, type: 'post' }, { slug: 'en.blog.wordpress.com' } ); assert( url === '/post/en.blog.wordpress.com/123' ); } ); it( 'should return correct path type=page is supplied', function() { const url = postUtils.getEditURL( { ID: 123, type: 'page' }, { slug: 'en.blog.wordpress.com' } ); assert( url === '/page/en.blog.wordpress.com/123' ); } ); it( 'should return correct path when custom post type is supplied', function() { const url = postUtils.getEditURL( { ID: 123, type: 'jetpack-portfolio' }, { slug: 'en.blog.wordpress.com' } ); assert( url === '/edit/jetpack-portfolio/en.blog.wordpress.com/123' ); } ); } ); describe( '#getVisibility', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.getVisibility() === undefined ); } ); it( 'should return public when password and private are not set', function() { assert( postUtils.getVisibility( {} ) === 'public' ); } ); it( 'should return private when post#status is private', function() { assert( postUtils.getVisibility( { status: 'private' } ) === 'private' ); } ); it( 'should return password when post#password is set', function() { assert( postUtils.getVisibility( { password: '******' } ) === 'password' ); } ); } ); describe( '#isPrivate', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.isPrivate() === undefined ); } ); it( 'should return true when post.status is private', function() { assert( postUtils.isPrivate( { status: 'private' } ) ); } ); it( 'should return false when post.status is not private', function() { assert( ! postUtils.isPrivate( { status: 'draft' } ) ); } ); } ); describe( '#isPublished', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.isPublished() === undefined ); } ); it( 'should return true when post.status is private', function() { assert( postUtils.isPublished( { status: 'private' } ) ); } ); it( 'should return true when post.status is publish', function() { assert( postUtils.isPublished( { status: 'publish' } ) ); } ); it( 'should return false when post.status is not publish or private', function() { assert( ! postUtils.isPublished( { status: 'draft' } ) ); } ); } ); describe( '#isPending', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.isPending() === undefined ); } ); it( 'should return true when post.status is pending', function() { assert( postUtils.isPending( { status: 'pending' } ) ); } ); it( 'should return false when post.status is not pending', function() { assert( ! postUtils.isPending( { status: 'draft' } ) ); } ); } ); describe( '#isBackDatedPublished', function() { it( 'should return false when no post is supplied', function() { assert( ! postUtils.isBackDatedPublished() ); } ); it( 'should return false when status !== future', function() { assert( ! postUtils.isBackDatedPublished( { status: 'draft' } ) ); } ); it( 'should return false when status === future and date is in future', function() { const tenMinutes = 1000 * 60; const postDate = Date.now() + tenMinutes; assert( ! postUtils.isBackDatedPublished( { status: 'future', date: postDate } ) ); } ); it( 'should return true when status === future and date is in the past', function() { const tenMinutes = 1000 * 60; const postDate = Date.now() - tenMinutes; assert( postUtils.isBackDatedPublished( { status: 'future', date: postDate } ) ); } ); } ); describe( '#removeSlug', function() { it( 'should return undefined when no path is supplied', function() { assert( postUtils.removeSlug() === undefined ); } ); it( 'should strip slug on post URL', function() { const noSlug = postUtils.removeSlug( 'https://en.blog.wordpress.com/2015/08/26/new-action-bar/' ); assert( noSlug === 'https://en.blog.wordpress.com/2015/08/26/' ); } ); it( 'should strip slug on page URL', function() { const noSlug = postUtils.removeSlug( 'https://en.blog.wordpress.com/a-test-page/' ); assert( noSlug === 'https://en.blog.wordpress.com/' ); } ); } ); describe( '#getPermalinkBasePath', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.getPermalinkBasePath() === undefined ); } ); it( 'should return post.URL when post is published', function() { const path = postUtils.getPermalinkBasePath( { status: 'publish', URL: 'https://en.blog.wordpress.com/2015/08/26/new-action-bar/' } ); assert( path === 'https://en.blog.wordpress.com/2015/08/26/' ); } ); it( 'should use permalink_URL when not published and present', function() { const path = postUtils.getPermalinkBasePath( { other_URLs: { permalink_URL: 'http://zo.mg/a/permalink/%post_name%/' }, URL: 'https://en.blog.wordpress.com/2015/08/26/new-action-bar/' } ); assert( path === 'http://zo.mg/a/permalink/' ); } ); } ); describe( '#getPagePath', function() { it( 'should return undefined when no post is supplied', function() { assert( postUtils.getPagePath() === undefined ); } ); it( 'should return post.URL without slug when page is published', function() { const path = postUtils.getPagePath( { status: 'publish', URL: 'http://zo.mg/a/permalink/' } ); assert( path === 'http://zo.mg/a/' ); } ); it( 'should use permalink_URL when not published and present', function() { const path = postUtils.getPagePath( { status: 'draft', other_URLs: { permalink_URL: 'http://zo.mg/a/permalink/%post_name%/' } } ); assert( path === 'http://zo.mg/a/permalink/' ); } ); } ); describe( '#getFeaturedImageId()', function() { it( 'should return undefined when no post is specified', function() { assert( postUtils.getFeaturedImageId() === undefined ); } ); it( 'should return a non-URL featured_image property', function() { const id = postUtils.getFeaturedImageId( { featured_image: 'media-1', post_thumbnail: { ID: 1 } } ); assert( id === 'media-1' ); } ); it( 'should return a `null` featured_image property', function() { // This describes the behavior of unassigning a featured image // from the current post const id = postUtils.getFeaturedImageId( { featured_image: null, post_thumbnail: { ID: 1 } } ); assert( id === null ); } ); it( 'should fall back to post thumbnail object ID if exists, if featured_image is URL', function() { const id = postUtils.getFeaturedImageId( { featured_image: 'https://example.com/image.png', post_thumbnail: { ID: 1 } } ); assert( id === 1 ); } ); it( 'should return undefined if featured_image is URL and post thumbnail object doesn\'t exist', function() { const id = postUtils.getFeaturedImageId( { featured_image: 'https://example.com/image.png' } ); assert( id === undefined ); } ); } ); } );
describe( 'StatsParser', () => { let statsParser, data; useMockery(); useFakeDom(); before( () => { statsParser = require( '../stats-parser' )(); data = require( './fixtures/data' ); } ); describe( 'stat types', () => { it( 'should have a statsClicks function', () => { assert.isFunction( statsParser.statsClicks, 'it should have a statsClick function' ); } ); it( 'should have a statsTags function', () => { assert.isFunction( statsParser.statsTags, 'it should have a statsTag function' ); } ); } ); describe( 'statsClicks', () => { it( 'should parse a clicks response properly', () => { // we have to pass in some context to calculate start of period on this call const mockContext = { options: { period: 'day', date: '2014-09-12' } }; const response = statsParser.statsClicks.call( mockContext, data.successResponses.statsClicks.body ); assert.lengthOf( response.data, 10 ); assert.equal( response.data[ 0 ].label, 'example.com' ); assert.equal( response.data[ 0 ].value, 126 ); assert.isNull( response.data[ 0 ].children ); assert.isNull( response.data[ 0 ].icon ); assert.lengthOf( response.data[ 2 ].children, 3 ); // check a record with children } ); } ); describe( 'statsTags', () => { it( 'should parse tags response properly', () => { const response = statsParser.statsTags( data.successResponses.statsTags.body ); const item = response.data[ 2 ]; assert.lengthOf( response.data, 9 ); // tags labels are arrays of labels, right? assert.isArray( item.label, 'Label should be array' ); assert.deepEqual( map( item.label, 'label' ), [ 'supertag', 'supertag-transition' ] ); assert.deepEqual( map( item.label, 'labelIcon' ), [ 'tag', 'tag' ] ); assert.isNull( item.label[0].link ); assert.equal( item.value, 480 ); } ); } ); describe( 'statsCountryViews', () => { it( 'should parse countryViews payload properly', () => { const mockContext = { options: { period: 'day', date: '2014-09-12' } }; const response = statsParser.statsCountryViews.call( mockContext, data.successResponses.statsCountryViews.body ); const item = response.data[ 0 ]; assert.lengthOf( response.data, 5 ); assert.equal( item.label, 'United States' ); assert.equal( item.value, 54 ); } ); } ); } );
describe( 'wpcom-api', () => { let dispatch, settingsModule; useSandbox( sandbox => { dispatch = sandbox.spy(); } ); useMockery( () => { mockery.registerMock( 'lib/user', () => ( { fetch() {} } ) ); settingsModule = require( '../' ); } ); describe( 'user settings request', () => { describe( '#requestUserSettings', () => { it( 'should dispatch HTTP GET request to me/settings endpoint', () => { const action = { type: 'DUMMY' }; settingsModule.requestUserSettings( { dispatch }, action ); expect( dispatch ).to.have.been.calledOnce; expect( dispatch ).to.have.been.calledWith( http( { apiVersion: '1.1', method: 'GET', path: '/me/settings', }, action ) ); } ); } ); describe( '#storeFetchedUserSettings', () => { it( 'should dispatch user settings update', () => { const action = { type: 'DUMMY' }; settingsModule.storeFetchedUserSettings( { dispatch }, action, { language: 'qix', } ); expect( dispatch ).to.have.been.calledOnce; expect( dispatch ).to.have.been.calledWith( updateUserSettings( { language: 'qix', } ) ); } ); it( 'should decode HTML entities returned in some fields of HTTP response', () => { const action = { type: 'DUMMY' }; settingsModule.storeFetchedUserSettings( { dispatch }, action, { display_name: 'baz & qix', description: 'foo & bar', user_URL: 'http://example.com?a=b&c=d', } ); expect( dispatch ).to.have.been.calledWith( updateUserSettings( { display_name: 'baz & qix', description: 'foo & bar', user_URL: 'http://example.com?a=b&c=d', } ) ); } ); } ); } ); describe( 'user settings save', () => { describe( '#saveUserSettings', () => { it( 'should dispatch POST request to me/settings using unsavedSettings from state', () => { const getState = () => ( { userSettings: { settings: { foo: 'bar' }, unsavedSettings: { foo: 'baz' } } } ); const action = { type: 'DUMMY' }; settingsModule.saveUserSettings( { dispatch, getState }, action, null ); expect( dispatch ).to.have.been.calledOnce; expect( dispatch ).to.have.been.calledWith( http( { apiVersion: '1.1', method: 'POST', path: '/me/settings', body: { foo: 'baz' } }, action ) ); } ); it( 'should dispatch POST request to me/settings using explicit settingsOverride', () => { const getState = () => ( {} ); const action = { type: 'DUMMY', settingsOverride: { foo: 'baz' } }; settingsModule.saveUserSettings( { dispatch, getState }, action, null ); expect( dispatch ).to.have.been.calledOnce; expect( dispatch ).to.have.been.calledWith( http( { apiVersion: '1.1', method: 'POST', path: '/me/settings', body: { foo: 'baz' } }, action ) ); } ); it( 'should not dispatch any HTTP request when there are no unsaved settings', () => { const getState = () => ( { userSettings: { settings: {}, unsavedSettings: {} } } ); const action = { type: 'DUMMY' }; settingsModule.saveUserSettings( { dispatch, getState }, action, null ); expect( dispatch ).to.not.have.been.called; } ); } ); describe( '#finishUserSettingsSave', () => { it( 'should dispatch user settings update and clear all unsaved settings on full save', () => { const action = { type: 'DUMMY' }; settingsModule.finishUserSettingsSave( { dispatch }, action, { language: 'qix', } ); expect( dispatch ).to.have.been.calledTwice; expect( dispatch ).to.have.been.calledWith( updateUserSettings( { language: 'qix', } ) ); expect( dispatch ).to.have.been.calledWith( clearUnsavedUserSettings() ); } ); it( 'should dispatch user settings update and clear only one unsaved setting on partial save', () => { const data = { language: 'qix', }; const action = { type: 'DUMMY', settingsOverride: data }; settingsModule.finishUserSettingsSave( { dispatch }, action, data ); expect( dispatch ).to.have.been.calledTwice; expect( dispatch ).to.have.been.calledWith( updateUserSettings( { language: 'qix' } ) ); expect( dispatch ).to.have.been.calledWith( clearUnsavedUserSettings( [ 'language' ] ) ); } ); it( 'should decode HTML entities returned in some fields of HTTP response', () => { const action = { type: 'DUMMY' }; settingsModule.finishUserSettingsSave( { dispatch }, action, { display_name: 'baz & qix', description: 'foo & bar', user_URL: 'http://example.com?a=b&c=d', } ); expect( dispatch ).to.have.been.calledWith( updateUserSettings( { display_name: 'baz & qix', description: 'foo & bar', user_URL: 'http://example.com?a=b&c=d', } ) ); } ); } ); } ); } );
describe( 'index', function() { let React, ReactDom, ReactInjection, DomainList, TestUtils, noticeTypes, component, sandbox; useSandbox( newSandbox => sandbox = newSandbox ); useFakeDom.withContainer(); useMockery( mockery => { require( 'test/helpers/mocks/component-classes' )( mockery ); require( 'test/helpers/mocks/component-tip' )( mockery ); require( 'test/helpers/mocks/data-poller' )( mockery ); mockery.registerMock( 'components/section-nav', EmptyComponent ); } ); useI18n(); before( () => { React = require( 'react' ); ReactDom = require( 'react-dom' ); TestUtils = require( 'react-addons-test-utils' ); noticeTypes = require( '../constants' ); ReactInjection = require( 'react/lib/ReactInjection' ); ReactInjection.Class.injectMixin( require( 'lib/mixins/i18n' ).mixin ); DomainList = require( '../' ).List; } ); const selectedSite = deepFreeze( { slug: 'example.com', ID: 1 } ); const defaultProps = deepFreeze( { domains: { hasLoadedFromServer: true, list: [ { name: 'domain0.com', isPrimary: false }, { name: 'example.com', isPrimary: true } ] }, cart: {}, context: { path: '' }, products: {}, selectedDomainName: 'example.com', selectedSite: selectedSite, sites: { getSelectedSite() { return selectedSite; } }, sitePlans: {} } ); function renderWithProps( props = defaultProps ) { return ReactDom.render( <DomainList { ...props } />, useFakeDom.getContainer() ); } describe( 'regular cases', function() { beforeEach( function() { component = renderWithProps(); } ); afterEach( function() { ReactDom.unmountComponentAtNode( useFakeDom.getContainer() ); } ); it( 'should list two domains', () => { assert.equal( [].slice.call( ReactDom.findDOMNode( component ).querySelectorAll( '.domain-management-list-item' ) ).length, 2 ); } ); } ); describe( 'setting primary domain', () => { describe( 'when not enabled', () => { beforeEach( () => { component = renderWithProps(); } ); afterEach( function() { ReactDom.unmountComponentAtNode( useFakeDom.getContainer() ); } ); it( 'should show "Change Primary Domain" button', () => { assert( ReactDom.findDOMNode( component ).querySelector( '.domain-management-list__change-primary-button' ) ); } ); it( 'should enable upon clicking the button', () => { const button = ReactDom.findDOMNode( component ).querySelector( '.domain-management-list__change-primary-button' ); TestUtils.Simulate.click( button ); assert( component.state.changePrimaryDomainModeEnabled ); } ); } ); describe( 'when enabled', () => { beforeEach( () => { component = renderWithProps(); const button = ReactDom.findDOMNode( component ).querySelector( '.domain-management-list__change-primary-button' ); TestUtils.Simulate.click( button ); } ); afterEach( function() { ReactDom.unmountComponentAtNode( useFakeDom.getContainer() ); } ); it( 'should show the cancel button', () => { assert( ReactDom.findDOMNode( component.refs.cancelChangePrimaryButton ) ); } ); it( 'should disable upon clicking cancel button', () => { const button = ReactDom.findDOMNode( component.refs.cancelChangePrimaryButton ); TestUtils.Simulate.click( button ); assert( ! component.state.changePrimaryDomainModeEnabled ); } ); it( 'should set the primaryDomainIndex correctly', () => { const primaryDomainIndex = 1; // from data above assert.equal( component.state.primaryDomainIndex, primaryDomainIndex ); } ); describe( '#handleUpdatePrimaryDomain', () => { let setPrimaryDomainStub, setPrimaryDomainResolve, setPrimaryDomainReject; beforeEach( () => { setPrimaryDomainStub = sandbox.stub( component, 'setPrimaryDomain' ).returns( new Promise( ( resolve, reject ) => { setPrimaryDomainResolve = resolve; setPrimaryDomainReject = reject; } ) ); } ); it( 'should not call setPrimaryDomain with on trying to set the already primary domain', () => { component.handleUpdatePrimaryDomain( 1, defaultProps.domains.list[ 1 ] ); setPrimaryDomainResolve(); assert( ! component.state.settingPrimaryDomain ); assert( ! component.state.changePrimaryDomainModeEnabled ); assert( ! setPrimaryDomainStub.called ); } ); it( 'should call setPrimaryDomain with a domain name', ( done ) => { component.handleUpdatePrimaryDomain( 0, defaultProps.domains.list[ 0 ] ); assert( component.state.settingPrimaryDomain ); assert( component.state.primaryDomainIndex === 0 ); assert( setPrimaryDomainStub.calledWith( defaultProps.domains.list[ 0 ].name ), '#setPrimaryDomain should be called with the domain name' ); setPrimaryDomainResolve(); setTimeout( () => { assert( ! component.state.settingPrimaryDomain, 'Setting Primary Domain should be false' ); assert( ! component.changePrimaryDomainModeEnabled ); assert.equal( component.state.notice.type, noticeTypes.PRIMARY_DOMAIN_CHANGE_SUCCESS ); assert.equal( component.state.notice.previousDomainName, defaultProps.domains.list[ 1 ].name ); done(); }, 0 ); } ); it( 'should handle errors and revert the optimistic updates', ( done ) => { component.handleUpdatePrimaryDomain( 0, defaultProps.domains.list[ 0 ] ); setPrimaryDomainReject(); setTimeout( () => { assert( ! component.state.settingPrimaryDomain ); assert( component.state.changePrimaryDomainModeEnabled ); assert.equal( component.state.primaryDomainIndex, 1 ); assert.equal( component.state.notice.type, noticeTypes.PRIMARY_DOMAIN_CHANGE_FAIL ); done(); }, 0 ); } ); } ); } ); describe( 'when less than 2 domains', () => { beforeEach( () => { const oneDomain = deepFreeze( { domains: { hasLoadedFromServer: true, list: [ { name: 'example.com', isPrimary: true } ] } } ); const propsWithOneDomain = deepFreeze( Object.assign( {}, defaultProps, oneDomain ) ); component = renderWithProps( propsWithOneDomain ); } ); it( 'should not show "Change Primary Domain" button', () => { const button = ReactDom.findDOMNode( component ).querySelector( '.domain-management-list__change-primary-button' ); assert( button === null ); } ); } ); } ); } );
describe( 'utils', function() { var flows, utils; useFilesystemMocks( __dirname ); useI18n(); useMockery( ( mockery ) => { mockery.registerMock( 'lib/abtest', { abtest: () => '' } ); flows = require( 'signup/config/flows' ); utils = require( '../utils' ); } ); describe( 'getLocale', function() { it( 'should find the locale anywhere in the params', function() { assert.equal( utils.getLocale( { lang: 'fr' } ), 'fr' ); assert.equal( utils.getLocale( { stepName: 'fr' } ), 'fr' ); assert.equal( utils.getLocale( { flowName: 'fr' } ), 'fr' ); } ); it( 'should return undefined if no locale is present in the params', function() { assert.equal( utils.getLocale( { stepName: 'theme-selection', flowName: 'flow-one' } ), undefined ); } ); } ); describe( 'getStepName', function() { it( 'should find the step name in either the stepName or flowName fragment', function() { assert.equal( utils.getStepName( { stepName: 'user' } ), 'user' ); assert.equal( utils.getStepName( { flowName: 'user' } ), 'user' ); } ); it( 'should return undefined if no step name is found', function() { assert.equal( utils.getStepName( { flowName: 'account' } ), undefined ); } ); } ); describe( 'getFlowName', function() { afterEach( function() { flows.filterFlowName = null; } ); it( 'should find the flow name in the flowName fragment if present', function() { assert.equal( utils.getFlowName( { flowName: 'other' } ), 'other' ); } ); it( 'should return the default flow if the flow is missing', function() { assert.equal( utils.getFlowName( {} ), 'main' ); } ); it( 'should return the result of filterFlowName if it is a function and the flow is missing', function() { flows.filterFlowName = sinon.stub().returns( 'filtered' ); assert.equal( utils.getFlowName( {} ), 'filtered' ); } ); it( 'should return the result of filterFlowName if it is a function and the flow is not valid', function() { flows.filterFlowName = sinon.stub().returns( 'filtered' ); assert.equal( utils.getFlowName( { flowName: 'invalid' } ), 'filtered' ); } ); it( 'should return the result of filterFlowName if it is a function and the requested flow is present', function() { flows.filterFlowName = sinon.stub().returns( 'filtered' ); assert.equal( utils.getFlowName( { flowName: 'other' } ), 'filtered' ); } ); it( 'should return the passed flow if the result of filterFlowName is not valid', function() { flows.filterFlowName = sinon.stub().returns( 'foobar' ); assert.equal( utils.getFlowName( { flowName: 'other' } ), 'other' ); } ); it( 'should call filterFlowName with the default flow if it is a function and the flow is not valid', function() { flows.filterFlowName = sinon.stub().returns( 'filtered' ); utils.getFlowName( { flowName: 'invalid' } ); assert( flows.filterFlowName.calledWith( 'main' ) ); } ); it( 'should call filterFlowName with the requested flow if it is a function and the flow is valid', function() { flows.filterFlowName = sinon.stub().returns( 'filtered' ); utils.getFlowName( { flowName: 'other' } ); assert( flows.filterFlowName.calledWith( 'other' ) ); } ); } ); describe( 'getValidPath', function() { it( 'should redirect to the default if no flow is present', function() { assert.equal( utils.getValidPath( {} ), '/start/user' ); } ); it( 'should redirect to the current flow default if no step is present', function() { assert.equal( utils.getValidPath( { flowName: 'account' } ), '/start/account/user' ); } ); it( 'should redirect to the default flow if the flow is the default', function() { assert.equal( utils.getValidPath( { flowName: 'main' } ), '/start/user' ); } ); it( 'should redirect invalid steps to the default flow if no flow is present', function() { assert.equal( utils.getValidPath( { stepName: 'fr', stepSectionName: 'fr' } ), '/start/user/fr' ); } ); it( 'should preserve a valid locale to the default flow if one is specified', function() { assert.equal( utils.getValidPath( { stepName: 'fr', stepSectionName: 'abc' } ), '/start/user/abc/fr' ); } ); it( 'should redirect invalid steps to the current flow default', function() { assert.equal( utils.getValidPath( { flowName: 'account', stepName: 'fr', stepSectionName: 'fr' } ), '/start/account/user/fr' ); } ); it( 'should preserve a valid locale if one is specified', function() { assert.equal( utils.getValidPath( { flowName: 'account', stepName: 'fr', stepSectionName: 'abc' } ), '/start/account/user/abc/fr' ); } ); it( 'should handle arbitrary step section names', function() { var randomStepSectionName = 'random-step-section-' + Math.random(); assert.equal( utils.getValidPath( { flowName: 'account', stepName: 'user', stepSectionName: randomStepSectionName, lang: 'fr' } ), '/start/account/user/' + randomStepSectionName + '/fr' ); } ); it( 'should handle arbitrary step section names in the default flow', function() { var randomStepSectionName = 'random-step-section-' + Math.random(); assert.equal( utils.getValidPath( { stepName: 'user', stepSectionName: randomStepSectionName, lang: 'fr' } ), '/start/user/' + randomStepSectionName + '/fr' ); } ); } ); describe( 'getValueFromProgressStore', function() { const testStore = [ { stepName: 'empty' }, { stepName: 'site', site: 'calypso' } ]; const config = { stepName: 'site', fieldName: 'site', signupProgressStore: testStore }; it( 'should return the value of the field if it exists', function() { assert.equal( utils.getValueFromProgressStore( config ), 'calypso' ); } ); it( 'should return null if the field is not present', function() { delete testStore[ 1 ].site; assert.equal( utils.getValueFromProgressStore( config ), null ); } ); } ); describe( 'mergeFormWithValue', function() { const config = { fieldName: 'username', fieldValue: 'calypso' }; it( 'should return the form with the field added if the field doesn\'t have a value', function() { const form = { username: {} }; config.form = form; assert.deepEqual( utils.mergeFormWithValue( config ), { username: { value: 'calypso' } } ); } ); it( 'should return the form unchanged if there is already a value in the form', function() { const form = { username: { value: 'wordpress' } }; config.form = form; assert.equal( utils.mergeFormWithValue( config ), form ); } ); } ); } );
describe( '#signupStep User', () => { let User, testElement, rendered, EMPTY_COMPONENT; useFakeDom(); useMockery( ( mockery ) => { EMPTY_COMPONENT = require( 'test/helpers/react/empty-component' ); mockery.registerMock( 'lib/abtest', noop ); mockery.registerMock( 'lib/analytics', {} ); mockery.registerMock( 'components/signup-form', EMPTY_COMPONENT ); mockery.registerMock( 'signup/step-wrapper', EMPTY_COMPONENT ); mockery.registerMock( 'signup/utils', { getFlowSteps: function( flow ) { let flowSteps = null; if ( 'userAsFirstStepInFlow' === flow ) { flowSteps = [ 'user' ]; } else { flowSteps = [ 'theme', 'domains', 'user' ]; } return flowSteps; }, getNextStepName: identity, getStepUrl: identity, getPreviousStepName: identity } ); } ); before( () => { User = require( '../' ); User.prototype.translate = ( string ) => string; } ); it( 'should show community subheader text if User step is first in the flow', () => { testElement = React.createElement( User, { subHeaderText: 'first subheader message', flowName: 'userAsFirstStepInFlow' } ); rendered = TestUtils.renderIntoDocument( testElement ); expect( rendered.state.subHeaderText ).to.equal( 'Welcome to the wonderful WordPress.com community' ); } ); it( 'should show provided subheader text if User step is not first in the flow', () => { testElement = React.createElement( User, { subHeaderText: 'test subheader message', flowName: 'someOtherFlow' } ); rendered = TestUtils.renderIntoDocument( testElement ); expect( rendered.state.subHeaderText ).to.equal( 'test subheader message' ); } ); describe( '#updateComponentProps', () => { let node, spyComponentProps, component; beforeEach( () => { node = document.createElement( 'div' ); spyComponentProps = sinon.spy( User.prototype, 'componentWillReceiveProps' ); const element = React.createElement( User, { subHeaderText: 'test subheader message', flowName: 'someOtherFlow' } ); component = ReactDOM.render( element, node ); } ); afterEach( () => { User.prototype.componentWillReceiveProps.restore(); } ); it( 'should show community subheader text when new flow has user as first step', () => { const testProps = { subHeaderText: 'My test message', flowName: 'userAsFirstStepInFlow' }; expect( spyComponentProps.calledOnce ).to.equal( false ); ReactDOM.render( React.createElement( User, testProps ), node ); expect( spyComponentProps.calledOnce ).to.equal( true ); expect( component.state.subHeaderText ).to.equal( 'Welcome to the wonderful WordPress.com community' ); } ); it( 'should show provided subheader text when new flow doesn\'t have user as first step', () => { const testProps = { subHeaderText: 'My test message', flowName: 'another test message test' }; expect( spyComponentProps.calledOnce ).to.equal( false ); ReactDOM.render( React.createElement( User, testProps ), node ); expect( spyComponentProps.calledOnce ).to.equal( true ); expect( component.state.subHeaderText ).to.equal( 'My test message' ); } ); } ); } );
describe( 'utils', () => { const currentState = deepFreeze( { test: [ 'one', 'two', 'three' ] } ), actionSerialize = { type: SERIALIZE }, actionDeserialize = { type: DESERIALIZE }; let createReducer, reducer; useMockery( ( mockery ) => { mockery.registerMock( 'lib/warn', noop ); createReducer = require( 'state/utils' ).createReducer; } ); describe( '#createReducer()', () => { context( 'only default behavior', () => { before( () => { reducer = createReducer(); } ); it( 'should return a function', () => { expect( reducer ).to.be.a.function; } ); it( 'should return initial state when invalid action passed', () => { const invalidAction = {}; expect( reducer( currentState, invalidAction ) ).to.be.deep.equal( currentState ); } ); it( 'should return initial state when unknown action type passed', () => { const unknownAction = { type: 'UNKNOWN' }; expect( reducer( currentState, unknownAction ) ).to.be.deep.equal( currentState ); } ); it( 'should return default null state when serialize action type passed', () => { expect( reducer( currentState, actionSerialize ) ).to.be.null; } ); it( 'should return default null state when deserialize action type passed', () => { expect( reducer( currentState, actionDeserialize ) ).to.be.null; } ); } ); context( 'with reducers and default state provided', () => { const initialState = {}, TEST_ADD = 'TEST_ADD'; before( () => { reducer = createReducer( initialState, { [ TEST_ADD ]: ( state, action ) => { return { test: [ ...state.test, action.value ] }; } } ); } ); it( 'should return default {} state when SERIALIZE action type passed', () => { expect( reducer( currentState, actionSerialize ) ).to.be.equal( initialState ); } ); it( 'should return default {} state when DESERIALIZE action type passed', () => { expect( reducer( currentState, actionDeserialize ) ).to.be.equal( initialState ); } ); it( 'should add new value to test array when acc action passed', () => { const addAction = { type: TEST_ADD, value: 'four' }; const newState = reducer( currentState, addAction ); expect( newState ).to.not.equal( currentState ); expect( newState ).to.be.eql( { test: [ 'one', 'two', 'three', 'four' ] } ); } ); } ); context( 'with schema provided', () => { const initialState = {}; before( () => { reducer = createReducer( initialState, {}, testSchema ); } ); it( 'should return initial state when serialize action type passed', () => { expect( reducer( currentState, actionSerialize ) ).to.be.deep.equal( currentState ); } ); it( 'should return initial state when valid initial state and deserialize action type passed', () => { expect( reducer( currentState, actionDeserialize ) ).to.be.deep.equal( currentState ); } ); it( 'should return default state when invalid initial state and deserialize action type passed', () => { expect( reducer( { invalid: 'state' }, actionDeserialize ) ).to.be.deep.equal( initialState ); } ); } ); context( 'with default actions overrides', () => { const overriddenState = { overridden: 'state' }; before( () => { reducer = createReducer( null, { [ SERIALIZE ]: () => overriddenState, [ DESERIALIZE ]: () => overriddenState } ); } ); it( 'should return overridden state when serialize action type passed', () => { expect( reducer( currentState, actionSerialize ) ).to.be.deep.equal( overriddenState ); } ); it( 'should return overridden state when deserialize action type passed', () => { expect( reducer( currentState, actionDeserialize ) ).to.be.deep.equal( overriddenState ); } ); } ); it( 'should cache the serialize result on custom serialization behavior', () => { const monitor = stub().returnsArg( 0 ); reducer = createReducer( [], { [ SERIALIZE ]: monitor, TEST_ADD: ( state ) => [ ...state, state.length ] }, testSchema ); let state; state = reducer( state, { type: SERIALIZE } ); state = reducer( state, { type: SERIALIZE } ); state = reducer( state, { type: 'TEST_ADD' } ); state = reducer( state, { type: SERIALIZE } ); state = reducer( state, { type: SERIALIZE } ); state = reducer( state, { type: 'TEST_ADD' } ); state = reducer( state, { type: SERIALIZE } ); expect( monitor ).to.have.been.calledThrice; expect( state ).to.eql( [ 0, 1 ] ); } ); } ); } );
describe( 'helper', () => { const { discoverPost } = fixtures; let helper; useMockery( mockery => { mockery.registerMock( 'config', () => fixtures.discoverSiteId ); mockery.registerMock( 'lib/user/utils', { getLocaleSlug: () => 'en' } ); } ); useFakeDom(); before( () => { helper = require( '../helper' ); } ); describe( 'isDiscoverPost', () => { it( 'returns true if discover metadata is present', () => { assert.isTrue( helper.isDiscoverPost( discoverPost ) ); } ); it( 'returns true if the site id is discovery_blog_id', () => { const withoutMetadata = omit( fixtures.discoverSiteFormat, 'discover_metadata' ); assert.isTrue( helper.isDiscoverPost( withoutMetadata ) ); } ); it( 'returns false if the site is not disover or discover metadata is not present', () => { assert.isFalse( helper.isDiscoverPost( fixtures.nonDiscoverPost ) ); } ); it( 'returns false if the post is undefined', () => { assert.isFalse( helper.isDiscoverPost() ); } ); } ); describe( 'isDiscoverSitePick', () => { it( 'returns true if the post is a site pick', () => { assert.isTrue( helper.isDiscoverSitePick( fixtures.discoverSiteFormat ) ); } ); it( 'returns false if the post is not a site pick', () => { assert.isFalse( helper.isDiscoverSitePick( discoverPost ) ); } ); it( 'returns false if the post is undefined', () => { assert.isFalse( helper.isDiscoverSitePick() ); } ); } ); describe( 'isInternalDiscoverPost', () => { it( 'returns true if the post is internal to wpcom', () => { assert.isTrue( helper.isInternalDiscoverPost( discoverPost ) ); } ); it( 'returns false if the post is not internal to wpcom', () => { assert.isFalse( helper.isInternalDiscoverPost( fixtures.externalDiscoverPost ) ); } ); } ); describe( 'getSiteUrl', () => { it( 'returns a reader route if the post is internal', () => { assert.match( helper.getSiteUrl( discoverPost ), /^\/read\/blogs/ ); } ); it( 'returns the permalink if the post is not internal', () => { const permalink = get( fixtures.externalDiscoverPost, 'discover_metadata.permalink' ); assert.equal( permalink, helper.getSiteUrl( fixtures.externalDiscoverPost ) ); } ); it( 'returns undefined if the post is not a discover post', () => { assert.isUndefined( helper.getSiteUrl( fixtures.nonDiscoverPost ) ); } ); } ); describe( 'hasSource', () => { it( 'returns true if the post is not a site pick', () => { assert.isTrue( helper.hasSource( discoverPost ) ); } ); it( 'returns false if the post is a site pick', () => { assert.isFalse( helper.hasSource( fixtures.discoverSiteFormat ) ); } ); it( 'returns false if the post is undefined', () => { assert.isFalse( helper.hasSource() ); } ); } ); describe( 'getSourceData', () => { it( 'returns empty object if the post is not a discover post', () => { assert.deepEqual( {}, helper.getSourceData( fixtures.nonDiscoverPost ) ); } ); it( 'returns empty object if the post is external', () => { assert.deepEqual( {}, helper.getSourceData( fixtures.externalDiscoverPost ) ); } ); it( 'returns blog id if the post is a discover site pick', () => { const fixtureData = { blogId: get( fixtures.discoverSiteFormat, 'discover_metadata.featured_post_wpcom_data.blog_id' ), postId: undefined, }; assert.deepEqual( fixtureData, helper.getSourceData( fixtures.discoverSiteFormat ) ); } ); it( 'returns the post and blog id', () => { const fixtureData = { blogId: get( discoverPost, 'discover_metadata.featured_post_wpcom_data.blog_id' ), postId: get( discoverPost, 'discover_metadata.featured_post_wpcom_data.post_id' ), }; assert.deepEqual( fixtureData, helper.getSourceData( discoverPost ) ); } ); } ); describe( 'getLinkProps', () => { it( 'returns empty props if the post is internal', () => { const siteUrl = helper.getSiteUrl( discoverPost ); assert.deepEqual( helper.getLinkProps( siteUrl ), { rel: '', target: '' } ); } ); it( 'returns props for external posts', () => { const siteUrl = helper.getSiteUrl( fixtures.externalDiscoverPost ); assert.deepEqual( helper.getLinkProps( siteUrl ), { rel: 'external', target: '_blank' } ); } ); } ); describe( 'getSourceFollowUrl', () => { it( 'returns the site url if its a discover pick to an internal site', () => { const followUrl = helper.getSourceFollowUrl( discoverPost ); assert.equal( followUrl, get( discoverPost, 'discover_metadata.attribution.blog_url' ) ); } ); it( 'returns undefined if the post is not a discover pick', () => { const followUrl = helper.getSourceFollowUrl( fixtures.nonDiscoverPost ); assert.isUndefined( followUrl ); } ); } ); } );
describe( 'MediaActions', function() { let mediaGet, mediaList, mediaAdd, mediaAddUrls, mediaUpdate, mediaDelete, MediaActions, sandbox, Dispatcher, PostEditStore, MediaListStore; useFakeDom(); useMockery(); before( function() { Dispatcher = require( 'dispatcher' ); PostEditStore = require( 'lib/posts/post-edit-store' ); MediaListStore = require( '../list-store' ); mockery.registerMock( './library-selected-store', { getAll: function() { return [ DUMMY_ITEM ]; } } ); mockery.registerMock( './store', { get: function() { return DUMMY_ITEM; } } ); mockery.registerMock( 'lib/wp', { site: function( siteId ) { return { addMediaFiles: mediaAdd.bind( siteId ), addMediaUrls: mediaAddUrls.bind( siteId ), mediaList: mediaList.bind( siteId ), media: function( mediaId ) { return { get: mediaGet.bind( [ siteId, mediaId ].join() ), update: mediaUpdate.bind( [ siteId, mediaId ].join() ), 'delete': mediaDelete.bind( [ siteId, mediaId ].join() ) }; } }; } } ); mockery.registerMock( 'lodash/uniqueId', function() { return 'media-1'; } ); mockery.registerMock( 'lodash/isPlainObject', function( obj ) { // In the browser, our DUMMY_UPLOAD will be an instanceof // window.File, but File is not provided by jsdom if ( obj === DUMMY_UPLOAD ) { return false; } return isPlainObject( obj ); } ); MediaActions = require( '../actions' ); } ); beforeEach( function() { sandbox = sinon.sandbox.create(); sandbox.stub( Dispatcher, 'handleServerAction' ); sandbox.stub( Dispatcher, 'handleViewAction' ); mediaGet = sandbox.stub().callsArgWithAsync( 0, null, DUMMY_API_RESPONSE ); mediaList = sandbox.stub().callsArgWithAsync( 1, null, DUMMY_API_RESPONSE ); mediaAdd = sandbox.stub().returns( Promise.resolve( DUMMY_API_RESPONSE ) ); mediaAddUrls = sandbox.stub().returns( Promise.resolve( DUMMY_API_RESPONSE ) ); mediaUpdate = sandbox.stub().callsArgWithAsync( 1, null, DUMMY_API_RESPONSE ); mediaDelete = sandbox.stub().callsArgWithAsync( 0, null, DUMMY_API_RESPONSE ); MediaActions._fetching = {}; window.FileList = function() {}; window.URL = { createObjectURL: sandbox.stub() }; } ); afterEach( function() { sandbox.restore(); delete window.FileList; delete window.URL; } ); after( function() { mockery.deregisterAll(); mockery.disable(); } ); describe( '#setQuery()', function() { it( 'should dispatch the SET_MEDIA_QUERY action', function() { MediaActions.setQuery( DUMMY_SITE_ID, DUMMY_QUERY ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'SET_MEDIA_QUERY', siteId: DUMMY_SITE_ID, query: DUMMY_QUERY } ); } ); } ); describe( '#fetch()', function() { it( 'should call to the WordPress.com REST API', function( done ) { Dispatcher.handleViewAction.restore(); sandbox.stub( Dispatcher, 'handleViewAction', function() { expect( MediaActions._fetching ).to.have.all.keys( [ [ DUMMY_SITE_ID, DUMMY_ITEM.ID ].join() ] ); } ); MediaActions.fetch( DUMMY_SITE_ID, DUMMY_ITEM.ID ); expect( Dispatcher.handleViewAction ).to.have.been.calledOnce; expect( mediaGet ).to.have.been.calledOn( [ DUMMY_SITE_ID, DUMMY_ITEM.ID ].join() ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', error: null, siteId: DUMMY_SITE_ID, data: DUMMY_API_RESPONSE } ); done(); } ); } ); it( 'should not allow simultaneous request for the same item', function() { MediaActions.fetch( DUMMY_SITE_ID, DUMMY_ITEM.ID ); MediaActions.fetch( DUMMY_SITE_ID, DUMMY_ITEM.ID ); expect( mediaGet ).to.have.been.calledOnce; } ); it( 'should allow simultaneous request for different items', function() { MediaActions.fetch( DUMMY_SITE_ID, DUMMY_ITEM.ID ); MediaActions.fetch( DUMMY_SITE_ID, DUMMY_ITEM.ID + 1 ); expect( mediaGet ).to.have.been.calledTwice; } ); } ); describe( '#fetchNextPage()', function() { it( 'should call to the WordPress.com REST API', function( done ) { var query = MediaListStore.getNextPageQuery( DUMMY_SITE_ID ); MediaActions.fetchNextPage( DUMMY_SITE_ID ); expect( mediaList ).to.have.been.calledOn( DUMMY_SITE_ID ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEMS', error: null, siteId: DUMMY_SITE_ID, data: DUMMY_API_RESPONSE, query: query } ); done(); } ); } ); } ); describe( '#add()', function() { it( 'should accept a single upload', function() { MediaActions.add( DUMMY_SITE_ID, DUMMY_UPLOAD ); expect( Dispatcher.handleViewAction ).to.have.been.calledOnce; expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'CREATE_MEDIA_ITEM' } ); } ); it( 'should accept an array of uploads', function() { MediaActions.add( DUMMY_SITE_ID, [ DUMMY_UPLOAD, DUMMY_UPLOAD ] ); expect( Dispatcher.handleViewAction ).to.have.been.calledTwice; expect( Dispatcher.handleViewAction ).to.have.always.been.calledWithMatch( { type: 'CREATE_MEDIA_ITEM' } ); } ); it( 'should accept a file URL', function() { return MediaActions.add( DUMMY_SITE_ID, DUMMY_URL ).then( () => { expect( mediaAddUrls ).to.have.been.calledWithMatch( {}, DUMMY_URL ); } ); } ); it( 'should accept a FileList of uploads', function() { var uploads = [ DUMMY_UPLOAD, DUMMY_UPLOAD ]; uploads.__proto__ = new window.FileList(); // eslint-disable-line no-proto MediaActions.add( DUMMY_SITE_ID, uploads ); expect( Dispatcher.handleViewAction ).to.have.been.calledTwice; expect( Dispatcher.handleViewAction ).to.have.always.been.calledWithMatch( { type: 'CREATE_MEDIA_ITEM' } ); } ); it( 'should accept a Blob object wrapper and pass it as "file" parameter', function() { return MediaActions.add( DUMMY_SITE_ID, DUMMY_BLOB_UPLOAD ).then( () => { expect( mediaAdd ).to.have.been.calledWithMatch( {}, { file: DUMMY_BLOB_UPLOAD } ); expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, id: 'media-1', data: DUMMY_API_RESPONSE.media[ 0 ] } ); } ); } ); it( 'should call to the WordPress.com REST API', function() { return MediaActions.add( DUMMY_SITE_ID, DUMMY_UPLOAD ).then( () => { expect( mediaAdd ).to.have.been.calledWithMatch( {}, DUMMY_UPLOAD ); expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, id: 'media-1', data: DUMMY_API_RESPONSE.media[ 0 ] } ); } ); } ); it( 'should immediately receive a transient object', function() { MediaActions.add( DUMMY_SITE_ID, DUMMY_UPLOAD ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'CREATE_MEDIA_ITEM', data: { ID: 'media-1', file: DUMMY_UPLOAD.name, 'transient': true } } ); } ); it( 'should attach file upload to a post if one is being edited', function() { sandbox.stub( PostEditStore, 'get' ).returns( { ID: 200 } ); return MediaActions.add( DUMMY_SITE_ID, DUMMY_UPLOAD ).then( () => { expect( mediaAdd ).to.have.been.calledWithMatch( {}, { file: DUMMY_UPLOAD, parent_id: 200 } ); } ); } ); it( 'should attach URL upload to a post if one is being edited', function() { sandbox.stub( PostEditStore, 'get' ).returns( { ID: 200 } ); return MediaActions.add( DUMMY_SITE_ID, DUMMY_URL ).then( () => { expect( mediaAddUrls ).to.have.been.calledWithMatch( {}, { url: DUMMY_URL, parent_id: 200 } ); } ); } ); it( 'should upload in series', () => { // An awkward test, but the idea is that at the point at which // handleServerAction is called for the first received media, // only the first of the two items should have started uploading. Dispatcher.handleServerAction.restore(); sandbox.stub( Dispatcher, 'handleServerAction' ).throws(); return MediaActions.add( DUMMY_SITE_ID, [ DUMMY_UPLOAD, DUMMY_UPLOAD ] ).then( () => { expect( Dispatcher.handleServerAction ).to.have.thrown; } ).catch( () => { expect( mediaAdd ).to.have.been.calledOnce; } ); } ); } ); describe( '#edit()', function() { var item = { ID: 100, description: 'Example' }; it( 'should immediately edit the existing item', function() { MediaActions.edit( DUMMY_SITE_ID, item ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, data: assign( {}, DUMMY_ITEM, item ) } ); } ); } ); describe( '#update()', function() { var item = { ID: 100, description: 'Example' }; it( 'should accept a single item', function() { MediaActions.update( DUMMY_SITE_ID, item ); expect( mediaUpdate ).to.have.been.calledOnce; } ); it( 'should accept an array of items', function() { MediaActions.update( DUMMY_SITE_ID, [ item, item ] ); expect( mediaUpdate ).to.have.been.calledTwice; } ); it( 'should immediately update the existing item', function() { MediaActions.update( DUMMY_SITE_ID, item ); expect( mediaUpdate ).to.have.been.calledWithMatch( item ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, data: assign( {}, DUMMY_ITEM, item ) } ); } ); it( 'should call to the WordPress.com REST API', function( done ) { MediaActions.update( DUMMY_SITE_ID, item ); expect( mediaUpdate ).to.have.been.calledWithMatch( item ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'RECEIVE_MEDIA_ITEM', error: null, siteId: DUMMY_SITE_ID, data: DUMMY_API_RESPONSE } ); done(); } ); } ); } ); describe( '#delete()', function() { var item = { ID: 100 }; it( 'should accept a single item', function() { MediaActions.delete( DUMMY_SITE_ID, item ); expect( mediaDelete ).to.have.been.calledOnce; } ); it( 'should accept an array of items', function() { MediaActions.delete( DUMMY_SITE_ID, [ item, item ] ); expect( mediaDelete ).to.have.been.calledTwice; } ); it( 'should call to the WordPress.com REST API', function( done ) { MediaActions.delete( DUMMY_SITE_ID, item ); expect( mediaDelete ).to.have.been.calledOn( [ DUMMY_SITE_ID, item.ID ].join() ); process.nextTick( function() { expect( Dispatcher.handleServerAction ).to.have.been.calledWithMatch( { type: 'REMOVE_MEDIA_ITEM', error: null, siteId: DUMMY_SITE_ID, data: DUMMY_API_RESPONSE } ); done(); } ); } ); it( 'should immediately remove the item', function() { MediaActions.delete( DUMMY_SITE_ID, item ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'REMOVE_MEDIA_ITEM', siteId: DUMMY_SITE_ID, data: item } ); } ); } ); describe( '#clearValidationErrors()', function() { it( 'should dispatch the `CLEAR_MEDIA_VALIDATION_ERRORS` action with the specified siteId', function() { MediaActions.clearValidationErrors( DUMMY_SITE_ID ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'CLEAR_MEDIA_VALIDATION_ERRORS', siteId: DUMMY_SITE_ID, itemId: undefined } ); } ); it( 'should dispatch the `CLEAR_MEDIA_VALIDATION_ERRORS` action with the specified siteId and itemId', function() { MediaActions.clearValidationErrors( DUMMY_SITE_ID, DUMMY_ITEM.ID ); expect( Dispatcher.handleViewAction ).to.have.been.calledWithMatch( { type: 'CLEAR_MEDIA_VALIDATION_ERRORS', siteId: DUMMY_SITE_ID, itemId: DUMMY_ITEM.ID } ); } ); } ); } );
describe( 'markup', function() { let sandbox, markup, sites; useFakeDom(); useSandbox( ( newSandbox ) => sandbox = newSandbox ); useMockery( mockery => { mockery.registerMock( 'lib/wp', { me: () => ( { get: noop } ) } ); } ); before( () => { markup = require( '../markup' ); sites = require( 'lib/sites-list' )(); } ); beforeEach( () => { sandbox.restore(); } ); describe( '#get()', function() { it( 'should return an empty string if not passed any arguments', function() { var value = markup.get(); expect( value ).to.equal( '' ); } ); it( 'should defer to a specific mime type handler if one exists', function() { sandbox.stub( markup.mimeTypes, 'image' ); markup.get( { mime_type: 'image/png' } ); expect( markup.mimeTypes.image ).to.have.been.called; } ); it( 'should return a link for a mime type prefix without a specific handler', function() { sandbox.stub( markup, 'link' ); markup.get( { mime_type: 'application/pdf' } ); expect( markup.link ).to.have.been.called; } ); } ); describe( '#link()', function() { it( 'should return a link for a mime type prefix without a specific handler', function() { var value = markup.link( { URL: 'http://example.com/wp-content/uploads/document.pdf', title: 'document' } ); expect( value ).to.equal( '<a href="http://example.com/wp-content/uploads/document.pdf" title="document">document</a>' ); } ); } ); describe( '#caption()', function() { it( 'should accept a media object, returning a caption element', function() { var value = markup.caption( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', caption: 'Logo', thumbnails: {}, width: 276 } ); expect( value.type ).to.equal( 'dl' ); expect( ReactDomServer.renderToStaticMarkup( value ) ).to.equal( '<dl class="wp-caption" style="width:276px;"><dt class="wp-caption-dt"><img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png" alt="Automattic" width="276" class="alignnone size-full wp-image-1"/></dt><dd class="wp-caption-dd">Logo</dd></dl>' ); } ); it( 'should accept a non-captioned image, returning null', function() { var value = markup.caption( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', thumbnails: {}, width: 276 } ); expect( value ).to.be.null; } ); it( 'should accept a captioned string, returning a React element', function() { var value = markup.caption( '[caption id="attachment_1627" align="aligncenter" width="660"]<img class="size-full wp-image-1627" src="https://andrewmduthietest.files.wordpress.com/2015/01/img_0372.jpg" alt="Example" width="660" height="660" /> Ceramic[/caption]' ); expect( value.type ).to.equal( 'dl' ); expect( ReactDomServer.renderToStaticMarkup( value ) ).to.equal( '<dl class="wp-caption aligncenter" style="width:660px;"><dt class="wp-caption-dt"><img class="size-full wp-image-1627" src="https://andrewmduthietest.files.wordpress.com/2015/01/img_0372.jpg" alt="Example" width="660" height="660" /></dt><dd class="wp-caption-dd">Ceramic</dd></dl>' ); } ); } ); describe( '.mimeTypes', function() { describe( '#image()', function() { beforeEach( function() { sandbox.stub( sites, 'getSelectedSite', function() { return {}; } ); } ); it( 'should not set width auto if media width cannot be determined', function() { var value = markup.mimeTypes.image( { ID: 'media-4', URL: 'blob:http%3A//example.com/ddd1d6b0-f31b-4937-ae9e-97f1d660cf71', thumbnails: {} } ); expect( value ).to.equal( '<img src="blob:http%3A//example.com/ddd1d6b0-f31b-4937-ae9e-97f1d660cf71" class="alignnone size-full wp-image-media-4"/>' ); } ); it( 'should return an img element for an image', function() { var value = markup.mimeTypes.image( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', thumbnails: {}, width: 276 } ); expect( value ).to.equal( '<img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png" alt="Automattic" width="276" class="alignnone size-full wp-image-1"/>' ); } ); it( 'should respect the max width for a site size', function() { sites.getSelectedSite.restore(); sandbox.stub( sites, 'getSelectedSite', function() { return { options: { image_large_width: 1024, image_large_height: 1024 } }; } ); const value = markup.mimeTypes.image( { ID: 1, URL: 'http://example.com/image.png', thumbnails: {}, width: 5000, height: 2000 }, { size: 'large' } ); sites.getSelectedSite.restore(); expect( value ).to.equal( '<img src="http://example.com/image.png?w=1024" width="1024" height="410" class="alignnone size-large wp-image-1"/>' ); } ); it( 'should respect the max height for a site size', function() { sites.getSelectedSite.restore(); sandbox.stub( sites, 'getSelectedSite', function() { return { options: { image_large_width: 1024, image_large_height: 1024 } }; } ); const value = markup.mimeTypes.image( { ID: 1, URL: 'http://example.com/image.png', thumbnails: {}, width: 2000, height: 5000 }, { size: 'large' } ); sites.getSelectedSite.restore(); expect( value ).to.equal( '<img src="http://example.com/image.png?w=410" width="410" height="1024" class="alignnone size-large wp-image-1"/>' ); } ); it( 'should include a resize parameter if forceResize option passed', function() { var value = markup.mimeTypes.image( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', thumbnails: {}, width: 276 }, { forceResize: true } ); expect( value ).to.equal( '<img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png?w=276" alt="Automattic" width="276" class="alignnone size-full wp-image-1"/>' ); } ); it( 'should avoid XSS because React', function() { var value = markup.mimeTypes.image( { ID: 1, URL: '""><SCRIPT>alert("XSS")</SCRIPT>"', thumbnails: {}, width: 276 } ); expect( value ).to.equal( '<img src="""><SCRIPT>alert("XSS")</SCRIPT>"" width="276" class="alignnone size-full wp-image-1"/>' ); } ); it( 'should attempt to find the site\'s thumbnail width if a size is specified', function() { var value; sites.getSelectedSite.restore(); sandbox.stub( sites, 'getSelectedSite', function() { return { options: { image_large_width: 200 } }; } ); value = markup.mimeTypes.image( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', thumbnails: {}, width: 276 }, { size: 'large' } ); sites.getSelectedSite.restore(); expect( value ).to.equal( '<img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png?w=200" alt="Automattic" width="200" class="alignnone size-large wp-image-1"/>' ); } ); it( 'should attempt to find the media\'s own thumbnail width if a size is specified', function() { var value; sites.getSelectedSite.restore(); sandbox.stub( sites, 'getSelectedSite', function() { return { jetpack: true }; } ); value = markup.mimeTypes.image( { ID: 1, URL: 'http://example.com/wp-content/uploads/2015/05/logo11w.png', alt: 'WordPress', thumbnails: { large: 'http://example.com/wp-content/uploads/2015/05/logo11w-1024x1024.png' }, width: 5380 }, { size: 'large' } ); sites.getSelectedSite.restore(); expect( value ).to.equal( '<img src="http://example.com/wp-content/uploads/2015/05/logo11w-1024x1024.png" alt="WordPress" width="1024" class="alignnone size-large wp-image-1"/>' ); } ); it( 'should wrap a captioned image in a caption shortcode', function() { var value = markup.mimeTypes.image( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', caption: 'Logo', thumbnails: {}, width: 276 } ); expect( value ).to.equal( '[caption id="attachment_1" width="276"]<img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png" alt="Automattic" width="276" class="alignnone size-full wp-image-1"/> Logo[/caption]' ); } ); it( 'should calculate the height when specifying a size', function() { var value = markup.mimeTypes.image( { ID: 1, URL: 'https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png', alt: 'Automattic', thumbnails: {}, width: 2760, height: 300 }, { size: 'large' } ); expect( value ).to.equal( '<img src="https://s1.wp.com/wp-content/themes/a8c/automattic-2011/images/automattic-logo.png?w=1024" alt="Automattic" width="1024" height="111" class="alignnone size-large wp-image-1"/>' ); } ); } ); describe( '#audio()', function() { it( 'should return an `audio` shortcode for an audio item', function() { var value = markup.mimeTypes.audio( { URL: 'http://example.com/wp-content/uploads/2015/06/loop.mp3' } ); expect( value ).to.equal( '[audio src="http://example.com/wp-content/uploads/2015/06/loop.mp3"][/audio]' ); } ); } ); describe( '#video()', function() { it( 'should return a `wpvideo` shortcode for a VideoPress video', function() { var value = markup.mimeTypes.video( { videopress_guid: '11acMj3O' } ); expect( value ).to.equal( '[wpvideo 11acMj3O]' ); } ); it( 'should return a `video` shortcode for a video', function() { var value = markup.mimeTypes.video( { URL: 'http://example.com/wp-content/uploads/2015/06/loop.mp4', height: 454, width: 1436 } ); expect( value ).to.equal( '[video src="http://example.com/wp-content/uploads/2015/06/loop.mp4" height="454" width="1436"][/video]' ); } ); } ); } ); } );
describe( 'devdocs', () => { let app, server; allowNetworkAccess(); useMockery(); before( done => { mockery.registerMock( 'config', { isEnabled: () => true } ); // for speed - the real search index is very large mockery.registerMock( 'devdocs/search-index', { index: {} } ); mockery.registerMock( 'lunr', { Index: { load: () => null } } ); devdocs = require( '../' ); app = devdocs(); server = app.listen( 9993, done ); } ); after( done => { server.close( done ); } ); it( 'should return documents', done => { getDocument( 'README.md', ( err, res ) => { expect( err ).to.be.null; expect( res.statusCode ).to.equal( 200 ); expect( res.text ).to.contain( '<a href="./.github/CONTRIBUTING.md">' ); done(); } ); } ); it( 'should return documents with relative paths', done => { getDocument( 'client/components/infinite-list', '../../lib/mixins/infinite-scroll/README.md', ( err, res ) => { expect( err ).to.be.null; expect( res.statusCode ).to.equal( 200 ); expect( res.text ).to.contain( '<h1 id="infinite-scroll">' ); done(); } ); } ); it( 'should return the README.md by default', done => { getDocument( 'client/lib/mixins/infinite-scroll', ( err, res ) => { expect( err ).to.be.null; expect( res.statusCode ).to.equal( 200 ); expect( res.text ).to.contain( '<h1 id="infinite-scroll">' ); done(); } ); } ); it( 'should not allow viewing files outside the Calypso repo', done => { const pathOutsideCalypso = fspath.join( __dirname, '..', '..', '..', '..', 'outside-calypso.md' ); fs.writeFileSync( pathOutsideCalypso, 'oh no' ); getDocument( '../outside-calypso.md', ( err, res ) => { fs.unlinkSync( pathOutsideCalypso ); expect( err ).not.to.be.null; expect( res.statusCode ).to.equal( 404 ); expect( res.text ).to.equal( 'File does not exist' ); done(); } ); } ); it( 'should not allow viewing JavaScript files', done => { getDocument( 'index.js', ( err, res ) => { expect( err ).not.to.be.null; expect( res.statusCode ).to.equal( 404 ); expect( res.text ).to.equal( 'File does not exist' ); done(); } ); } ); } );
describe( 'index', function() { useFakeDom(); useFilesystemMocks( __dirname ); useMockery( mockery => { mockery.registerSubstitute( 'matches-selector', 'component-matches-selector' ); } ); let SitesDropdown; before( function() { SitesDropdown = require( '..' ).SitesDropdown; } ); describe( 'component rendering', function() { it( 'should render a dropdown component initially closed', function() { const sitesDropdown = shallow( <SitesDropdown /> ); expect( sitesDropdown.hasClass( 'sites-dropdown' ) ).to.be.true; expect( sitesDropdown.hasClass( 'is-open' ) ).to.be.false; } ); it( 'should toggle the dropdown, when it is clicked', function() { const toggleOpenSpy = sinon.spy( SitesDropdown.prototype, 'toggleOpen' ); const sitesDropdown = shallow( <SitesDropdown /> ); sitesDropdown.find( '.sites-dropdown__selected' ).simulate( 'click' ); sinon.assert.calledOnce( toggleOpenSpy ); expect( sitesDropdown.hasClass( 'is-open' ) ).to.be.true; toggleOpenSpy.restore(); } ); } ); describe( 'component state', function() { it( 'should initially consider as selected the selectedOrPrimarySiteId prop', function() { const sitesDropdown = shallow( <SitesDropdown selectedSiteId={ 1234567 } /> ); expect( sitesDropdown.instance().state.selectedSiteId ).to.be.equal( 1234567 ); } ); } ); describe( 'selectSite', function() { it( 'should update the `selectedSiteSlug`, and `open` state properties', function() { const setStateSpy = sinon.spy(); const siteSelectedSpy = sinon.spy(); const fakeContext = { setState: setStateSpy, props: { onSiteSelect: siteSelectedSpy, } }; SitesDropdown.prototype.selectSite.call( fakeContext, 12345 ); sinon.assert.calledOnce( siteSelectedSpy ); sinon.assert.calledWith( siteSelectedSpy, 12345 ); sinon.assert.calledOnce( setStateSpy ); sinon.assert.calledWith( setStateSpy, { open: false, selectedSiteId: 12345 } ); } ); } ); describe( 'onClose', function() { it( 'should set `open` state property to false', function() { const setStateSpy = sinon.spy(); const fakeContext = { setState: setStateSpy, props: { onClose: noop } }; SitesDropdown.prototype.onClose.call( fakeContext ); sinon.assert.calledOnce( setStateSpy ); sinon.assert.calledWith( setStateSpy, { open: false } ); } ); it( 'should run the component `onClose` hook, when it is provided', function() { const onCloseSpy = sinon.spy(); const fakeContext = { setState: noop, props: { onClose: onCloseSpy } }; SitesDropdown.prototype.onClose.call( fakeContext ); sinon.assert.calledOnce( onCloseSpy ); } ); } ); describe( 'getSelectedSite', function() { xit( 'should return a site on the basis of the component `selectedSiteSlug` state property', function() { const fakeState = { selectedSiteId: 42 }; const selectedSite = SitesDropdown.prototype.getSelectedSite.call( { state: fakeState } ); expect( selectedSite ).to.be.eql( { ID: 42, slug: 'foo.wordpress.com' } ); } ); } ); } );