UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

337 lines (312 loc) 14.3 kB
import _upperCase from "lodash/upperCase"; import _isFunction from "lodash/isFunction"; function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest(); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } jest.mock('./logger'); import assert from 'assert'; import sinon from 'sinon'; import { cleanArgs, thunk, getReduxPrimitives } from './redux'; describe('redux utils', function () { describe('#cleanArgs', function () { it('it should remove the last element if it has an `event` property', function () { assert.deepEqual(cleanArgs(['foo', 'bar', { event: 'event' }]), ['foo', 'bar']); assert.deepEqual(cleanArgs(['foo', 'bar', { event: null }]), ['foo', 'bar']); }); it('it should not remove the last element if it has no `event` property', function () { assert.deepEqual(cleanArgs(['foo', 'bar']), ['foo', 'bar']); }); }); describe('#thunk', function () { it('should set `isThunk` property on the input function to `true`', function () { assert(thunk(function () {}).isThunk, 'must have `isThunk`'); }); }); describe('#getReduxPrimitives', function () { var reducers = { foo: { onChange: function onChange(_state, payload) { return { value: payload }; }, bar: { onChange: function onChange(_state, payload) { return { value: payload }; } } } }; var initialState = { foo: { value: 'foo', bar: { value: null } } }; describe('reducer', function () { var _getReduxPrimitives = getReduxPrimitives({ reducers: reducers, initialState: initialState }), reducer = _getReduxPrimitives.reducer; it('should return initialState on unmatched type', function () { var action = { type: 'UNKNOWN' }; assert.deepEqual(reducer(initialState, action), initialState, 'must deep equal initialState'); }); it('should correctly apply state change', function () { var action = { type: 'foo.onChange', payload: 'bar' }; var nextState = reducer(initialState, action); assert.equal(nextState.foo.value, 'bar', 'must equal action payload'); }); describe('nested reducer', function () { it('should correctly apply state change', function () { var action = { type: 'foo.bar.onChange', payload: 'baz' }; var nextState = reducer(initialState, action); assert.equal(nextState.foo.bar.value, 'baz', 'must equal action payload'); }); }); describe('with rootPath', function () { var _getReduxPrimitives2 = getReduxPrimitives({ reducers: reducers, initialState: initialState, rootPath: ['root'] }), reducerWithRootPath = _getReduxPrimitives2.reducer; it('should correctly apply state change', function () { var action = { type: 'root.foo.onChange', payload: 'bar' }; var nextState = reducerWithRootPath(initialState, action); assert.equal(nextState.foo.value, 'bar', 'must equal action payload'); }); }); describe('thunks', function () { it('should not include thunk paths in reducer', function () { var action = { type: 'root.foo.asyncOperation' }; var nextState = reducer(initialState, action); assert.deepEqual(initialState, nextState, 'must deep equal initialState'); }); }); }); describe('connectors', function () { describe('selector/mapStateToProps', function () { var selectors = { foo: { uppercase: function uppercase(_ref) { var value = _ref.value; return _upperCase(value); } } }; var _getReduxPrimitives3 = getReduxPrimitives({ reducers: reducers, initialState: initialState, selectors: selectors }), _getReduxPrimitives3$ = _slicedToArray(_getReduxPrimitives3.connectors, 1), mapStateToProps = _getReduxPrimitives3$[0]; it('should apply selector', function () { var viewState = mapStateToProps(initialState, null, null); assert.equal(viewState.foo.uppercase, 'FOO', 'must equal "FOO"'); }); it('should pass state through unharmed if selectors are undefined', function () { var _getReduxPrimitives4 = getReduxPrimitives({ reducers: reducers, initialState: initialState }), _getReduxPrimitives4$ = _slicedToArray(_getReduxPrimitives4.connectors, 1), mapStateToProps = _getReduxPrimitives4$[0]; var viewState = mapStateToProps(initialState, null, null); assert.deepEqual(viewState, initialState); }); describe('rootPath', function () { var _getReduxPrimitives5 = getReduxPrimitives({ reducers: reducers, initialState: initialState, selectors: selectors, rootPath: ['root'] }), _getReduxPrimitives5$ = _slicedToArray(_getReduxPrimitives5.connectors, 1), mapStateToProps = _getReduxPrimitives5$[0]; it('should apply selector', function () { var viewState = mapStateToProps({ root: initialState }, null, null); assert.equal(viewState.foo.uppercase, 'FOO', 'must equal "FOO"'); }); }); describe('rootSelector', function () { var _getReduxPrimitives6 = getReduxPrimitives({ reducers: reducers, initialState: initialState, selectors: selectors, rootSelector: function rootSelector(state) { return _objectSpread(_objectSpread({}, state), {}, { computed: state.foo.uppercase + state.foo.value }); } }), _getReduxPrimitives6$ = _slicedToArray(_getReduxPrimitives6.connectors, 1), mapStateToProps = _getReduxPrimitives6$[0]; it('should apply rootSelector', function () { var viewState = mapStateToProps(initialState, null, null); assert.equal(viewState.computed, 'FOOfoo', 'must equal "FOOfoo"'); }); }); }); describe('dispatchTree/mapDispatchToProps', function () { describe('synchronous dispatch', function () { var rootState = { qux: { quux: { foo: { value: 'foo', bar: { value: null } } } } }; var initialState = rootState.qux.quux; var _getReduxPrimitives7 = getReduxPrimitives({ reducers: reducers, initialState: initialState, rootPath: ['qux', 'quux'] }), connectors = _getReduxPrimitives7.connectors; var mockDispatch = sinon.spy(function (action) { return action; }); var mapDispatchToProps = connectors[1]; var dispatchTree = mapDispatchToProps(mockDispatch, null, null); beforeEach(function () { mockDispatch.reset(); }); it('should dispatch the correct action', function () { dispatchTree.foo.onChange('bar', 'baz'); var dispatchedAction = mockDispatch.getCall(0).args[0]; assert.deepEqual(dispatchedAction, { type: 'qux.quux.foo.onChange', payload: 'bar', meta: ['baz'] }, 'must include path as type, first param as payload, and subsequent params as meta'); }); }); describe('thunks', function () { var thunkSpy = sinon.spy(); var reducers = { foo: { onChange: function onChange(state, payload) { return { value: payload }; }, bar: { onChange: function onChange(state, payload) { return { value: payload }; } }, asyncOperation: thunk(function (payload) { return function (dispatchTree) { return dispatchTree.onChange(payload); }; }), thunkSpy: thunk(function () { return thunkSpy; }) } }; var rootState = { qux: { quux: { foo: { value: 'foo', bar: { value: null } } } } }; var initialState = rootState.qux.quux; var _getReduxPrimitives8 = getReduxPrimitives({ reducers: reducers, initialState: initialState, rootPath: ['qux', 'quux'] }), connectors = _getReduxPrimitives8.connectors; var extraArgs = ['rest1', 'rest2']; var mapDispatchToProps = connectors[1]; var mockGetState = sinon.spy(function () { return rootState; }); var mockDispatch = sinon.spy(function (action) { return _isFunction(action) ? action.apply(void 0, [mockDispatch, mockGetState].concat(extraArgs)) : action; }); var dispatchTree = mapDispatchToProps(mockDispatch, null, null); beforeEach(function () { thunkSpy.reset(); dispatchTree.foo.asyncOperation('qux'); }); it('should dispatch a thunk', function () { var dispatchedThunk = mockDispatch.getCall(0).args[0]; assert(_isFunction(dispatchedThunk), 'must be a function'); }); it('should dispatch the correct action', function () { var dispatchedAction = mockDispatch.getCall(1).args[0]; assert.deepEqual(dispatchedAction, { type: 'qux.quux.foo.onChange', payload: 'qux', meta: [] }, 'must include path as type and param on payload'); }); it('should call the thunk with the correct arguments', function () { dispatchTree.foo.thunkSpy(); var _thunkSpy$getCall = thunkSpy.getCall(0), _thunkSpy$getCall$arg = _toArray(_thunkSpy$getCall.args), localDispatchTree = _thunkSpy$getCall$arg[0], getLocalState = _thunkSpy$getCall$arg[1], dispatch = _thunkSpy$getCall$arg[2], getState = _thunkSpy$getCall$arg[3], rest = _thunkSpy$getCall$arg.slice(4); assert.equal(dispatchTree.foo, localDispatchTree, 'must be called with local dispatchTree'); assert.equal(getLocalState(), rootState.qux.quux.foo, 'must be called with getLocalState'); assert.equal(dispatch, mockDispatch, 'must be called with redux.dispatch'); assert.equal(getState, mockGetState, 'must be called with redux.getState'); assert.deepEqual(rest, extraArgs, 'must pass through extra arguments'); }); }); }); }); }); });