-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Provider): implemented and began tests for react/Provider
affects: @tao.js/react all code implemented tests for basic class and addComponentHandler specs written but not implemented
- Loading branch information
Showing
2 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import cartesian from 'cartesian'; | ||
import { AppCtx } from '@tao.js/core'; | ||
|
||
const wrappedHandler = (Component, props, _provider) => (tao, data) => { | ||
_provider._current = { | ||
Component, | ||
tao, | ||
props: { | ||
...props, | ||
...data | ||
} | ||
}; | ||
}; | ||
|
||
class Provider { | ||
constructor(TAO) { | ||
this._tao = TAO; | ||
this._current = null; | ||
this._default = {}; | ||
// this._taoIndex = new Map(); | ||
this._components = new Map(); | ||
} | ||
|
||
get current() { | ||
return this._current; | ||
} | ||
|
||
get defaultCtx() { | ||
return { ...this._default }; | ||
} | ||
|
||
set defaultCtx({ term, action, orient } = {}) { | ||
this._default = { term, action, orient }; | ||
} | ||
|
||
setDefaultCtx({ term, action, orient } = {}) { | ||
this.defaultCtx = { term, action, orient }; | ||
return this; | ||
} | ||
|
||
addComponentHandler({ term, action, orient } = {}, Component, props) { | ||
const ctx = Object.assign(this.defaultCtx, { term, action, orient }); | ||
const permutations = cartesian(ctx); | ||
const handler = wrappedHandler(Component, props, this); | ||
if (!this._components.has(Component)) { | ||
this._components.set(Component, { | ||
handlers: new Map(), | ||
index: new Map() | ||
}); | ||
} | ||
const componentHandlers = this._components.get(Component); | ||
permutations.forEach(tao => { | ||
const { term, action, orient } = tao; | ||
const acKey = AppCtx.getKey(term, action, orient); | ||
if (!componentHandlers.index.has(acKey)) { | ||
componentHandlers.index.set(acKey, new AppCtx(term, action, orient)); | ||
} | ||
const ac = componentHandlers.index.get(acKey); | ||
componentHandlers.handlers.set(ac, handler); | ||
this._tao.addInlineHandler(ac.unwrapCtx(), handler); | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
removeComponentHandler({ term, action, orient } = {}, Component) { | ||
if (!this._components.has(Component)) { | ||
return this; | ||
} | ||
const componentHandlers = this._components.get(Component); | ||
if (!term && !action && !orient) { | ||
// remove all handlers | ||
componentHandlers.handlers.forEach((ac, handler) => { | ||
this._tao.removeInlineHandler(ac.unwrapCtx(), handler); | ||
}); | ||
this._components.delete(Component); | ||
return this; | ||
} | ||
const ctx = Object.assign(this.defaultCtx, { term, action, orient }); | ||
const permutations = cartesian(ctx); | ||
permutations.forEach(({ term: t, action: a, orient: o }) => { | ||
const acKey = AppCtx.getKey(t, a, o); | ||
const ac = componentHandlers.index.get(acKey); | ||
if (!ac) { | ||
return; | ||
} | ||
componentHandlers.index.delete(acKey); | ||
if (!componentHandlers.handlers.has(ac)) { | ||
return; | ||
} | ||
const handler = componentHandlers.handlers.get(ac); | ||
this._tao.removeInlineHandler(ac.unwrapCtx(), handler); | ||
componentHandlers.handlers.delete(ac); | ||
}); | ||
return this; | ||
} | ||
|
||
// registerReactor(reactor) { | ||
// // DO I NEED THIS? | ||
// } | ||
|
||
// unregisterReactor(reactor) { | ||
// // DO I NEED THIS? | ||
// } | ||
} | ||
|
||
export default Provider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import React, { Component } from 'react'; | ||
import { AppCtx } from '@tao.js/core'; | ||
import Kernel from '@tao.js/core/src/Kernel'; | ||
import Provider from './Provider'; | ||
|
||
const TERM = 'colleague'; | ||
const ACTION = 'hug'; | ||
const ORIENT = 'justfriends'; | ||
|
||
let TAO = null; | ||
function initTAO() { | ||
TAO = new Kernel(); | ||
} | ||
function clearTAO() { | ||
TAO = null; | ||
} | ||
|
||
beforeEach(initTAO); | ||
afterEach(clearTAO); | ||
|
||
describe('Provider exports a class', () => { | ||
it('should provide a constructor that takes a TAO as argument', () => { | ||
// Assemble | ||
// Act | ||
// Assert | ||
expect(Provider).toBeDefined(); | ||
expect(new Provider(TAO)).toBeInstanceOf(Provider); | ||
}); | ||
|
||
it('should provide a defaultCtx getter to retrieve the default AC', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
// Act | ||
// Assert | ||
expect(uut.defaultCtx).toBeDefined(); | ||
expect(uut.defaultCtx).toMatchObject({}); | ||
}); | ||
|
||
it('should provide a defaultCtx setter which will set the defaults for ACs', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
// Act | ||
uut.defaultCtx = { term: TERM }; | ||
// Assert | ||
expect(uut.defaultCtx).toMatchObject({ term: TERM }); | ||
}); | ||
|
||
it('should not allow return from defaultCtx getter to update the default AC', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
// Act | ||
uut.defaultCtx = { term: TERM }; | ||
const defCtx = uut.defaultCtx; | ||
defCtx.action = ACTION; | ||
// Assert | ||
expect(uut.defaultCtx).toMatchObject({ term: TERM }); | ||
expect(uut.defaultCtx).not.toMatchObject({ action: ACTION }); | ||
}); | ||
|
||
it('should provide a chainable setDefaultCtx to set AC defaults to reduce verbosity', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
const defCtx = { term: TERM, action: ACTION }; | ||
// Act | ||
const returned = uut.setDefaultCtx(defCtx); | ||
// Assert | ||
expect(uut.setDefaultCtx).toBeDefined(); | ||
expect(uut.setDefaultCtx).toBeInstanceOf(Function); | ||
expect(returned).toBe(uut); | ||
expect(uut.defaultCtx).toMatchObject(defCtx); | ||
}); | ||
|
||
it('should provide a `current` getter to retrieve the current Component context - initially null', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
// Act | ||
// Assert | ||
expect(uut.current).toBeDefined(); | ||
expect(uut.current).toBeNull(); | ||
}); | ||
}); | ||
|
||
describe('Provider integrates with React', () => { | ||
describe('by allowing to add Components as AC Handlers', () => { | ||
it('should provide an addComponentHandler chainable function', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
const ac = new AppCtx(TERM, ACTION, ORIENT); | ||
// Act | ||
// Assert | ||
expect(uut.addComponentHandler).toBeDefined(); | ||
expect(uut.addComponentHandler).toBeInstanceOf(Function); | ||
expect(uut.addComponentHandler(ac.unwrapCtx(true), Component)).toBe(uut); | ||
}); | ||
|
||
it('should update the `current` value using the Component when a matching AC is triggered', async () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
const triggerData = { a: 1 }; | ||
const triggerAc = new AppCtx(TERM, ACTION, ORIENT, [triggerData]); | ||
uut.addComponentHandler(triggerAc.unwrapCtx(true), Component); | ||
// const initialCurrent = uut.current; | ||
// Act | ||
await TAO.setAppCtx(triggerAc); | ||
// Assert | ||
expect(uut.current).not.toBeNull(); | ||
expect(uut.current).toMatchObject({ | ||
Component, | ||
tao: triggerAc.unwrapCtx(), | ||
props: { | ||
[TERM]: triggerData | ||
} | ||
}); | ||
}); | ||
|
||
it('should add the same Component to multiple ACs', () => {}); | ||
|
||
it('should use the defaultCtx to fill in missing parts of the AC when adding a Component', () => {}); | ||
|
||
it('should allow defining default props passed to the Component', () => {}); | ||
}); | ||
|
||
describe('can also remove Components from handling ACs', () => { | ||
it('should provide a removeComponentHandler chainable function', () => { | ||
// Assemble | ||
const uut = new Provider(TAO); | ||
const ac = new AppCtx(TERM, ACTION, ORIENT); | ||
// Act | ||
// Assert | ||
expect(uut.removeComponentHandler).toBeDefined(); | ||
expect(uut.removeComponentHandler).toBeInstanceOf(Function); | ||
expect(uut.removeComponentHandler(ac.unwrapCtx(true), Component)).toBe( | ||
uut | ||
); | ||
}); | ||
|
||
it('should remove a Component from handling an AC', () => {}); | ||
|
||
it('should remove a Component entirely (all ACs)', () => {}); | ||
|
||
it('should not remove a Component handler for an AC that it was not added for', () => {}); | ||
}); | ||
}); |