@girders-elements/core
Version:
Girders Elements is an architectural framework that assists with building data-driven apps with React or React Native.
169 lines (127 loc) • 4.63 kB
JavaScript
import { fromJS, List } from 'immutable'
import * as actions from '../../action'
import * as Subsystem from '../../subsystem'
import * as Kernel from '../../kernel'
import effectSubS from '..'
describe('effects API', function() {
const data = fromJS({
element1: {
element2: {
element3: {
kind: ['article', 'specific'],
},
kind: ['container'],
},
kind: ['container'],
},
kind: 'root',
})
const app = Subsystem.create(() => ({
name: 'app',
}))
const { effect } = app
describe('Effects', () => {
const specificEffect = jest.fn()
const specificEffect2 = jest.fn()
const specificEffect3 = jest.fn()
const articleEffect = jest.fn()
const componentEffect = jest.fn()
afterEach(() => {
;[
specificEffect,
specificEffect2,
specificEffect3,
articleEffect,
componentEffect,
].forEach(m => m.mockClear())
})
// a typical effect that updates the element state when finishing
effect.register(['article'], 'mark', async (context, action) => {
await sleep(50)
return el => el.set('marked', action.type)
})
effect.forKind(['article', 'specific'], effects => {
effects.register('toggle', specificEffect)
effects.register('toggle2', specificEffect2)
effects.register('toggle3', specificEffect3)
effects.register('noop', async () => {})
effects.register('noop2', () => {})
})
effect.register(['article'], 'toggle', articleEffect)
effect.register(['container'], '.toggle', componentEffect)
effect.register(['container'], 'fireOnSub', async context => {
context.focusOn(['element3']).dispatch({ type: 'toggle3' })
})
describe('effect context', () => {
const kernel = Kernel.create([effectSubS, app], data, {})
test('normal action', async () => {
kernel
.focusOn(['element1', 'element2', 'element3'])
.dispatch({ type: 'toggle' })
await sleep(50)
expect(specificEffect).toHaveBeenCalled()
expect(specificEffect2).not.toHaveBeenCalled()
const [context, action] = specificEffect.mock.calls[
specificEffect.mock.calls.length - 1
]
expect(action.type).toEqual('toggle')
expect(actions.actionMeta(action).keyPath).toEqual([
'element1',
'element2',
'element3',
])
expect(context.query().get('kind')).toEqualI(
List.of('article', 'specific')
)
context.dispatch({ type: 'toggle2' })
await sleep(50)
expect(specificEffect2).toHaveBeenCalled()
})
test('global action', async () => {
kernel
.focusOn(['element1', 'element2', 'element3'])
.dispatch({ type: '.toggle' })
expect(componentEffect).toHaveBeenCalled()
const [context, action] = componentEffect.mock.calls[0]
expect(context.query().get('kind')).toEqualI(List.of('container'))
expect(actions.actionMeta(action).keyPath).toEqual([
'element1',
'element2',
'element3',
])
expect(context.query()._keyPath).toEqual(['element1', 'element2'])
expect(action.type).toEqual('.toggle')
kernel.focusOn(['element1']).dispatch({ type: '.toggle' })
await sleep(50)
const [context2, action2] = componentEffect.mock.calls[1]
expect(context2.query()._keyPath).toEqual(['element1'])
expect(action2.type).toEqual('.toggle')
})
describe('action on sub-element', async () => {
kernel.focusOn(['element1', 'element2']).dispatch({ type: 'fireOnSub' })
await sleep(50)
expect(specificEffect3).toHaveBeenCalled()
})
})
describe("Effect's return value", () => {
const kernel = Kernel.create([effectSubS, app], data, {})
test('Effect can return an updating fn state => newState', async () => {
const focus = kernel.focusOn(['element1', 'element2', 'element3'])
focus.dispatch({ type: 'mark' })
await sleep(25)
expect(focus.query().get('marked')).not.toEqual('mark')
await sleep(50)
expect(focus.query().get('marked')).toEqual('mark')
})
test('An async effect can return undefined', () => {
const focus = kernel.focusOn(['element1', 'element2', 'element3'])
focus.dispatch({ type: 'noop2' })
focus.dispatch({ type: 'noop' })
})
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}