import u from 'updeep'; import { Updux } from './Updux'; import { action } from './actions'; test('subscriptions', () => { const inc = action('inc'); const set_copy = action('set_copy'); const dux = new Updux({ initial: { x: 0, copy: 0, }, actions: { inc, set_copy, }, mutations: { inc: (payload) => u({ x: (x) => x + 1 }), set_copy: (copy) => u({ copy }), }, }); dux.addSubscription((store) => (state, previous, unsubscribe) => { if (state.x > 2) return unsubscribe(); store.dispatch(set_copy(state.x)); }); const store = dux.createStore(); store.dispatch(inc()); expect(store.getState()).toMatchObject({ x: 1, copy: 1 }); store.dispatch(inc()); store.dispatch(inc()); expect(store.getState()).toMatchObject({ x: 3, copy: 2 }); }); test('subduxes subscriptions', () => { const inc_top = action('inc_top'); const inc_bar = action('inc_bar'); const transform_bar = action('transform_bar'); const bar = new Updux({ initial: 'a', actions: { inc_bar, transform_bar }, mutations: { inc_bar: () => (state) => state + 'a', transform_bar: (outcome) => () => outcome, }, reactions: [ (store) => (state, previous, unsubscribe) => { if (state.length <= 2) return; unsubscribe(); store.dispatch(transform_bar('look at ' + state)); }, ], }); const dux = new Updux({ initial: { count: 0, }, subduxes: { bar }, actions: { inc_top, }, mutations: { inc_top: () => u({ count: (count) => count + 1 }), }, effects: { '*': () => (next) => (action) => { next(action); }, }, reactions: [ (store) => { return ({ count }, { count: previous } = {}) => { if (count !== previous) { previous = count; store.dispatch.inc_bar(); } }; }, ], }); const store = dux.createStore(); store.dispatch(inc_top()); store.dispatch(inc_top()); expect(store.getState()).toMatchObject({ count: 2, bar: 'look at look at aaa', }); store.dispatch(inc_top()); expect(store.getState()).toMatchObject({ count: 3, bar: 'look at look at aaaa', }); }); test('subscription within subduxes', () => { let innerState = jest.fn(() => null); let outerState = jest.fn(() => null); const resetMocks = () => [innerState, outerState].map((f) => f.mockReset()); const inner = new Updux({ initial: 1, actions: { inc: null }, mutations: { inc: () => (state) => state + 1, }, reactions: [ (store) => (state, previous, unsub) => { if (!previous) return; store.subscribe(innerState); unsub(); }, ], }); const dux = new Updux({ initial: { x: 0 }, subduxes: { inner }, actions: { incOuter: null, }, mutations: { incOuter: () => (state) => ({ ...state, x: state.x + 1 }), }, reactions: [ (store) => (state, previous, unsub) => { if (!previous) return; store.subscribe(outerState); unsub(); }, ], }); const store = dux.createStore(); store.dispatch({ type: 'noop' }); store.dispatch({ type: 'noop' }); expect(innerState).not.toHaveBeenCalled(); expect(outerState).not.toHaveBeenCalled(); store.dispatch.inc(); // not called yet expect(innerState).not.toBeCalled(); expect(outerState).not.toBeCalled(); store.dispatch.inc(); expect(outerState).toBeCalledTimes(1); expect(outerState).toHaveBeenCalledWith( { inner: 3, x: 0 }, undefined, expect.anything() ); expect(innerState).toHaveBeenCalled(); expect(innerState).toHaveBeenCalledWith(3, undefined, expect.anything()); resetMocks(); store.dispatch.inc(); expect(outerState).toBeCalled(); expect(outerState).toBeCalledWith( { inner: 4, x: 0 }, expect.anything(), expect.anything() ); expect(innerState).toBeCalledWith(4, 3, expect.anything()); // state of subdux doesnt change resetMocks(); store.dispatch.incOuter(); expect(outerState).toBeCalled(); expect(innerState).not.toBeCalled(); });