overmind
Version:
Frictionless state management
243 lines • 7.34 kB
JavaScript
import { PROXY_TREE } from 'proxy-state-tree';
import { EventType, Overmind, derived } from './';
describe('Derived', () => {
test('should instantiate app with derived state', () => {
const state = {
foo: 'bar',
upperFoo: derived((state) => state.foo.toUpperCase()),
};
const config = {
state,
};
const app = new Overmind(config);
expect(app.state.upperFoo).toEqual('BAR');
});
test('should dynamically remove derived', async () => {
let dirtyCount = 0;
const removeDerived = ({ state }) => {
state.upperFoo = null;
};
const changeFoo = ({ state }, count) => {
state.foo = 'bar' + count;
};
const state = {
foo: 'bar',
upperFoo: derived((state) => state.foo.toUpperCase()),
};
const actions = {
changeFoo,
removeDerived,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
const trackStateTree = app.getTrackStateTree();
const onTrack = () => { };
function render() {
trackStateTree.track(onTrack);
app.state.upperFoo;
}
app.eventHub.on(EventType.DERIVED_DIRTY, () => {
dirtyCount++;
});
render();
expect(app.state.upperFoo).toBe('BAR');
app.actions.changeFoo(2);
expect(app.state.upperFoo).toBe('BAR2');
app.actions.removeDerived();
app.actions.changeFoo(3);
// The dirty event is async
await new Promise((resolve) => setTimeout(resolve, 0));
expect(dirtyCount).toBe(1);
});
test('should dynamically add derived', () => {
const addDerived = ({ state }) => {
state.upperFoo = derived((state) => state.foo.toUpperCase());
};
const changeFoo = ({ state }) => {
state.foo = 'bar2';
};
const state = {
foo: 'bar',
upperFoo: null,
};
const actions = {
changeFoo,
addDerived,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
const trackStateTree = app.getTrackStateTree();
const onTrack = () => { };
function render() {
trackStateTree.track(onTrack);
app.state.upperFoo;
}
render();
app.actions.addDerived();
expect(app.state.upperFoo).toBe('BAR');
app.actions.changeFoo();
expect(app.state.upperFoo).toBe('BAR2');
});
test('should allow access to proxy state tree to continue tracking with derived function', () => {
let renderCount = 0;
const changeFoo = ({ state }) => {
state.foo = 'bar2';
};
const state = {
foo: 'bar',
upperFoo: derived(() => function () {
const state = this[PROXY_TREE].state;
return state.foo.toUpperCase();
}),
};
const actions = {
changeFoo,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
const trackStateTree = app.getTrackStateTree();
const onTrack = () => {
renderCount++;
};
function render() {
trackStateTree.track(onTrack);
app.state.upperFoo();
}
render();
app.actions.changeFoo();
expect(renderCount).toBe(1);
});
test('should track derived state', () => {
let renderCount = 0;
const changeFoo = ({ state }) => {
state.foo = 'bar2';
};
const state = {
foo: 'bar',
upperFoo: derived((state) => state.foo.toUpperCase()),
};
const actions = {
changeFoo,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
const trackStateTree = app.getTrackStateTree();
const onTrack = () => {
renderCount++;
};
function render() {
trackStateTree.track(onTrack);
app.state.upperFoo;
}
render();
app.actions.changeFoo();
expect(app.state.upperFoo).toBe('BAR2');
expect(renderCount).toBe(1);
});
test('should not require flush to flag as dirty', () => {
expect.assertions(1);
const state = {
foo: 'bar',
upperFoo: derived((state) => state.foo.toUpperCase()),
};
const changeFoo = ({ state }) => {
state.foo = 'bar2';
expect(state.upperFoo).toBe('BAR2');
};
const actions = {
changeFoo,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
app.actions.changeFoo();
});
test('should pass parent as second argument', () => {
expect.assertions(1);
const changeFoo = ({ state }) => {
state.foo = 'bar2';
expect(state.upperFoo).toBe('BAR2');
};
const state = {
foo: 'bar',
upperFoo: derived((_, parent) => parent.foo.toUpperCase()),
};
const actions = {
changeFoo,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
app.actions.changeFoo();
});
test('should track nested derived correctly', () => {
expect.assertions(4);
let runCount = 0;
const changeFoo = ({ state }) => {
state.nestedDerived.foo = 'bar2';
expect(state.nestedDerived.upperFoo).toBe('BAR2');
};
const state = {
nestedDerived: {
foo: 'bar',
upperFoo: derived((parent) => parent.foo.toUpperCase()),
},
};
const actions = {
changeFoo,
};
const config = {
state,
actions,
};
const app = new Overmind(config);
const trackStateTree = app.getTrackStateTree();
function render() {
runCount++;
if (runCount === 1) {
expect(trackStateTree.state.nestedDerived.upperFoo).toBe('BAR');
}
else {
expect(trackStateTree.state.nestedDerived.upperFoo).toBe('BAR2');
}
trackStateTree.stopTracking();
}
trackStateTree.track(render);
render();
app.actions.changeFoo();
expect(runCount).toBe(2);
});
test('should be able to add derived to class instances', () => {
class SomeClass {
constructor() {
this.foo = derived((state) => state.bar + '!');
this.bar = 'foo';
}
}
const state = {
class: new SomeClass(),
};
const config = {
state,
};
const app = new Overmind(config);
expect(app.state.class.foo).toEqual('foo!');
});
});
//# sourceMappingURL=derived.test.js.map