From 61a38d059138aa3e56d2c1924f753fadf008dc7f Mon Sep 17 00:00:00 2001 From: Aniko Litvanyi Date: Tue, 19 Sep 2017 17:31:37 +0200 Subject: [PATCH] Release/3.2.0 (#41) * [KFI]fix(Epics): Remove requestContent call from the initSensenetStoreEpic * [KFI]fix(Reducers): Fix userAvatarPath For now userAvatarPath contains the Avatar field's value so it doesn't matter if the avatar is a binary or a reference * [KFI]test(ReducerTests): Fix userAvatarPath test * [KFI]fix(Reducers): Fix fetch getError and order reducers * [KFI]feat(Actions): Add two new Actions for selection and deselecting a Content * [KFI]feat(Reducers): Change selected reducer to handle select and deselect actions * [KFI]test(Actions): Add tests for select and deselect actions * [KFI]test(Reducers): Add test for testing the selected Reducer handling select and deselect Actions * [KFI]feat(Actions): Add actions for getting sn Actions of a content * [KFI]test(Actions): Add tests for testing the new sn action getter Actions * [KFI]feat(Reducers): Complete the Reducer of the content items * [KFI]test(Reducers): Add test for testing the childrenactions reducer * [KFI]feat(Reducers): Add a isOpened reducer This reducer holds the id of the content where the actionmenu was opened last * [KFI]fix(Reducers): Change action in isOpened reducer to REQUEST_CONTENT_ACTION * [KFI]test(Reducers): Add test for testing isOpened reducer * [KFI]feat(Reducers): Add a function to return the currently opened items id * [KFI]refactor(Reducer): Rename getOpenedContentId to getOpenedContent * [KFI]test(Reducers): Add a test for testing getOpenedContent function * [KFI]feat(Actions): Add id as input attr to RequestContentActions Action * [KFI]feat(Reducers): Add getChildrenActions function to return actions from the state tree * [KFI]feat(Actions): Change RequestContentActions first input param to content * [KFI]feat(Epics): Add getContentActions Epic * [KFI]fix(GetActions): Fix GetActions action * [KFI]feat(Reducers): Add getCurrentContent to get the path of the current content * [KFI]fix(Epics): Fix loadContentActions epic * [KFI]test: Improve epic tests * [KFI]chore: Update version number --- package.json | 2 +- src/Actions.ts | 49 ++++++++++++++ src/Epics.ts | 20 ++++-- src/Reducers.ts | 90 ++++++++++++++++---------- test/ActionsTests.ts | 49 ++++++++++++++ test/EpicsTests.ts | 145 ++++++++++++++++++++++++++++++++++++++++-- test/ReducersTests.ts | 99 ++++++++++++++++++++++++++-- 7 files changed, 402 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index b6f10b0..89eb945 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sn-redux", - "version": "3.1.1", + "version": "3.2.0", "description": "A set of redux actions, reducers and redux-ovbservable epics for Sense/Net ECM", "main": "dist/src/sn-redux.js", "scripts": { diff --git a/src/Actions.ts b/src/Actions.ts index 49aca5d..4e2626e 100644 --- a/src/Actions.ts +++ b/src/Actions.ts @@ -693,4 +693,53 @@ export module Actions { type: 'LOAD_REPOSITORY', repository: repositoryConfig }) + /** + * Action creator for selecting a Content + * @param id {number} The id of the selected Content + * @returns {Object} Returns a redux action. + */ + export const SelectContent = (id) => ({ + type: 'SELECT_CONTENT', + id + }) + /** + * Action creator for deselecting a Content + * @param id {number} The id of the deselected Content + * @returns {Object} Returns a redux action. + */ + export const DeSelectContent = (id) => ({ + type: 'DESELECT_CONTENT', + id + }) + /** + * Action creator for a request for get actions of a content by a given scenario. + * @param content {Content} The name of the scenario + * @param scenario {string} The name of the scenario + * @returns {Object} Returns a redux action. + */ + export const RequestContentActions = (content, scenario?: string) => ({ + type: 'REQUEST_CONTENT_ACTIONS', + content, + scenario + }) + /** + * Action creator for the step getting the actions of a content successfully. + * @param response {any} JSON response of the ajax request. + * @returns {Object} Returns a redux action with a response. + */ + export const RequestContentActionsSuccess = (response: any, id: number) => { + return ({ + type: 'REQUEST_CONTENT_ACTIONS_SUCCESS', + response: response, + id + })} + /** + * Action creator for the step when getting the actions of a content is failed + * @param error {any} JSON response of the ajax request. + * @returns {Object} Returns a redux action with a response. + */ + export const RequestContentActionsFailure = (error: any) => ({ + type: 'REQUEST_CONTENT_ACTIONS_FAILURE', + message: error.message + }) } \ No newline at end of file diff --git a/src/Epics.ts b/src/Epics.ts index ec82a4b..b59beb1 100644 --- a/src/Epics.ts +++ b/src/Epics.ts @@ -3,7 +3,7 @@ import { Reducers } from './Reducers'; import { ActionsObservable, combineEpics } from 'redux-observable'; import { Observable } from '@reactivex/rxjs'; -import { Repository, Content, Collection, ODataApi, Authentication } from 'sn-client-js'; +import { Repository, Content, ContentTypes, Collection, ODataApi, Authentication } from 'sn-client-js'; /** * Module for redux-observable Epics of the sensenet built-in OData actions. @@ -50,7 +50,6 @@ export module Epics { store.dispatch(Actions.LoadRepository(dependencies.repository.Config)) return dependencies.repository.Load(action.path, action.options) .map((response) => { - store.dispatch(Actions.RequestContent(action.path, action.options)) return Actions.ReceiveLoadedContent(response, action.options) }) .catch(error => { @@ -99,8 +98,9 @@ export module Epics { export const loadContentActionsEpic = (action$, store, dependencies?: { repository: Repository.BaseRepository }) => { return action$.ofType('LOAD_CONTENT_ACTIONS') .mergeMap(action => { - return action.content.Actions(action.scenario) - .map(Actions.ReceiveContentActions) + let c = dependencies.repository.HandleLoadedContent(action.content, ContentTypes.GenericContent); + return c.Actions(action.scenario) + .map(result => Actions.ReceiveContentActions(result)) .catch(error => Observable.of(Actions.ReceiveContentActionsFailure(error))) }) } @@ -335,6 +335,15 @@ export module Epics { .catch(error => Observable.of(Actions.UserLogoutFailure(error))) }) } + export const getContentActions = (action$, store, dependencies?: { repository: Repository.BaseRepository }) => { + return action$.ofType('REQUEST_CONTENT_ACTIONS') + .mergeMap(action => { + let c = dependencies.repository.HandleLoadedContent(action.content, ContentTypes.GenericContent); + return c.Actions(action.scenario) + .map(result => Actions.RequestContentActionsSuccess(result, action.content.Id)) + .catch(error => Observable.of(Actions.RequestContentActionsFailure(error))) + }) + } /** * sn-redux root Epic, the main Epic combination that is used on a default sensenet application. Contains Epics related to CRUD operations and thr other built-in sensenet * [OData Actions and Function](http://wiki.sensenet.com/Built-in_OData_actions_and_functions). @@ -358,7 +367,8 @@ export module Epics { restoreversionContentEpic, userLoginEpic, userLogoutEpic, - checkLoginStateEpic + checkLoginStateEpic, + getContentActions ); } diff --git a/src/Reducers.ts b/src/Reducers.ts index 6c0caec..cefda67 100644 --- a/src/Reducers.ts +++ b/src/Reducers.ts @@ -130,14 +130,13 @@ export module Reducers { export const userAvatarPath = (state = '', action) => { switch (action.type) { case 'USER_CHANGED': - return action.user.ImageData ? action.user.ImageData.__mediaresource.media_src : '' + return action.user.Avatar ? action.user.Avatar._deferred : '' default: return state } } - /** - * Reducer combining userName, fullName and userLanguage into a single object, ```user```. + * Reducer combining userName, fullName, userLanguage, userAvatarPath into a single object, ```user```. */ const user = combineReducers({ userName, @@ -195,7 +194,7 @@ export module Reducers { * @returns {Object} state. Returns the next state based on the action. */ export const entities = (state = {}, action) => { - if (action.response && (action.type !== 'USER_LOGIN_SUCCESS' && action.type !== 'LOAD_CONTENT_SUCCESS')) { + if (action.response && (action.type !== 'USER_LOGIN_SUCCESS' && action.type !== 'LOAD_CONTENT_SUCCESS' && action.type !== 'REQUEST_CONTENT_ACTIONS_SUCCESS')) { return (Object).assign({}, state, action.response.entities.entities); } switch (action.type) { @@ -234,40 +233,17 @@ export module Reducers { switch (action.type) { case 'FETCH_CONTENT_FAILURE': return action.message; - case 'CREATE_CONTENT_FAILURE': - case 'UPDATE_CONTENT_FAILURE': - case 'DELETE_CONTENT_FAILURE': - case 'CHECKIN_CONTENT_FAILURE': - case 'CHECKOUT_CONTENT_FAILURE': - case 'PUBLISH_CONTENT_FAILURE': - case 'APPROVE_CONTENT_FAILURE': - case 'REJECT_CONTENT_FAILURE': - case 'UNDOCHECKOUT_CONTENT_FAILURE': - case 'FORCEUNDOCHECKOUT_CONTENT_FAILURE': - case 'RESTOREVERSION_CONTENT_FAILURE': - case 'FETCH_CONTENT_REQUEST': case 'FETCH_CONTENT_SUCCESS': - case 'CREATE_CONTENT_REQUEST': case 'CREATE_CONTENT_SUCCESS': - case 'UPDATE_CONTENT_REQUEST': case 'UPDATE_CONTENT_SUCCESS': - case 'DELETE_CONTENT_REQUEST': case 'DELETE_CONTENT_SUCCESS': - case 'CHECKIN_CONTENT_REQUEST': case 'CHECKIN_CONTENT_SUCCESS': - case 'CHECKOUT_CONTENT_REQUEST': case 'CHECKOUT_CONTENT_SUCCESS': - case 'APPROVE_CONTENT_REQUEST': case 'APPROVE_CONTENT_SUCCESS': - case 'PUBLISH_CONTENT_REQUEST': case 'PUBLISH_CONTENT_SUCCESS': - case 'REJECT_CONTENT_REQUEST': case 'REJECT_CONTENT_SUCCESS': - case 'UNDOCHECKOUT_CONTENT_REQUEST': case 'UNDOCHECKOUT_CONTENT_SUCCESS': - case 'FORCEUNDOCHECKOUT_CONTENT_REQUEST': case 'FORCEUNDOCHECKOUT_CONTENT_SUCCESS': - case 'RESTOREVERSION_CONTENT_REQUEST': case 'RESTOREVERSION_CONTENT_SUCCESS': return null; default: @@ -280,8 +256,13 @@ export module Reducers { * @param {Object} action Represents an action that is called. * @returns {Object} state. Returns the next state based on the action. */ - export const childrenactions = (state = {}, action) => { - return state + export const childrenactions = (state = [], action) => { + switch (action.type) { + case 'REQUEST_CONTENT_ACTIONS_SUCCESS': + return action.response + default: + return state + } } /** * Reducer to handle Actions on the top property in the children object. @@ -343,8 +324,8 @@ export module Reducers { export const order = (state = {}, action) => { switch (action.type) { case 'FETCH_CONTENT_REQUEST': - if (action.options.order) - return action.options.order + if (action.options.orderby) + return action.options.orderby else return state default: @@ -385,6 +366,20 @@ export module Reducers { return state } } + /** + * Reducer to handle Actions on the isOpened property in the children object. + * @param {Object} [state={}] Represents the current state. + * @param {Object} action Represents an action that is called. + * @returns {Object} state. Returns the next state based on the action. + */ + export const isOpened = (state = null, action) => { + switch (action.type) { + case 'REQUEST_CONTENT_ACTIONS_SUCCESS': + return action.id + default: + return state + } + } /** * Reducer combining ids, entities, isFetching, error, top, skip, query, order, filter and select into a single object, ```children```. */ @@ -393,13 +388,14 @@ export module Reducers { entities, isFetching, error: childrenerror, - // actions: childrenactions, + actions: childrenactions, top, skip, query, order, filter, - select + select, + isOpened }) /** * Reducer to handle Actions on the isSaved property in the contentState object. @@ -575,7 +571,15 @@ export module Reducers { * @returns {Object} state. Returns the next state based on the action. */ export const selected = (state = [], action) => { - return state; + switch (action.type) { + case 'SELECT_CONTENT': + return [...state, action.id] + case 'DESELECT_CONTENT': + const index = state.indexOf(action.id) + return [...state.slice(0, index), ...state.slice(index + 1)] + default: + return state + } } /** * Reducer combining session, children, currentcontent and selected into a single object, ```sensenet``` which will be the top-level one. @@ -612,7 +616,7 @@ export module Reducers { * @returns {string} Returns the error message. */ export const getError = (state: any) => { - return state.errorMessage + return state.error }; export const getAuthenticationStatus = (state) => { @@ -626,4 +630,20 @@ export module Reducers { export const getRepositoryUrl = (state) => { return state.session.repository.RepositoryUrl; } + + export const getSelectedContent = (state) => { + return state.selected + } + + export const getOpenedContent = (state) => { + return state.isOpened + } + + export const getChildrenActions = (state) => { + return state.actions + } + + export const getCurrentContent = (state) => { + return state.currentcontent.content + } } \ No newline at end of file diff --git a/test/ActionsTests.ts b/test/ActionsTests.ts index be472cc..e20a738 100644 --- a/test/ActionsTests.ts +++ b/test/ActionsTests.ts @@ -544,4 +544,53 @@ describe('Actions', () => { expect(Actions.LoadRepository(repo)).to.deep.equal(expectedAction) }); }); + describe('SelectContent', () => { + it('should return the select content action', () => { + const expectedAction = { + type: 'SELECT_CONTENT', + id: 1 + } + expect(Actions.SelectContent(1)).to.deep.equal(expectedAction) + }) + }) + describe('DeSelectContent', () => { + it('should return the deselect content action', () => { + const expectedAction = { + type: 'DESELECT_CONTENT', + id: 1 + } + expect(Actions.DeSelectContent(1)).to.deep.equal(expectedAction) + }) + }) + describe('RequestContentActions', () => { + + const content = Content.Create({ DisplayName: 'My content', Id: 123 }, ContentTypes.Task, repo) + it('should return the RequestContentActions action', () => { + const expectedAction = { + type: 'REQUEST_CONTENT_ACTIONS', + content: content, + scenario: 'DMSListItem' + } + expect(Actions.RequestContentActions(content, 'DMSListItem')).to.deep.equal(expectedAction) + }) + it('should return the RequestContentActionsSuccess action', () => { + const expectedAction = { + type: 'REQUEST_CONTENT_ACTIONS_SUCCESS', + response: [ + { + ActionName: 'Rename' + } + ], + id: 1 + } + expect(Actions.RequestContentActionsSuccess([{ ActionName: 'Rename' }], 1)).to.deep.equal(expectedAction) + }) + it('should return the RequestContentActionsFailure action', () => { + const expectedAction = { + type: 'REQUEST_CONTENT_ACTIONS_FAILURE', + message: 'error' + } + expect(Actions.RequestContentActionsFailure({ message: 'error' })).to.deep.equal(expectedAction) + }); + }) }); \ No newline at end of file diff --git a/test/EpicsTests.ts b/test/EpicsTests.ts index 48c7c54..666060d 100644 --- a/test/EpicsTests.ts +++ b/test/EpicsTests.ts @@ -38,7 +38,7 @@ describe('Epics', () => { path: '/workspaces/Project', options: { - select: [ 'Id', 'Path', 'Name', 'Type', 'DisplayName', 'Description', 'Icon' ], + select: ['Id', 'Path', 'Name', 'Type', 'DisplayName', 'Description', 'Icon'], metadata: 'no', inlinecount: 'allpages', expand: undefined, @@ -117,7 +117,7 @@ describe('Epics', () => { }); after(() => { - epicMiddleware.replaceEpic(Epics.fetchContentEpic); + epicMiddleware.replaceEpic(Epics.loadContentEpic); }); it('handles the error', () => { store.dispatch({ type: 'LOAD_CONTENT_REQUEST', path: '/workspaces/Project', options: {} }); @@ -158,10 +158,11 @@ describe('Epics', () => { }); after(() => { - epicMiddleware.replaceEpic(Epics.fetchContentEpic); + epicMiddleware.replaceEpic(Epics.reloadContentEpic); }); + + const content = repo.HandleLoadedContent({ DisplayName: 'My Content', Id: 123, Path: '/workspaces' }, ContentTypes.Task) it('handles the error', () => { - const content = repo.HandleLoadedContent({ DisplayName: 'My Content', Id: 123, Path: '/workspaces' }, ContentTypes.Task) content.Save('/workspaces') store.dispatch({ type: 'RELOAD_CONTENT_REQUEST', content, options: {} }); expect(store.getActions()).to.be.deep.eq([ @@ -171,6 +172,16 @@ describe('Epics', () => { options: {} }]); }) + it('handles the error', () => { + store.dispatch({ type: 'RELOAD_CONTENT_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'RELOAD_CONTENT_REQUEST', + content, + options: {} + }, + { type: 'RELOAD_CONTENT_FAILURE', error: 'error' }]); + }) }); describe('reloadContentFields Epic', () => { let store; @@ -182,8 +193,10 @@ describe('Epics', () => { }); after(() => { - epicMiddleware.replaceEpic(Epics.fetchContentEpic); + epicMiddleware.replaceEpic(Epics.reloadContentFieldsEpic); }); + + const content = repo.HandleLoadedContent({ DisplayName: 'My Content', Id: 123, Path: '/workspaces' }, ContentTypes.Task) it('handles the error', () => { const content = repo.HandleLoadedContent({ DisplayName: 'My Content', Id: 123, Path: '/workspaces' }, ContentTypes.Task) store.dispatch({ type: 'RELOAD_CONTENTFIELDS_REQUEST', content, options: {}, fields: ['DisplayName'] }); @@ -199,6 +212,21 @@ describe('Epics', () => { message: 'XMLHttpRequest is not supported by your browser' }]); }) + it('handles the error', () => { + store.dispatch({ type: 'RELOAD_CONTENTFIELDS_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'RELOAD_CONTENTFIELDS_REQUEST', + content, + options: {}, + fields: ['DisplayName'] + }, + { + type: 'RELOAD_CONTENTFIELDS_FAILURE', + message: 'XMLHttpRequest is not supported by your browser' + }, + { type: 'RELOAD_CONTENTFIELDS_FAILURE', error: 'error' }]); + }) }); describe('createContent Epic', () => { let store; @@ -212,8 +240,8 @@ describe('Epics', () => { after(() => { epicMiddleware.replaceEpic(Epics.createContentEpic); }); + const content = Content.Create({ DisplayName: 'My content', Id: 123, Path: '/workspaces' }, ContentTypes.Task, repo); it('handles the error', () => { - const content = Content.Create({ DisplayName: 'My content', Id: 123, Path: '/workspaces' }, ContentTypes.Task, repo); store.dispatch({ type: 'CREATE_CONTENT_REQUEST', content, contentType: ContentTypes.Task }); expect(store.getActions()).to.be.deep.eq( [{ @@ -222,6 +250,16 @@ describe('Epics', () => { contentType: ContentTypes.Task }]); }) + it('handles the error', () => { + store.dispatch({ type: 'CREATE_CONTENT_FAILURE', error: { message: 'error' } }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'CREATE_CONTENT_REQUEST', + content, + contentType: ContentTypes.Task + }, + { type: 'CREATE_CONTENT_FAILURE', error: { message: 'error' } }]); + }) }); describe('updateContent Epic', () => { let store; @@ -257,8 +295,8 @@ describe('Epics', () => { after(() => { epicMiddleware.replaceEpic(Epics.deleteContentEpic); }); + const content = Content.Create({ DisplayName: 'My content', Id: 123, Path: '/workspaces' }, ContentTypes.Task, repo); it('handles the error', () => { - const content = Content.Create({ DisplayName: 'My content', Id: 123, Path: '/workspaces' }, ContentTypes.Task, repo); store.dispatch({ type: 'DELETE_CONTENT_REQUEST', content, permanently: false }); expect(store.getActions()).to.be.deep.eq( [{ @@ -267,6 +305,16 @@ describe('Epics', () => { permanently: false }]); }) + it('handles the error', () => { + store.dispatch({ type: 'DELETE_CONTENT_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'DELETE_CONTENT_REQUEST', + content, + permanently: false + }, + { type: 'DELETE_CONTENT_FAILURE', error: 'error' }]); + }) }); describe('deleteBatch Epic', () => { let store; @@ -593,4 +641,87 @@ describe('Epics', () => { { type: 'USER_LOGIN_FAILURE', message: null }]); }) }); + describe('getContentActions Epic', () => { + let store; + const epicMiddleware = createEpicMiddleware(Epics.getContentActions, { dependencies: { repository: repo } }); + const mockStore = configureMockStore([epicMiddleware]); + + before(() => { + store = mockStore(); + }); + + after(() => { + epicMiddleware.replaceEpic(Epics.getContentActions); + }); + const content = Content.Create({ Name: 'alba', Id: '2' }, ContentTypes.Task, repo) + it('handles the success', () => { + store.dispatch({ type: 'REQUEST_CONTENT_ACTIONS', content, scenario: 'DMSDemoScenario' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'REQUEST_CONTENT_ACTIONS', + content, + scenario: 'DMSDemoScenario' + }, + { + type: 'REQUEST_CONTENT_ACTIONS_FAILURE', + message: 'XMLHttpRequest is not supported by your browser' + }]); + }) + it('handles the error', () => { + store.dispatch({ type: 'REQUEST_CONTENT_ACTIONS_FAILURE', error: 'error' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'REQUEST_CONTENT_ACTIONS', + content, + scenario: 'DMSDemoScenario' + }, + { + type: 'REQUEST_CONTENT_ACTIONS_FAILURE', + message: 'XMLHttpRequest is not supported by your browser' + }, + { type: 'REQUEST_CONTENT_ACTIONS_FAILURE', error: 'error' }]); + }) + }); + describe('loadContentActionsEpic Epic', () => { + let store; + const epicMiddleware = createEpicMiddleware(Epics.loadContentActionsEpic, { dependencies: { repository: repo } }); + const mockStore = configureMockStore([epicMiddleware]); + + before(() => { + store = mockStore(); + }); + + after(() => { + epicMiddleware.replaceEpic(Epics.loadContentActionsEpic); + }); + const content = Content.Create({ Name: 'alba', Id: '2' }, ContentTypes.Task, repo) + it('handles the success', () => { + store.dispatch({ type: 'LOAD_CONTENT_ACTIONS', content, scenario: 'DMSDemoScenario' }); + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'LOAD_CONTENT_ACTIONS', + content, + scenario: 'DMSDemoScenario' + }, + { + type: 'LOAD_CONTENT_ACTIONS_FAILURE', + error: { message: 'XMLHttpRequest is not supported by your browser' } + }]); + }) + it('handles the error', () => { + store.dispatch({ type: 'LOAD_CONTENT_ACTIONS_FAILURE', error: 'error' }); + + expect(store.getActions()).to.be.deep.eq( + [{ + type: 'LOAD_CONTENT_ACTIONS', + content, + scenario: 'DMSDemoScenario' + }, + { + type: 'LOAD_CONTENT_ACTIONS_FAILURE', + error: { message: 'XMLHttpRequest is not supported by your browser' } + }, + { type: 'LOAD_CONTENT_ACTIONS_FAILURE', error: 'error' }]); + }) + }); }); \ No newline at end of file diff --git a/test/ReducersTests.ts b/test/ReducersTests.ts index 25b2cf3..bd8582a 100644 --- a/test/ReducersTests.ts +++ b/test/ReducersTests.ts @@ -95,7 +95,7 @@ describe('Reducers', () => { expect(Reducers.userAvatarPath(undefined, { type: 'USER_CHANGED', user: { DisplayName: 'Alba Monday' } })).to.be.deep.equal(''); }); it('should return the logged-in users avatars path', () => { - expect(Reducers.userAvatarPath(undefined, { type: 'USER_CHANGED', user: { ImageData: { __mediaresource: { media_src: 'Alba Monday' } } } })).to.be.deep.equal('Alba Monday'); + expect(Reducers.userAvatarPath(undefined, { type: 'USER_CHANGED', user: { Avatar: { _deferred: 'Alba Monday' } } })).to.be.deep.equal('Alba Monday'); }); }) @@ -303,7 +303,18 @@ describe('Reducers', () => { }); describe('childrenactions reducer', () => { it('should return the initial state', () => { - expect(Reducers.childrenactions(undefined, {})).to.be.deep.equal({}); + expect(Reducers.childrenactions(undefined, {})).to.be.deep.equal([]); + }); + it('should handle REQUEST_CONTENT_ACTIONS_SUCCESS', () => { + const action = { + type: 'REQUEST_CONTENT_ACTIONS_SUCCESS', + response: [ + { + ActionName: 'Rename' + } + ] + } + expect(Reducers.childrenactions(undefined, action)).to.be.deep.equal([{ ActionName: 'Rename' }]); }); }); describe('top reducer', () => { @@ -344,7 +355,7 @@ describe('Reducers', () => { expect(Reducers.order(undefined, {})).to.be.deep.equal({}); }); it('should return "DisplayName desc"', () => { - expect(Reducers.order(undefined, { type: 'FETCH_CONTENT_REQUEST', options: { order: 'DisplayName desc' } })).to.be.eq('DisplayName desc'); + expect(Reducers.order(undefined, { type: 'FETCH_CONTENT_REQUEST', options: { orderby: 'DisplayName desc' } })).to.be.eq('DisplayName desc'); }); it('should return initial state', () => { expect(Reducers.order(undefined, { type: 'FETCH_CONTENT_REQUEST', options: {} })).to.be.deep.equal({}); @@ -372,6 +383,18 @@ describe('Reducers', () => { expect(Reducers.select(undefined, { type: 'FETCH_CONTENT_REQUEST', options: {} })).to.be.deep.equal({}); }); }); + describe('isOpened reducer', () => { + it('should return the initial state', () => { + expect(Reducers.isOpened(undefined, {})).to.be.eq(null) + }) + it('should return 1', () => { + const action = { + type: 'REQUEST_CONTENT_ACTIONS_SUCCESS', + id: 1 + } + expect(Reducers.isOpened(undefined, action)).to.be.eq(1) + }) + }) describe('isSaved reducer', () => { it('should return the initial state', () => { expect(Reducers.isSaved(undefined, {})).to.be.deep.equal(true); @@ -614,6 +637,34 @@ describe('Reducers', () => { it('should return the initial state', () => { expect(Reducers.selected(undefined, {})).to.deep.equal([]); }); + it('should return an array with one item with the id 1', () => { + const action = { + type: 'SELECT_CONTENT', + id: 1 + } + expect(Reducers.selected(undefined, action)).to.deep.equal([1]); + }) + it('should return an array with two items with the id 1 and 2', () => { + const action = { + type: 'SELECT_CONTENT', + id: 2 + } + expect(Reducers.selected([1], action)).to.deep.equal([1, 2]); + }) + it('should return an array with one item with the id 1', () => { + const action = { + type: 'DESELECT_CONTENT', + id: 2 + } + expect(Reducers.selected([1, 2], action)).to.deep.equal([1]); + }) + it('should return an empty array', () => { + const action = { + type: 'DESELECT_CONTENT', + id: 1 + } + expect(Reducers.selected([1], action)).to.deep.equal([]); + }) }) describe('getContent', () => { const state = { @@ -676,7 +727,7 @@ describe('Reducers', () => { const state = { ids: [5145, 5146], isFetching: false, - errorMessage: 'error' + error: 'error' } it('should return the value of errorMessage from the current state', () => { expect(Reducers.getError(state)).to.be.eq('error'); @@ -716,4 +767,44 @@ describe('Reducers', () => { expect(Reducers.getRepositoryUrl(state)).to.be.eq('https://dmsservice.demo.sensenet.com'); }); }); + describe('getSelectedContent', () => { + const state = { + selected: [1, 2] + } + it('should return the value of the selected reducers current state, an array with two items', () => { + expect(Reducers.getSelectedContent(state)).to.be.deep.equal([1, 2]) + }) + }) + describe('getOpenedContentId', () => { + const state = { + isOpened: 1 + } + it('should return 1 as the opened items id', () => { + expect(Reducers.getOpenedContent(state)).to.be.eq(1) + }) + }) + describe('getChildrenActions', () => { + const state = { + actions: [ + { + ActionName: 'Rename' + } + ] + } + it('should return 1 as the opened items id', () => { + expect(Reducers.getChildrenActions(state)).to.be.deep.equal([{ ActionName: 'Rename' }]) + }) + }) + describe('getCurrentContent', () => { + const state = { + currentcontent: { + content: { + DisplayName: 'my content' + } + } + } + it('should return the content', () => { + expect(Reducers.getCurrentContent(state)).to.be.deep.equal({ DisplayName: 'my content' }) + }) + }) }); \ No newline at end of file