Skip to content

Commit

Permalink
feat(Provider): implemented and began tests for react/Provider
Browse files Browse the repository at this point in the history
affects: @tao.js/react

all code implemented
tests for basic class and addComponentHandler
specs written but not implemented
  • Loading branch information
eudaimos committed May 12, 2018
1 parent 47de443 commit 448fb2c
Show file tree
Hide file tree
Showing 2 changed files with 250 additions and 0 deletions.
107 changes: 107 additions & 0 deletions packages/react-tao/src/Provider.js
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;
143 changes: 143 additions & 0 deletions packages/react-tao/src/Provider.spec.js
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', () => {});
});
});

0 comments on commit 448fb2c

Please sign in to comment.