Browse Source

Merge pull request #376 from richardwillars/uppy-redux

Adds redux plugin
Artur Paikin 7 years ago
parent
commit
28900c7728
2 changed files with 172 additions and 0 deletions
  1. 33 0
      src/plugins/Redux.js
  2. 139 0
      src/plugins/Redux.test.js

+ 33 - 0
src/plugins/Redux.js

@@ -0,0 +1,33 @@
+const Plugin = require('./Plugin')
+
+export default class Redux extends Plugin {
+  constructor (core, opts) {
+    super(core, opts)
+    this.type = 'state-sync'
+    this.id = 'Redux'
+    this.title = 'Redux Emitter'
+
+    if (typeof opts.action === 'undefined') {
+      throw new Error('action option is not defined')
+    }
+    if (typeof opts.dispatch === 'undefined') {
+      throw new Error('dispatch option is not defined')
+    }
+    this.opts = opts
+
+    this.handleStateUpdate = this.handleStateUpdate.bind(this)
+  }
+
+  handleStateUpdate (prev, state, patch) {
+    this.opts.dispatch(this.opts.action(prev, state, patch)) // this dispatches a redux event with the new state
+  }
+
+  install () {
+    this.core.emitter.on('core:state-update', this.handleStateUpdate)
+    this.handleStateUpdate({}, this.core.state, this.core.state) // set the initial redux state
+  }
+
+  uninstall () {
+    this.core.emitter.off('core:state-update', this.handleStateUpdate)
+  }
+}

+ 139 - 0
src/plugins/Redux.test.js

@@ -0,0 +1,139 @@
+import ReduxPlugin from './Redux'
+import Plugin from './Plugin'
+
+describe('uploader/reduxPlugin', () => {
+  it('should initialise successfully', () => {
+    const actionFunction = () => {}
+    const dispatchFunction = () => {}
+    const redux = new ReduxPlugin(null, {
+      action: actionFunction,
+      dispatch: dispatchFunction
+    })
+    expect(redux instanceof Plugin).toEqual(true)
+    expect(redux.opts.action).toBe(actionFunction)
+    expect(redux.opts.dispatch).toBe(dispatchFunction)
+  })
+
+  it('should throw an error if the action option is not specified', () => {
+    const dispatchFunction = () => {}
+
+    expect(() => {
+      new ReduxPlugin(null, { dispatch: dispatchFunction }) // eslint-disable-line no-new
+    }).toThrow('action option is not defined')
+  })
+
+  it('should throw an error if the dispatch option is not specified', () => {
+    const actionFunction = () => {}
+
+    expect(() => {
+      new ReduxPlugin(null, { action: actionFunction }) // eslint-disable-line no-new
+    }).toThrow('dispatch option is not defined')
+  })
+
+  describe('install', () => {
+    it('should subscribe to uppy events', () => {
+      const core = {
+        emitter: {
+          on: jest.fn()
+        }
+      }
+
+      const redux = new ReduxPlugin(core, {
+        action: () => {},
+        dispatch: () => {}
+      })
+      redux.handleStateUpdate = jest.fn()
+      redux.install()
+
+      expect(core.emitter.on.mock.calls.length).toEqual(1)
+      expect(core.emitter.on.mock.calls[0]).toEqual([
+        'core:state-update',
+        redux.handleStateUpdate
+      ])
+    })
+
+    it('should call this.handleStateUpdate with the current state on install', () => {
+      const core = {
+        emitter: {
+          on: jest.fn()
+        }
+      }
+
+      const redux = new ReduxPlugin(core, {
+        action: () => {},
+        dispatch: () => {}
+      })
+      redux.handleStateUpdate = jest.fn()
+      redux.install()
+
+      expect(redux.handleStateUpdate.mock.calls.length).toEqual(1)
+      expect(redux.handleStateUpdate.mock.calls[0]).toEqual([
+        {},
+        core.state,
+        core.state
+      ])
+    })
+  })
+
+  describe('uninstall', () => {
+    it('should should unsubscribe from uppy events on uninstall', () => {
+      const core = {
+        emitter: {
+          off: jest.fn()
+        }
+      }
+
+      const redux = new ReduxPlugin(core, {
+        action: () => {},
+        dispatch: () => {}
+      })
+      redux.uninstall()
+
+      expect(core.emitter.off.mock.calls.length).toEqual(1)
+      expect(core.emitter.off.mock.calls[0]).toEqual([
+        'core:state-update',
+        redux.handleStateUpdate
+      ])
+    })
+  })
+
+  describe('handleStateUpdate', () => {
+    it('should create a redux action with the new state', () => {
+      const core = {}
+      const actionMock = jest.fn().mockReturnValue({
+        foo: 'bar'
+      })
+      const dispatchMock = () => {}
+
+      const redux = new ReduxPlugin(core, {
+        action: actionMock,
+        dispatch: dispatchMock
+      })
+      const prev = { a: 'b' }
+      const state = { a: 'b', c: 'd' }
+      const patch = { c: 'd' }
+      redux.handleStateUpdate(prev, state, patch)
+      expect(actionMock.mock.calls.length).toEqual(1)
+      expect(actionMock.mock.calls[0]).toEqual([prev, state, patch])
+    })
+
+    it('should dispatch a redux action with the new state', () => {
+      const core = {}
+      const actionMock = jest.fn().mockReturnValue({
+        foo: 'bar'
+      })
+      const dispatchMock = jest.fn()
+
+      const redux = new ReduxPlugin(core, {
+        action: actionMock,
+        dispatch: dispatchMock
+      })
+      const prev = { a: 'b' }
+      const state = { a: 'b', c: 'd' }
+      const patch = { c: 'd' }
+      redux.handleStateUpdate(prev, state, patch)
+      expect(dispatchMock.mock.calls.length).toEqual(1)
+      expect(dispatchMock.mock.calls[0]).toEqual([{ foo: 'bar' }])
+    })
+  })
+})