@platform/state
Version:
A small, simple, strongly typed, [rx/observable] state-machine.
257 lines (256 loc) • 13.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var test_1 = require("../test");
var rxjs_1 = require("rxjs");
var _1 = require(".");
var initial = { count: 0, foo: { list: [] } };
describe('Store', function () {
describe('lifecycle', function () {
it('constructs', function () {
var store = _1.Store.create({ initial: initial });
(0, test_1.expect)(store.isDisposed).to.eql(false);
(0, test_1.expect)(store.state).to.not.equal(initial);
(0, test_1.expect)(store.state).to.eql(initial);
});
it('disposes', function () {
var store = _1.Store.create({ initial: initial });
var count = 0;
store.dispose$.subscribe(function () { return count++; });
store.dispose();
store.dispose();
(0, test_1.expect)(store.isDisposed).to.eql(true);
(0, test_1.expect)(count).to.eql(1);
});
it('takes event$ at creation', function () {
var event$ = new rxjs_1.Subject();
var store = _1.Store.create({ initial: initial, event$: event$ });
(0, test_1.expect)(store._event$).to.equal(event$);
});
});
describe('state', function () {
it('returns new immutable object from [state] property', function () {
var store = _1.Store.create({ initial: initial });
var state1 = store.state;
var state2 = store.state;
(0, test_1.expect)(store.state).to.eql(initial);
(0, test_1.expect)(store.state).to.not.equal(initial);
(0, test_1.expect)(state1).to.eql(store.state);
(0, test_1.expect)(state1).to.eql(initial);
(0, test_1.expect)(state1).to.not.equal(state2);
(0, test_1.expect)(store.state).to.not.equal(initial);
});
});
describe('dispatch', function () {
it('returns the state object', function () {
var state = _1.Store.create({ initial: initial });
var res = state.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(res).to.equal(res);
});
it('fires dispatch event', function () {
var store = _1.Store.create({ initial: initial });
var events = [];
store.event$.subscribe(function (e) { return events.push(e); });
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
store.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
(0, test_1.expect)(events.length).to.eql(2);
(0, test_1.expect)(events[0].type).to.eql('TEST/increment');
(0, test_1.expect)(events[1].type).to.eql('TEST/decrement');
});
it('fires (via injected event$)', function () {
var event$ = new rxjs_1.Subject();
var store = _1.Store.create({ initial: initial, event$: event$ });
var events = [];
store.event$.subscribe(function (e) { return events.push(e); });
event$.next({ type: 'TEST/increment', payload: { by: 1 } });
event$.next({ type: 'TEST/decrement', payload: { by: 2 } });
(0, test_1.expect)(events.length).to.eql(2);
(0, test_1.expect)(events[0].type).to.eql('TEST/increment');
(0, test_1.expect)(events[1].type).to.eql('TEST/decrement');
});
it('returns copy of the current state object on event', function () {
var store = _1.Store.create({ initial: initial });
var states = [];
store.on('TEST/increment').subscribe(function (e) {
states.push(e.state);
states.push(e.state);
});
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(states.length).to.eql(2);
(0, test_1.expect)(states[0]).to.eql(store.state);
(0, test_1.expect)(states[1]).to.eql(store.state);
(0, test_1.expect)(states[0]).to.not.equal(store.state);
(0, test_1.expect)(states[1]).to.not.equal(store.state);
(0, test_1.expect)(states[0]).to.not.equal(states[1]);
});
it('changes the current state (via {...object})', function () {
var store = _1.Store.create({ initial: initial });
(0, test_1.expect)(store.state.count).to.eql(0);
store.on('TEST/increment').subscribe(function (e) {
var count = e.state.count + e.payload.by;
var next = tslib_1.__assign(tslib_1.__assign({}, e.state), { count: count });
e.change(next);
});
store.on('TEST/decrement').subscribe(function (e) {
var count = e.state.count - e.payload.by;
var next = tslib_1.__assign(tslib_1.__assign({}, e.state), { count: count });
e.change(next);
});
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(store.state.count).to.eql(1);
store.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
(0, test_1.expect)(store.state.count).to.eql(-1);
});
it('changes the current state (via immutable function)', function () {
var store = _1.Store.create({
initial: tslib_1.__assign(tslib_1.__assign({}, initial), { bar: { msg: 'hello' } }),
});
var before = store.state;
(0, test_1.expect)(before.count).to.eql(0);
store.on('TEST/increment').subscribe(function (e) {
e.change(function (draft) {
draft.count += e.payload.by;
});
});
store.on('TEST/changeFoo').subscribe(function (e) {
e.change(function (draft) {
draft.foo.list.push(123);
var foo = draft.foo;
foo.msg = 'hello';
});
});
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
var after1 = store.state;
(0, test_1.expect)(before).to.not.equal(after1);
(0, test_1.expect)(after1.count).to.eql(1);
(0, test_1.expect)(after1.foo).to.equal(before.foo);
(0, test_1.expect)(after1.bar).to.equal(before.bar);
store.dispatch({ type: 'TEST/changeFoo', payload: {} });
var after2 = store.state;
(0, test_1.expect)(after2.foo.list).to.eql([123]);
(0, test_1.expect)(after2.foo.msg).to.eql('hello');
(0, test_1.expect)(after2).to.not.equal(after1);
(0, test_1.expect)(after2.foo).to.not.equal(after1.foo);
(0, test_1.expect)(after2.foo.list).to.not.equal(after1.foo.list);
});
it('fires [changing] event', function () {
var store = _1.Store.create({ initial: initial });
var events = [];
store.changing$.subscribe(function (e) { return events.push(e); });
store.on('TEST/increment').subscribe(function (e) { return e.change(e.state); });
store
.on('TEST/changeFoo')
.subscribe(function (e) { return e.change(function (draft) { return (draft.foo.list = [1, 2, 3]); }); });
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(events.length).to.eql(1);
(0, test_1.expect)(events[0].isCancelled).to.eql(false);
(0, test_1.expect)(events[0].change.type).to.eql('TEST/increment');
store.dispatch({ type: 'TEST/changeFoo', payload: {} });
(0, test_1.expect)(events.length).to.eql(2);
(0, test_1.expect)(events[1].isCancelled).to.eql(false);
(0, test_1.expect)(events[1].change.type).to.eql('TEST/changeFoo');
});
it('cancels change', function () {
var store = _1.Store.create({ initial: initial });
var cancel = false;
store.changing$.subscribe(function (e) {
if (cancel) {
e.cancel();
}
});
store.on('TEST/increment').subscribe(function (e) {
if (e.payload.by > 0) {
var count = e.state.count + e.payload.by;
var next = tslib_1.__assign(tslib_1.__assign({}, e.state), { count: count });
e.change(next);
}
});
store
.on('TEST/changeFoo')
.subscribe(function (e) { return e.change(function (draft) { return (draft.foo.list = [1, 2, 3]); }); });
(0, test_1.expect)(store.state.count).to.eql(0);
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(store.state.count).to.eql(1);
cancel = true;
store.dispatch({ type: 'TEST/increment', payload: { by: 99 } });
(0, test_1.expect)(store.state.count).to.eql(1);
store.dispatch({ type: 'TEST/changeFoo', payload: {} });
(0, test_1.expect)(store.state.foo.list).to.eql([]);
cancel = false;
store.dispatch({ type: 'TEST/changeFoo', payload: {} });
(0, test_1.expect)(store.state.foo.list).to.eql([1, 2, 3]);
});
it('fires [changed] event', function () {
var store = _1.Store.create({ initial: initial });
var events = [];
store.changed$.subscribe(function (e) { return events.push(e); });
store.on('TEST/increment').subscribe(function (e) {
if (e.payload.by > 0) {
var count = e.state.count + e.payload.by;
var next = tslib_1.__assign(tslib_1.__assign({}, e.state), { count: count });
e.change(next);
}
});
store.dispatch({ type: 'TEST/increment', payload: { by: 90 } });
store.dispatch({ type: 'TEST/increment', payload: { by: 0 } });
store.dispatch({ type: 'TEST/increment', payload: { by: 2 } });
(0, test_1.expect)(events.length).to.eql(2);
var change1 = events[0];
var change2 = events[1];
(0, test_1.expect)(change1.type).to.eql('TEST/increment');
(0, test_1.expect)(change1.event.type).to.eql('TEST/increment');
(0, test_1.expect)(change1.event.payload.by).to.eql(90);
(0, test_1.expect)(change2.type).to.eql('TEST/increment');
(0, test_1.expect)(change2.event.type).to.eql('TEST/increment');
(0, test_1.expect)(change2.event.payload.by).to.eql(2);
(0, test_1.expect)(change1.from.count).to.eql(0);
(0, test_1.expect)(change1.to.count).to.eql(90);
(0, test_1.expect)(change2.from.count).to.eql(90);
(0, test_1.expect)(change2.to.count).to.eql(92);
});
});
describe('epics', function () {
it('dispatches a follow-on event (sync)', function () {
var store = _1.Store.create({ initial: initial });
var events = [];
store.event$.subscribe(function (e) { return events.push(e); });
store.on('TEST/increment').subscribe(function (e) {
e.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
});
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(events.length).to.eql(2);
(0, test_1.expect)(events[0].type).to.eql('TEST/increment');
(0, test_1.expect)(events[1].type).to.eql('TEST/decrement');
});
it('dispatches a follow-on event (async)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
var store, events;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
store = _1.Store.create({ initial: initial });
events = [];
store.event$.subscribe(function (e) { return events.push(e); });
store.on('TEST/increment').subscribe(function (e) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, test_1.time.wait(3)];
case 1:
_a.sent();
e.dispatch({ type: 'TEST/decrement', payload: { by: 2 } });
return [2];
}
});
}); });
(0, test_1.expect)(events.length).to.eql(0);
store.dispatch({ type: 'TEST/increment', payload: { by: 1 } });
(0, test_1.expect)(events.length).to.eql(1);
return [4, test_1.time.wait(10)];
case 1:
_a.sent();
(0, test_1.expect)(events.length).to.eql(2);
return [2];
}
});
}); });
});
});