UNPKG

@kabbi/react-redux-form

Version:
515 lines (467 loc) 14.9 kB
import { assert } from 'chai'; import { combineReducers } from 'redux'; import Immutable from 'immutable'; import thunk from 'redux-thunk'; import { actions, modelReducer, formReducer, track, combineForms, createForms } from '../src'; import { actions as immutableActions, modelReducer as immutableModelReducer, } from '../immutable'; import createTestStore from 'redux-test-store'; import { testCreateStore } from './utils'; describe('model actions', () => { const testItems = [ { id: 1, value: 'one' }, { id: 2, value: 'two' }, { id: 3, value: 'three' }, ]; describe('load()', () => { it('should load model values', () => { const reducer = modelReducer('foo'); const actual = reducer({}, actions.load('foo', { bar: 'string' })); assert.deepEqual(actual, { bar: 'string' }); }); it('should load array values and form values', () => { const reducer = combineForms({ user: { username: '', items: [{ name: 'item 1', subitems: [{ name: 'subitem 1' }] }], }, }); const DATA = { username: 'loaded', items: [ { name: 'item 1', subitems: [ { name: 'subitem 1' }, { name: 'subitem 2' }, ] }, { name: 'item 2', subitems: [ { name: 'subitem 1' }, ] }], }; // const initial = reducer(undefined, {}); const actual = reducer({}, actions.load('user', DATA)); assert.equal( actual.forms.user.items.$form.model, 'user.items' ); assert.equal( actual.forms.user.items[0].$form.model, 'user.items.0' ); assert.equal( actual.forms.user.items[0].name.model, 'user.items.0.name' ); assert.equal( actual.forms.user.items[0].subitems.$form.model, 'user.items.0.subitems' ); // assert.equal(actual.forms.user.items[0].subitems, "user.items.0.subitems.0"); assert.equal( actual.forms.user.items[0].subitems[0].$form.model, 'user.items.0.subitems.0' ); assert.equal( actual.forms.user.items[0].subitems[0].name.model, 'user.items.0.subitems.0.name' ); assert.equal( actual.forms.user.items[0].subitems[1].$form.model, 'user.items.0.subitems.1' ); assert.equal( actual.forms.user.items[0].subitems[1].name.model, 'user.items.0.subitems.1.name' ); assert.equal( actual.forms.user.items[1].$form.model, 'user.items.1' ); assert.equal( actual.forms.user.items[1].name.model, 'user.items.1.name' ); assert.equal( actual.forms.user.items[1].subitems.$form.model, 'user.items.1.subitems' ); assert.equal( actual.forms.user.items[1].subitems[0].$form.model, 'user.items.1.subitems.0' ); assert.equal( actual.forms.user.items[1].subitems[0].name.model, 'user.items.1.subitems.0.name' ); }); it('should load model and form stay untouched', () => { const reducer = combineReducers({ foo: modelReducer('foo'), fooForm: formReducer('foo'), }); const actual = reducer({}, actions.load('foo', { bar: 'string' })); assert.deepEqual(actual.foo, { bar: 'string' }); assert.equal(actual.fooForm.$form.pristine, true); assert.equal(actual.fooForm.$form.touched, false); }); it('after load model actions on non-set field should not fail', () => { const reducer = createForms({ foo: { bar: '', }, }); const store = createTestStore(testCreateStore({ ...reducer, }, thunk)); store.dispatch(actions.load('foo', { bar: 'string' })); const actual = store.getState(); assert.deepEqual(actual.foo, { bar: 'string' }); assert.equal(actual.forms.$form.pristine, true); assert.equal(actual.forms.$form.touched, false); store.dispatch(actions.focus('foo.notExist', { silent: true })); store.dispatch(actions.blur('foo.notExist')); }); }); describe('change()', () => { it('should modify the model given a shallow path', () => { const reducer = modelReducer('foo'); const actual = reducer({}, actions.change('foo.bar', 'string')); assert.deepEqual(actual, { bar: 'string' }); }); it('should modify the model given a deep path', () => { const reducer = modelReducer('foo'); const actual = reducer({}, actions.change('foo.bar.baz', 'string')); assert.deepEqual(actual, { bar: { baz: 'string' } }); }); it('should work with a tracker', () => { const reducer = modelReducer('foo', testItems); const dispatch = (action) => { const actual = reducer(undefined, action); assert.deepEqual(actual[1], { id: 2, value: 'tracked' }); }; const getState = () => ({ foo: testItems }); actions.change( track('foo[].value', { id: 2 }), 'tracked')(dispatch, getState); }); }); describe('reset()', () => { it('should reset the model to the initial state provided in the reducer', () => { const reducer = modelReducer('test', { foo: 'initial', }); const actual = reducer({ foo: 'bar' }, actions.reset('test.foo')); assert.deepEqual(actual, { foo: 'initial' }); }); it('should set the model to undefined if an initial state was not provided from a deep model', () => { const reducer = modelReducer('test', { foo: 'initial', }); const actual = reducer({ bar: { baz: 'uninitialized' } }, actions.reset('test.bar.baz')); assert.isDefined(actual.bar); assert.isUndefined(actual.bar.baz); }); it('should set the model to undefined if an initial state was not provided', () => { const reducer = modelReducer('test', { foo: 'initial', }); const actual = reducer({ bar: 'uninitialized' }, actions.reset('test.bar')); assert.isUndefined(actual.bar); }); it('should be able to reset an entire model', () => { const initialState = { foo: 'test foo', bar: 'test bar', baz: { one: 'two' }, }; const reducer = modelReducer('test', initialState); const actual = reducer({}, actions.reset('test')); assert.deepEqual(actual, initialState); }); }); describe('thunk action creators', () => { const actionTests = { change: [ { init: { foo: 'existing' }, params: ['test.foo', (val) => val.toUpperCase()], expected: { foo: 'EXISTING' }, }, ], push: [ { init: { foo: [123] }, params: ['test.foo', 456], expected: { foo: [123, 456] }, }, { init: {}, params: ['test.foo', 456], expected: { foo: [456] }, }, { init: [ testItems[0], { id: 2, value: ['two'] }, testItems[2], ], params: [track('test[].value', { id: 2 }), 'pushed'], expected: [ testItems[0], { id: 2, value: ['two', 'pushed'], }, testItems[2], ], tracked: true, }, ], xor: [ { init: { foo: [123, 456] }, params: ['test.foo', 456], expected: { foo: [123] }, }, { init: { foo: ['primitive', { a: 'b' }] }, params: ['test.foo', { a: 'b' }, (item) => (item.get ? item.get('a') : item.a) === 'b'], expected: { foo: ['primitive'] }, immutable: false, }, { init: [ testItems[0], { id: 2, value: ['two'] }, testItems[2], ], params: [track('test[].value', { id: 2 }), 'two'], expected: [ testItems[0], { id: 2, value: [] }, testItems[2], ], tracked: true, }, ], toggle: [ { init: { foo: true }, params: ['test.foo'], expected: { foo: false }, }, { init: testItems, params: [track('test[].value', { id: 2 })], expected: [ testItems[0], { id: 2, value: false }, testItems[2], ], tracked: true, }, ], filter: [ { init: { foo: [1, 2, 3, 4, 5, 6] }, params: ['test.foo', n => n % 2 === 0], expected: { foo: [2, 4, 6] }, }, { init: [ testItems[0], { id: 2, value: [1, 2, 3, 4, 5, 6] }, testItems[2], ], params: [track('test[].value', { id: 2 }), n => n % 2 === 0], expected: [ testItems[0], { id: 2, value: [2, 4, 6] }, testItems[2], ], tracked: true, }, ], map: [ { init: { foo: [1, 2, 3, 4, 5] }, params: ['test.foo', n => n * 2], expected: { foo: [2, 4, 6, 8, 10] }, }, { init: [ testItems[0], { id: 2, value: [1, 2, 3, 4, 5] }, testItems[2], ], params: [track('test[].value', { id: 2 }), n => n * 2], expected: [ testItems[0], { id: 2, value: [2, 4, 6, 8, 10] }, testItems[2], ], tracked: true, }, ], remove: [ { init: { foo: ['first', 'second', 'third'] }, params: ['test.foo', 1], expected: { foo: ['first', 'third'] }, }, { init: [ testItems[0], { id: 2, value: ['first', 'second', 'third'] }, testItems[2], ], params: [track('test[].value', { id: 2 }), 1], expected: [ testItems[0], { id: 2, value: ['first', 'third'] }, testItems[2], ], tracked: true, }, ], move: [ { init: { foo: ['first', 'second', 'third'] }, params: ['test.foo', 2, 1], expected: { foo: ['first', 'third', 'second'] }, }, { init: { foo: ['first', 'second', 'third'] }, params: ['test.foo', 0, 2], expected: { foo: ['second', 'third', 'first'] }, }, { init: { foo: [] }, params: ['test.foo', 0, 2], expected: Error('Error moving array item: invalid bounds 0, 2'), }, { init: [ testItems[0], { id: 2, value: ['first', 'second', 'third'] }, testItems[2], ], params: [track('test[].value', { id: 2 }), 0, 2], expected: [ testItems[0], { id: 2, value: ['second', 'third', 'first'] }, testItems[2], ], tracked: true, }, ], merge: [ { init: { foo: { bar: 'baz', untouched: 'intact' } }, params: ['test.foo', { bar: 'new', one: 'two' }], expected: { foo: { bar: 'new', one: 'two', untouched: 'intact' } }, }, { init: [ testItems[0], { id: 2, value: { bar: 'baz', untouched: 'intact' } }, testItems[2], ], params: [ track('test[].value', { id: 2 }), { bar: 'new', one: 'two' }, ], expected: [ testItems[0], { id: 2, value: { bar: 'new', one: 'two', untouched: 'intact' } }, testItems[2], ], tracked: true, }, ], omit: [ { init: { one: 1, two: 2, three: 3 }, params: ['test', 'two'], expected: { one: 1, three: 3 }, }, { init: { one: 1, two: 2, three: 3 }, params: ['test', ['one', 'three']], expected: { two: 2 }, }, { init: [ testItems[0], { id: 2, value: { one: 1, two: 2, three: 3 } }, testItems[2], ], params: [track('test[].value', { id: 2 }), 'two'], expected: [ testItems[0], { id: 2, value: { one: 1, three: 3 } }, testItems[2], ], tracked: true, }, ], }; /* eslint-disable array-callback-return */ Object.keys(actionTests).map((action) => { describe(`${action}()`, () => { actionTests[action].map((test) => { const { init, params, expected } = test; it('should modify the model to the expected result', () => { const reducer = modelReducer('test'); const getState = () => ({ test: init }); const dispatch = (_action) => { if (typeof _action === 'function') { _action(dispatch, getState); } else { assert.deepEqual( reducer(init, _action), expected); } }; if (expected instanceof Error) { assert.throws(() => actions[action](...params)(dispatch, getState), expected.message); } else { actions[action](...params)(dispatch, getState); } }); }); }); describe(`${action}() (Immutable.JS)`, () => { actionTests[action].map((test, i) => { const { init, params, expected, tracked } = test; // TODO: test tracker with immutablejs if (tracked) return; const initImmutable = Immutable.fromJS(init); const immutableParams = params.map((param) => Immutable.fromJS(param)); it(`should modify the model to the expected result (${i})`, () => { const reducer = immutableModelReducer('test'); const getState = () => ({ test: Immutable.fromJS(initImmutable) }); const dispatch = (_action) => { if (typeof _action === 'function') { _action(dispatch, getState); } else { assert.deepEqual( reducer(initImmutable, _action).toJS(), expected, [reducer(initImmutable, _action), expected]); } }; if (expected instanceof Error) { assert.throws(() => immutableActions[action](...immutableParams)(dispatch, getState), expected.message); } else { immutableActions[action](...immutableParams)(dispatch, getState); } }); }); }); }); /* eslint-enable */ }); });