From 3a3305b9acbb3ac3ac82f9b29c099699b5210fa5 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 24 Jul 2016 18:22:40 +0800 Subject: [web] fix: Flow update changes list order #36 --- web/src/js/ducks/utils/view.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js index c00f00bd..2f1e03fa 100755 --- a/web/src/js/ducks/utils/view.js +++ b/web/src/js/ducks/utils/view.js @@ -57,16 +57,9 @@ export default function reduce(state = defaultState, action) { if (state.indexOf[action.item.id] == null) { return } - const nextState = { - ...state, - ...sortedRemove(state, action.item.id), - } - if (!action.filter(action.item)) { - return nextState - } return { - ...nextState, - ...sortedInsert(nextState, action.item, action.sort) + ...state, + ...sortedUpdate(state, action.item, action.sort), } case RECEIVE: @@ -110,7 +103,7 @@ export function receive(list, filter = defaultFilter, sort = defaultSort) { function sortedInsert(state, item, sort) { const index = sortedIndex(state.data, item, sort) - const data = [...state.data] + const data = [ ...state.data ] const indexOf = { ...state.indexOf } data.splice(index, 0, item) @@ -134,6 +127,28 @@ function sortedRemove(state, id) { return { data, indexOf } } +function sortedUpdate(state, item, sort) { + let data = [ ...state.data ] + let indexOf = { ...state.indexOf } + let index = indexOf[item.id] + data[index] = item + while (index + 1 < data.length && sort(data[index], data[index + 1]) > 0) { + data[index] = data[index + 1] + data[index + 1] = item + indexOf[item.id] = index + 1 + indexOf[data[index].id] = index + ++index + } + while (index > 0 && sort(data[index], data[index - 1]) < 0) { + data[index] = data[index - 1] + data[index - 1] = item + indexOf[item.id] = index - 1 + indexOf[data[index].id] = index + --index + } + return { data, indexOf } +} + function sortedIndex(list, item, sort) { let low = 0 let high = list.length -- cgit v1.2.3 From 7b51f12813ab145304c15f0d39222a2811e6ca4d Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 26 Jul 2016 02:09:54 +0800 Subject: [web] bug fix and add test --- web/src/js/__tests__/ducks/tutils.js | 6 ++++++ web/src/js/__tests__/ducks/ui.js | 36 -------------------------------- web/src/js/__tests__/ducks/ui/header.js | 36 ++++++++++++++++++++++++++++++++ web/src/js/__tests__/ducks/utils/view.js | 6 ++++-- web/src/js/ducks/utils/view.js | 28 +++++++++++++++++++------ 5 files changed, 68 insertions(+), 44 deletions(-) delete mode 100644 web/src/js/__tests__/ducks/ui.js create mode 100644 web/src/js/__tests__/ducks/ui/header.js diff --git a/web/src/js/__tests__/ducks/tutils.js b/web/src/js/__tests__/ducks/tutils.js index 90a21b78..6a543434 100644 --- a/web/src/js/__tests__/ducks/tutils.js +++ b/web/src/js/__tests__/ducks/tutils.js @@ -10,3 +10,9 @@ export function createStore(parts) { applyMiddleware(...[thunk]) ) } + +describe('tutils', () => { + it('do nothing', () => { + return + }) +}) \ No newline at end of file diff --git a/web/src/js/__tests__/ducks/ui.js b/web/src/js/__tests__/ducks/ui.js deleted file mode 100644 index d3242815..00000000 --- a/web/src/js/__tests__/ducks/ui.js +++ /dev/null @@ -1,36 +0,0 @@ -jest.unmock('../../ducks/ui') -jest.unmock('../../ducks/flows') - -import reducer, { setActiveMenu } from '../../ducks/ui' -import * as flowActions from '../../ducks/flows' - -describe('ui reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {}).activeMenu).toEqual('Start') - }) - - it('should return the state for view', () => { - expect(reducer(undefined, setActiveMenu('View')).activeMenu).toEqual('View') - }) - - it('should change the state to Start when deselecting a flow and we a currently at the flow tab', () => { - expect(reducer( - { activeMenu: 'Flow', isFlowSelected: true }, - flowActions.select(undefined)).activeMenu - ).toEqual('Start') - }) - - it('should change the state to Flow when we selected a flow and no flow was selected before', () => { - expect(reducer( - { activeMenu: 'Start', isFlowSelected: false }, - flowActions.select(1)).activeMenu - ).toEqual('Flow') - }) - - it('should not change the state to Flow when OPTIONS tab is selected and we selected a flow and a flow as selected before', () => { - expect(reducer( - { activeMenu: 'Options', isFlowSelected: true }, - flowActions.select(1) - ).activeMenu).toEqual('Options') - }) -}) diff --git a/web/src/js/__tests__/ducks/ui/header.js b/web/src/js/__tests__/ducks/ui/header.js new file mode 100644 index 00000000..8968e636 --- /dev/null +++ b/web/src/js/__tests__/ducks/ui/header.js @@ -0,0 +1,36 @@ +jest.unmock('../../../ducks/ui/header') +jest.unmock('../../../ducks/flows') + +import reducer, { setActiveMenu } from '../../../ducks/ui/header' +import * as flowActions from '../../../ducks/flows' + +describe('header reducer', () => { + it('should return the initial state', () => { + expect(reducer(undefined, {}).activeMenu).toEqual('Start') + }) + + it('should return the state for view', () => { + expect(reducer(undefined, setActiveMenu('View')).activeMenu).toEqual('View') + }) + + it('should change the state to Start when deselecting a flow and we a currently at the flow tab', () => { + expect(reducer( + { activeMenu: 'Flow', isFlowSelected: true }, + flowActions.select(undefined)).activeMenu + ).toEqual('Start') + }) + + it('should change the state to Flow when we selected a flow and no flow was selected before', () => { + expect(reducer( + { activeMenu: 'Start', isFlowSelected: false }, + flowActions.select(1)).activeMenu + ).toEqual('Flow') + }) + + it('should not change the state to Flow when OPTIONS tab is selected and we selected a flow and a flow as selected before', () => { + expect(reducer( + { activeMenu: 'Options', isFlowSelected: true }, + flowActions.select(1) + ).activeMenu).toEqual('Options') + }) +}) diff --git a/web/src/js/__tests__/ducks/utils/view.js b/web/src/js/__tests__/ducks/utils/view.js index f0b147da..af3da173 100644 --- a/web/src/js/__tests__/ducks/utils/view.js +++ b/web/src/js/__tests__/ducks/utils/view.js @@ -66,11 +66,13 @@ describe('view reduce', () => { it('should update item', () => { const state = createState([ { id: 1, val: 1 }, - { id: 2, val: 2 } + { id: 2, val: 2 }, + { id: 3, val: 3 } ]) const result = createState([ { id: 1, val: 1 }, - { id: 2, val: 3 } + { id: 2, val: 3 }, + { id: 3, val: 3 } ]) expect(reduce(state, view.update({ id: 2, val: 3 }))).toEqual(result) }) diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js index 2f1e03fa..fdddc391 100755 --- a/web/src/js/ducks/utils/view.js +++ b/web/src/js/ducks/utils/view.js @@ -54,14 +54,30 @@ export default function reduce(state = defaultState, action) { } case UPDATE: - if (state.indexOf[action.item.id] == null) { - return + let hasOldItem = state.indexOf[action.item.id] !== null && state.indexOf[action.item.id] !== undefined + let hasNewItem = action.filter(action.item) + if (!hasNewItem && !hasOldItem) { + return state } - return { - ...state, - ...sortedUpdate(state, action.item, action.sort), + if (hasNewItem && !hasOldItem) { + return { + ...state, + ...sortedInsert(state, action.item, action.sort) + } } - + if (!hasNewItem && hasOldItem) { + return { + ...state, + ...sortedRemove(state, action.item.id) + } + } + if (hasNewItem && hasOldItem) { + return { + ...state, + ...sortedUpdate(state, action.item, action.sort), + } + } + break; case RECEIVE: { const data = action.list.filter(action.filter).sort(action.sort) -- cgit v1.2.3 From 2b9e5dcd1b93792bcfa1df07352cff1ecffbf36d Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 27 Jul 2016 00:39:01 +0800 Subject: [web] change test files --- web/package.json | 1 + web/src/js/__tests__/ducks/flowView.js | 67 ------------ web/src/js/__tests__/ducks/flowViewSpec.js | 67 ++++++++++++ web/src/js/__tests__/ducks/flows.js | 30 ------ web/src/js/__tests__/ducks/flowsSpec.js | 30 ++++++ web/src/js/__tests__/ducks/tutils.js | 6 -- web/src/js/__tests__/ducks/ui/header.js | 36 ------- web/src/js/__tests__/ducks/ui/headerSpec.js | 36 +++++++ web/src/js/__tests__/ducks/utils/list.js | 63 ----------- web/src/js/__tests__/ducks/utils/listSpec.js | 63 +++++++++++ web/src/js/__tests__/ducks/utils/view.js | 156 --------------------------- web/src/js/__tests__/ducks/utils/viewSpec.js | 156 +++++++++++++++++++++++++++ web/src/js/ducks/utils/view.js | 1 - 13 files changed, 353 insertions(+), 359 deletions(-) delete mode 100644 web/src/js/__tests__/ducks/flowView.js create mode 100644 web/src/js/__tests__/ducks/flowViewSpec.js delete mode 100644 web/src/js/__tests__/ducks/flows.js create mode 100644 web/src/js/__tests__/ducks/flowsSpec.js delete mode 100644 web/src/js/__tests__/ducks/ui/header.js create mode 100644 web/src/js/__tests__/ducks/ui/headerSpec.js delete mode 100644 web/src/js/__tests__/ducks/utils/list.js create mode 100644 web/src/js/__tests__/ducks/utils/listSpec.js delete mode 100644 web/src/js/__tests__/ducks/utils/view.js create mode 100644 web/src/js/__tests__/ducks/utils/viewSpec.js diff --git a/web/package.json b/web/package.json index 81b96adc..66a12501 100644 --- a/web/package.json +++ b/web/package.json @@ -7,6 +7,7 @@ "start": "gulp" }, "jest": { + "testRegex": "__tests__/.*\\Spec.js$", "testPathDirs": [ "/src/js" ], diff --git a/web/src/js/__tests__/ducks/flowView.js b/web/src/js/__tests__/ducks/flowView.js deleted file mode 100644 index d5d9a6d9..00000000 --- a/web/src/js/__tests__/ducks/flowView.js +++ /dev/null @@ -1,67 +0,0 @@ -jest.unmock('../../ducks/flows') -jest.unmock('../../ducks/flowView') -jest.unmock('../../ducks/utils/view') -jest.unmock('../../ducks/utils/list') -jest.unmock('./tutils') - -import { createStore } from './tutils' - -import flows, * as flowActions from '../../ducks/flows' -import flowView, * as flowViewActions from '../../ducks/flowView' - - -function testStore() { - let store = createStore({ - flows, - flowView - }) - for (let i of [1, 2, 3, 4]) { - store.dispatch( - flowActions.addFlow({ id: i }) - ) - } - return store -} - -describe('select relative', () => { - - function testSelect(start, relative, result) { - const store = testStore() - store.dispatch(flowActions.select(start)) - expect(store.getState().flows.selected).toEqual(start ? [start] : []) - store.dispatch(flowViewActions.selectRelative(relative)) - expect(store.getState().flows.selected).toEqual([result]) - } - - describe('previous', () => { - - it('should select the previous flow', () => { - testSelect(3, -1, 2) - }) - - it('should not changed when first flow is selected', () => { - testSelect(1, -1, 1) - }) - - it('should select first flow if no flow is selected', () => { - testSelect(undefined, -1, 1) - }) - - }) - - describe('next', () => { - - it('should select the next flow', () => { - testSelect(2, 1, 3) - }) - - it('should not changed when last flow is selected', () => { - testSelect(4, 1, 4) - }) - - it('should select last flow if no flow is selected', () => { - testSelect(undefined, 1, 4) - }) - - }) -}) diff --git a/web/src/js/__tests__/ducks/flowViewSpec.js b/web/src/js/__tests__/ducks/flowViewSpec.js new file mode 100644 index 00000000..d5d9a6d9 --- /dev/null +++ b/web/src/js/__tests__/ducks/flowViewSpec.js @@ -0,0 +1,67 @@ +jest.unmock('../../ducks/flows') +jest.unmock('../../ducks/flowView') +jest.unmock('../../ducks/utils/view') +jest.unmock('../../ducks/utils/list') +jest.unmock('./tutils') + +import { createStore } from './tutils' + +import flows, * as flowActions from '../../ducks/flows' +import flowView, * as flowViewActions from '../../ducks/flowView' + + +function testStore() { + let store = createStore({ + flows, + flowView + }) + for (let i of [1, 2, 3, 4]) { + store.dispatch( + flowActions.addFlow({ id: i }) + ) + } + return store +} + +describe('select relative', () => { + + function testSelect(start, relative, result) { + const store = testStore() + store.dispatch(flowActions.select(start)) + expect(store.getState().flows.selected).toEqual(start ? [start] : []) + store.dispatch(flowViewActions.selectRelative(relative)) + expect(store.getState().flows.selected).toEqual([result]) + } + + describe('previous', () => { + + it('should select the previous flow', () => { + testSelect(3, -1, 2) + }) + + it('should not changed when first flow is selected', () => { + testSelect(1, -1, 1) + }) + + it('should select first flow if no flow is selected', () => { + testSelect(undefined, -1, 1) + }) + + }) + + describe('next', () => { + + it('should select the next flow', () => { + testSelect(2, 1, 3) + }) + + it('should not changed when last flow is selected', () => { + testSelect(4, 1, 4) + }) + + it('should select last flow if no flow is selected', () => { + testSelect(undefined, 1, 4) + }) + + }) +}) diff --git a/web/src/js/__tests__/ducks/flows.js b/web/src/js/__tests__/ducks/flows.js deleted file mode 100644 index 2b261cb1..00000000 --- a/web/src/js/__tests__/ducks/flows.js +++ /dev/null @@ -1,30 +0,0 @@ -jest.unmock('../../ducks/flows'); - -import reduceFlows, * as flowActions from '../../ducks/flows' - - -describe('select flow', () => { - - let state = reduceFlows(undefined, {}) - for (let i of [1, 2, 3, 4]) { - state = reduceFlows(state, flowActions.addFlow({ id: i })) - } - - it('should be possible to select a single flow', () => { - expect(reduceFlows(state, flowActions.select(2))).toEqual( - { - ...state, - selected: [2], - } - ) - }) - - it('should be possible to deselect a flow', () => { - expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( - { - ...state, - selected: [], - } - ) - }) -}) diff --git a/web/src/js/__tests__/ducks/flowsSpec.js b/web/src/js/__tests__/ducks/flowsSpec.js new file mode 100644 index 00000000..2b261cb1 --- /dev/null +++ b/web/src/js/__tests__/ducks/flowsSpec.js @@ -0,0 +1,30 @@ +jest.unmock('../../ducks/flows'); + +import reduceFlows, * as flowActions from '../../ducks/flows' + + +describe('select flow', () => { + + let state = reduceFlows(undefined, {}) + for (let i of [1, 2, 3, 4]) { + state = reduceFlows(state, flowActions.addFlow({ id: i })) + } + + it('should be possible to select a single flow', () => { + expect(reduceFlows(state, flowActions.select(2))).toEqual( + { + ...state, + selected: [2], + } + ) + }) + + it('should be possible to deselect a flow', () => { + expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( + { + ...state, + selected: [], + } + ) + }) +}) diff --git a/web/src/js/__tests__/ducks/tutils.js b/web/src/js/__tests__/ducks/tutils.js index 6a543434..90a21b78 100644 --- a/web/src/js/__tests__/ducks/tutils.js +++ b/web/src/js/__tests__/ducks/tutils.js @@ -10,9 +10,3 @@ export function createStore(parts) { applyMiddleware(...[thunk]) ) } - -describe('tutils', () => { - it('do nothing', () => { - return - }) -}) \ No newline at end of file diff --git a/web/src/js/__tests__/ducks/ui/header.js b/web/src/js/__tests__/ducks/ui/header.js deleted file mode 100644 index 8968e636..00000000 --- a/web/src/js/__tests__/ducks/ui/header.js +++ /dev/null @@ -1,36 +0,0 @@ -jest.unmock('../../../ducks/ui/header') -jest.unmock('../../../ducks/flows') - -import reducer, { setActiveMenu } from '../../../ducks/ui/header' -import * as flowActions from '../../../ducks/flows' - -describe('header reducer', () => { - it('should return the initial state', () => { - expect(reducer(undefined, {}).activeMenu).toEqual('Start') - }) - - it('should return the state for view', () => { - expect(reducer(undefined, setActiveMenu('View')).activeMenu).toEqual('View') - }) - - it('should change the state to Start when deselecting a flow and we a currently at the flow tab', () => { - expect(reducer( - { activeMenu: 'Flow', isFlowSelected: true }, - flowActions.select(undefined)).activeMenu - ).toEqual('Start') - }) - - it('should change the state to Flow when we selected a flow and no flow was selected before', () => { - expect(reducer( - { activeMenu: 'Start', isFlowSelected: false }, - flowActions.select(1)).activeMenu - ).toEqual('Flow') - }) - - it('should not change the state to Flow when OPTIONS tab is selected and we selected a flow and a flow as selected before', () => { - expect(reducer( - { activeMenu: 'Options', isFlowSelected: true }, - flowActions.select(1) - ).activeMenu).toEqual('Options') - }) -}) diff --git a/web/src/js/__tests__/ducks/ui/headerSpec.js b/web/src/js/__tests__/ducks/ui/headerSpec.js new file mode 100644 index 00000000..8968e636 --- /dev/null +++ b/web/src/js/__tests__/ducks/ui/headerSpec.js @@ -0,0 +1,36 @@ +jest.unmock('../../../ducks/ui/header') +jest.unmock('../../../ducks/flows') + +import reducer, { setActiveMenu } from '../../../ducks/ui/header' +import * as flowActions from '../../../ducks/flows' + +describe('header reducer', () => { + it('should return the initial state', () => { + expect(reducer(undefined, {}).activeMenu).toEqual('Start') + }) + + it('should return the state for view', () => { + expect(reducer(undefined, setActiveMenu('View')).activeMenu).toEqual('View') + }) + + it('should change the state to Start when deselecting a flow and we a currently at the flow tab', () => { + expect(reducer( + { activeMenu: 'Flow', isFlowSelected: true }, + flowActions.select(undefined)).activeMenu + ).toEqual('Start') + }) + + it('should change the state to Flow when we selected a flow and no flow was selected before', () => { + expect(reducer( + { activeMenu: 'Start', isFlowSelected: false }, + flowActions.select(1)).activeMenu + ).toEqual('Flow') + }) + + it('should not change the state to Flow when OPTIONS tab is selected and we selected a flow and a flow as selected before', () => { + expect(reducer( + { activeMenu: 'Options', isFlowSelected: true }, + flowActions.select(1) + ).activeMenu).toEqual('Options') + }) +}) diff --git a/web/src/js/__tests__/ducks/utils/list.js b/web/src/js/__tests__/ducks/utils/list.js deleted file mode 100644 index 72d162f2..00000000 --- a/web/src/js/__tests__/ducks/utils/list.js +++ /dev/null @@ -1,63 +0,0 @@ -jest.unmock('lodash') -jest.unmock('../../../ducks/utils/list') - -import reduce, * as list from '../../../ducks/utils/list' - -describe('list reduce', () => { - - it('should add item', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 }, - { id: 2 }, - { id: 3 } - ]) - expect(reduce(state, list.add({ id: 3 }))).toEqual(result) - }) - - it('should update item', () => { - const state = createState([ - { id: 1, val: 1 }, - { id: 2, val: 2 } - ]) - const result = createState([ - { id: 1, val: 1 }, - { id: 2, val: 3 } - ]) - expect(reduce(state, list.update({ id: 2, val: 3 }))).toEqual(result) - }) - - it('should remove item', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - result.byId[2] = result.indexOf[2] = null - expect(reduce(state, list.remove(2))).toEqual(result) - }) - - it('should replace all items', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - expect(reduce(state, list.receive([{ id: 1 }]))).toEqual(result) - }) -}) - -function createState(items) { - return { - data: items, - byId: _.fromPairs(items.map((item, index) => [item.id, item])), - indexOf: _.fromPairs(items.map((item, index) => [item.id, index])) - } -} diff --git a/web/src/js/__tests__/ducks/utils/listSpec.js b/web/src/js/__tests__/ducks/utils/listSpec.js new file mode 100644 index 00000000..72d162f2 --- /dev/null +++ b/web/src/js/__tests__/ducks/utils/listSpec.js @@ -0,0 +1,63 @@ +jest.unmock('lodash') +jest.unmock('../../../ducks/utils/list') + +import reduce, * as list from '../../../ducks/utils/list' + +describe('list reduce', () => { + + it('should add item', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 }, + { id: 2 }, + { id: 3 } + ]) + expect(reduce(state, list.add({ id: 3 }))).toEqual(result) + }) + + it('should update item', () => { + const state = createState([ + { id: 1, val: 1 }, + { id: 2, val: 2 } + ]) + const result = createState([ + { id: 1, val: 1 }, + { id: 2, val: 3 } + ]) + expect(reduce(state, list.update({ id: 2, val: 3 }))).toEqual(result) + }) + + it('should remove item', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + result.byId[2] = result.indexOf[2] = null + expect(reduce(state, list.remove(2))).toEqual(result) + }) + + it('should replace all items', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + expect(reduce(state, list.receive([{ id: 1 }]))).toEqual(result) + }) +}) + +function createState(items) { + return { + data: items, + byId: _.fromPairs(items.map((item, index) => [item.id, item])), + indexOf: _.fromPairs(items.map((item, index) => [item.id, index])) + } +} diff --git a/web/src/js/__tests__/ducks/utils/view.js b/web/src/js/__tests__/ducks/utils/view.js deleted file mode 100644 index af3da173..00000000 --- a/web/src/js/__tests__/ducks/utils/view.js +++ /dev/null @@ -1,156 +0,0 @@ -jest.unmock('../../../ducks/utils/view') -jest.unmock('lodash') - -import reduce, * as view from '../../../ducks/utils/view' -import _ from 'lodash' - -describe('view reduce', () => { - - it('should filter items', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - expect(reduce(state, view.updateFilter(state.data, item => item.id === 1))).toEqual(result) - }) - - it('should sort items', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 2 }, - { id: 1 } - ]) - expect(reduce(state, view.updateSort((a, b) => b.id - a.id))).toEqual(result) - }) - - it('should add item', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 }, - { id: 2 }, - { id: 3 } - ]) - expect(reduce(state, view.add({ id: 3 }))).toEqual(result) - }) - - it('should add item in place', () => { - const state = createState([ - { id: 1 } - ]) - const result = createState([ - { id: 3 }, - { id: 1 } - ]) - expect(reduce(state, view.add({ id: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result) - }) - - it('should filter added item', () => { - const state = createState([ - { id: 1 } - ]) - const result = createState([ - { id: 1 } - ]) - expect(reduce(state, view.add({ id: 3 }, i => i.id === 1))).toEqual(result) - }) - - it('should update item', () => { - const state = createState([ - { id: 1, val: 1 }, - { id: 2, val: 2 }, - { id: 3, val: 3 } - ]) - const result = createState([ - { id: 1, val: 1 }, - { id: 2, val: 3 }, - { id: 3, val: 3 } - ]) - expect(reduce(state, view.update({ id: 2, val: 3 }))).toEqual(result) - }) - - it('should sort updated item', () => { - const state = createState([ - { id: 1, val: 1 }, - { id: 2, val: 2 } - ]) - const result = createState([ - { id: 2, val: 3 }, - { id: 1, val: 1 } - ]) - expect(reduce(state, view.update({ id: 2, val: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result) - }) - - it('should filter updated item', () => { - const state = createState([ - { id: 1, val: 1 }, - { id: 2, val: 2 } - ]) - const result = createState([ - { id: 1, val: 1 } - ]) - result.indexOf[2] = null - expect(reduce(state, view.update({ id: 2, val: 3 }, i => i.id === i.val))).toEqual(result) - }) - - it('should remove item', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - result.indexOf[2] = null - expect(reduce(state, view.remove(2))).toEqual(result) - }) - - it('should replace items', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - expect(reduce(state, view.receive([{ id: 1 }]))).toEqual(result) - }) - - it('should sort received items', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 2 }, - { id: 1 } - ]) - expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], undefined, (a, b) => b.id - a.id))).toEqual(result) - }) - - it('should filter received', () => { - const state = createState([ - { id: 1 }, - { id: 2 } - ]) - const result = createState([ - { id: 1 } - ]) - expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], i => i.id === 1))).toEqual(result) - }) -}) - -function createState(items) { - return { - data: items, - indexOf: _.fromPairs(items.map((item, index) => [item.id, index])) - } -} diff --git a/web/src/js/__tests__/ducks/utils/viewSpec.js b/web/src/js/__tests__/ducks/utils/viewSpec.js new file mode 100644 index 00000000..af3da173 --- /dev/null +++ b/web/src/js/__tests__/ducks/utils/viewSpec.js @@ -0,0 +1,156 @@ +jest.unmock('../../../ducks/utils/view') +jest.unmock('lodash') + +import reduce, * as view from '../../../ducks/utils/view' +import _ from 'lodash' + +describe('view reduce', () => { + + it('should filter items', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + expect(reduce(state, view.updateFilter(state.data, item => item.id === 1))).toEqual(result) + }) + + it('should sort items', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 2 }, + { id: 1 } + ]) + expect(reduce(state, view.updateSort((a, b) => b.id - a.id))).toEqual(result) + }) + + it('should add item', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 }, + { id: 2 }, + { id: 3 } + ]) + expect(reduce(state, view.add({ id: 3 }))).toEqual(result) + }) + + it('should add item in place', () => { + const state = createState([ + { id: 1 } + ]) + const result = createState([ + { id: 3 }, + { id: 1 } + ]) + expect(reduce(state, view.add({ id: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result) + }) + + it('should filter added item', () => { + const state = createState([ + { id: 1 } + ]) + const result = createState([ + { id: 1 } + ]) + expect(reduce(state, view.add({ id: 3 }, i => i.id === 1))).toEqual(result) + }) + + it('should update item', () => { + const state = createState([ + { id: 1, val: 1 }, + { id: 2, val: 2 }, + { id: 3, val: 3 } + ]) + const result = createState([ + { id: 1, val: 1 }, + { id: 2, val: 3 }, + { id: 3, val: 3 } + ]) + expect(reduce(state, view.update({ id: 2, val: 3 }))).toEqual(result) + }) + + it('should sort updated item', () => { + const state = createState([ + { id: 1, val: 1 }, + { id: 2, val: 2 } + ]) + const result = createState([ + { id: 2, val: 3 }, + { id: 1, val: 1 } + ]) + expect(reduce(state, view.update({ id: 2, val: 3 }, undefined, (a, b) => b.id - a.id))).toEqual(result) + }) + + it('should filter updated item', () => { + const state = createState([ + { id: 1, val: 1 }, + { id: 2, val: 2 } + ]) + const result = createState([ + { id: 1, val: 1 } + ]) + result.indexOf[2] = null + expect(reduce(state, view.update({ id: 2, val: 3 }, i => i.id === i.val))).toEqual(result) + }) + + it('should remove item', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + result.indexOf[2] = null + expect(reduce(state, view.remove(2))).toEqual(result) + }) + + it('should replace items', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + expect(reduce(state, view.receive([{ id: 1 }]))).toEqual(result) + }) + + it('should sort received items', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 2 }, + { id: 1 } + ]) + expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], undefined, (a, b) => b.id - a.id))).toEqual(result) + }) + + it('should filter received', () => { + const state = createState([ + { id: 1 }, + { id: 2 } + ]) + const result = createState([ + { id: 1 } + ]) + expect(reduce(state, view.receive([{ id: 1 }, { id: 2 }], i => i.id === 1))).toEqual(result) + }) +}) + +function createState(items) { + return { + data: items, + indexOf: _.fromPairs(items.map((item, index) => [item.id, index])) + } +} diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js index fdddc391..6bf0a63e 100755 --- a/web/src/js/ducks/utils/view.js +++ b/web/src/js/ducks/utils/view.js @@ -77,7 +77,6 @@ export default function reduce(state = defaultState, action) { ...sortedUpdate(state, action.item, action.sort), } } - break; case RECEIVE: { const data = action.list.filter(action.filter).sort(action.sort) -- cgit v1.2.3