Skip to content

Commit

Permalink
feat(vmPlugin): added vmPlugin for simple app architecture (experimen…
Browse files Browse the repository at this point in the history
…tal)
  • Loading branch information
AoDev committed May 14, 2019
1 parent fbf7ce9 commit 1fa6917
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 3 deletions.
12 changes: 9 additions & 3 deletions src/mobx/react/Route.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import {inject, observer} from 'mobx-react'

function Route (props) {
const {router, path, Component, ...otherProps} = props
return router.route.startsWith(path) ? <Component {...otherProps}/> : null
if (!router.route.startsWith(path)) {
return null
}
if (router.vmPlugin && router.vmPlugin.vmTree[path]) {
otherProps.vm = router.vmPlugin.vmTree[path]
}
return <Component {...otherProps}/>
}

export default inject('router')(observer(Route))

Route.propTypes = {
Component: PropTypes.func.isRequired,
router: PropTypes.shape({
Expand All @@ -17,3 +21,5 @@ Route.propTypes = {
}),
path: PropTypes.string.isRequired,
}

export default inject('router')(observer(Route))
52 changes: 52 additions & 0 deletions src/plugins/vmPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {diffPaths, splitPath} from '../Router'

/**
* @param {{router: {vmPlugin: {vmTree: *}},incomingRequest: {route: string}, currentState: {route: string}}} - beforeNav event
*/
export function vmPluginInstantiateVM ({router, incomingRequest, currentState}) {
const nodes = diffPaths(splitPath(currentState.route), splitPath(incomingRequest.route))
nodes.forEach((node) => {
const routeConfig = router.routes[node]
if (routeConfig.vmPlugin) {
const {vmClass} = routeConfig.vmPlugin
router.vmPlugin.vmTree[node] = new vmClass(router.app.rootStore) // eslint-disable-line
}
})
}

/**
* @param {{router: {vmPlugin: {vmTree: *}},incomingRequest: {route: string}, currentState: {route: string}}} - afterNav event
*/
export function vmPluginCleanupVM ({router, incomingRequest, currentState}) {
const nodes = diffPaths(splitPath(incomingRequest.route), splitPath(currentState.route))
const {vmTree} = router.vmPlugin
nodes.forEach((node) => {
if (vmTree[node] && typeof vmTree[node].destroyVM === 'function') {
vmTree[node].destroyVM()
}
})
}

/**
* @param {*} router
*/
function register (router) {
router.on('beforeNav', vmPluginInstantiateVM)
router.on('afterNav', vmPluginCleanupVM)

const unregister = () => {
router.off('beforeNav', vmPluginInstantiateVM)
router.off('afterNav', vmPluginCleanupVM)
delete router.vmPlugin
}

router.vmPlugin = {
vmTree: {},
}

return unregister
}

export default {
register,
}
110 changes: 110 additions & 0 deletions src/plugins/vmPlugin.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import vmPlugin, {vmPluginInstantiateVM, vmPluginCleanupVM} from './vmPlugin'

class SomePageMockVM {
destroyVM = jest.fn()
}

class OtherPageMockVM {
}

const routes = {
'/': {
},
'/some-page': {
vmPlugin: {
vmClass: SomePageMockVM,
},
},
'/some-other-page': {
vmPlugin: {
vmClass: OtherPageMockVM,
},
},
}

class RouterMock {
route = '/'
params = {}
goTo = jest.fn().mockImplementation((request) => {
this.route = request.route
this.params = request.params
})
on = jest.fn()
off = jest.fn()
routes = routes
app = {
rootStore: {}
}
}

describe('vmPlugin', () => {
let routerMock
let unregister

beforeEach(() => {
routerMock = new RouterMock(routes)
unregister = vmPlugin.register(routerMock)
})

describe('register', () => {
it('should register vmPluginInstantiateVM / vmPluginCleanupVM event listeners', () => {
// Before nav
const spyArgsBeforeNav = routerMock.on.mock.calls[0]
expect(spyArgsBeforeNav[0]).toBe('beforeNav')
expect(spyArgsBeforeNav[1]).toBe(vmPluginInstantiateVM)
// After nav
const spyArgsAfterNav = routerMock.on.mock.calls[1]
expect(spyArgsAfterNav[0]).toBe('afterNav')
expect(spyArgsAfterNav[1]).toBe(vmPluginCleanupVM)
})

it('should return a function to unregister the plugin', () => {
expect(unregister).toBeInstanceOf(Function)
})

it('should decorate the router with the vmTree', () => {
const routerMock = new RouterMock()
expect(routerMock.vmPlugin).toBeUndefined()
vmPlugin.register(routerMock)
expect(routerMock.vmPlugin.vmTree).toBeDefined()
})
})

describe('unregister', () => {
it('should remove the event handlers & vmTree', () => {
expect(routerMock.vmPlugin.vmTree).toBeDefined()
unregister()
expect(routerMock.vmPlugin).toBeUndefined()

// Before nav
const spyArgsBeforeNav = routerMock.off.mock.calls[0]
expect(spyArgsBeforeNav[0]).toBe('beforeNav')
expect(spyArgsBeforeNav[1]).toBe(vmPluginInstantiateVM)
// After nav
const spyArgsAfterNav = routerMock.off.mock.calls[1]
expect(spyArgsAfterNav[0]).toBe('afterNav')
expect(spyArgsAfterNav[1]).toBe(vmPluginCleanupVM)
})
})

describe('vmPluginInstantiateVM', () => {
it('should instantiate the corresponding VMs', () => {
vmPluginInstantiateVM({
router: routerMock, incomingRequest: {route: '/some-page'}, currentState: {route: '/'}
})
expect(routerMock.vmPlugin.vmTree['/some-page']).toBeInstanceOf(routes['/some-page'].vmPlugin.vmClass)
})
})

describe('vmPluginCleanupVM', () => {
it('should cleanup VMs', () => {
vmPluginInstantiateVM({
router: routerMock, incomingRequest: {route: '/some-page'}, currentState: {route: '/'}
})
vmPluginCleanupVM({
router: routerMock, incomingRequest: {route: '/some-other-page'}, currentState: {route: '/some-page'}
})
expect(routerMock.vmPlugin.vmTree['/some-page'].destroyVM).toHaveBeenCalled()
})
})
})

0 comments on commit 1fa6917

Please sign in to comment.