overmind
Version:
Frictionless state management
427 lines • 13.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const _1 = require("./");
const config_1 = require("./config");
function toJSON(obj) {
return JSON.parse(JSON.stringify(obj));
}
class StateValue {
constructor() {
this.value = 'foo';
}
toJSON() {
return {
[_1.SERIALIZE]: true,
value: this.value,
};
}
static fromJSON(json) {
const instance = new StateValue();
instance.value = json.value;
return instance;
}
}
function createDefaultOvermind() {
const state = {
foo: 'bar',
item: {
isAwesome: true,
},
value: null,
};
const changeFoo = (context) => {
context.state.foo = 'bar2';
return 'changeFoo';
};
const changeFooWithEffect = (context) => {
context.state.foo = context.effects.hello();
};
const waitAndChangeFoo = (context) => {
return context.effects.wait().then(() => {
context.state.foo = 'bar2';
});
};
const asyncChangeFoo = (context) => tslib_1.__awaiter(this, void 0, void 0, function* () {
yield Promise.resolve();
context.state.foo = 'bar2';
});
const changeValue = (_, value) => {
value.isAwesome = !value.isAwesome;
};
const changeOptionalFoo = (context, newFoo) => {
if (newFoo !== undefined) {
context.state.foo = newFoo;
}
else {
context.state.foo = 'default-foo';
}
};
const asyncChangeOptionalFoo = (context, newFoo) => tslib_1.__awaiter(this, void 0, void 0, function* () {
yield Promise.resolve();
if (newFoo !== undefined) {
context.state.foo = newFoo;
}
else {
context.state.foo = 'async-default-foo';
}
});
const changeFormValue = (_, payload) => {
const { form, key, value } = payload;
form[key] = value;
};
const rehydrateAction = ({ state }, newState) => {
(0, _1.rehydrate)(state, newState, {
value: StateValue.fromJSON,
});
};
const actions = {
asyncChangeFoo,
changeFormValue,
changeFoo,
changeFooWithEffect,
changeValue,
waitAndChangeFoo,
rehydrateAction,
changeOptionalFoo,
asyncChangeOptionalFoo,
};
const effects = {
hello() {
return 'hello';
},
wait() {
return Promise.resolve();
},
};
const config = {
state,
actions,
effects,
};
const app = new _1.Overmind(config);
return app;
}
describe('Overmind', () => {
test('should instantiate app with state', () => {
const app = new _1.Overmind({
state: {
foo: 'bar',
},
});
expect(app.state.foo).toEqual('bar');
});
test('should instantiate app with onInitialize', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(2);
let value;
const app = new _1.Overmind({
state: {
foo: 'bar',
},
actions: {
onInitializeOvermind(context, val) {
expect(context.state.foo).toBe('bar');
value = val;
},
doThis() { },
},
});
yield app.initialized;
expect(value).toBe(app);
}));
test('should be able to type actions', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(app.state.foo).toBe('bar');
app.actions.changeFoo();
expect(app.state.foo).toBe('bar2');
});
test('should allow changing state in actions', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(app.state.foo).toBe('bar');
app.actions.changeFoo();
expect(app.state.foo).toBe('bar2');
});
test('should expose effects to actions', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(app.state.foo).toBe('bar');
app.actions.changeFooWithEffect();
expect(app.state.foo).toBe('hello');
});
test('should allow actions to return a value', () => {
const app = createDefaultOvermind();
expect(app.actions.changeFoo()).toBe('changeFoo');
});
test('should be able to do mutations async via effects', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(app.state.foo).toBe('bar');
return app.actions.waitAndChangeFoo().then(() => {
expect(app.state.foo).toBe('bar2');
});
});
test('should track action start and end', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(2);
const app = new _1.Overmind({
actions: {
doThis() { },
},
});
yield app.initialized;
app.eventHub.once(_1.EventType.ACTION_START, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'doThis',
actionName: 'doThis',
isRunning: true,
namespacePath: [],
executionId: 1,
operatorId: 0,
path: [],
type: 'action',
});
});
app.eventHub.on(_1.EventType.ACTION_END, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'doThis',
isRunning: false,
executionId: 1,
actionName: 'doThis',
namespacePath: [],
operatorId: 0,
path: [],
type: 'action',
});
});
app.actions.doThis();
}));
test('should track operator start and end', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(2);
const app = new _1.Overmind({
actions: {
doThis() { },
},
});
yield app.initialized;
app.eventHub.once(_1.EventType.OPERATOR_START, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'doThis',
isRunning: true,
actionName: 'doThis',
path: [],
executionId: 1,
operatorId: 0,
namespacePath: [],
type: 'action',
});
});
app.eventHub.once(_1.EventType.OPERATOR_END, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'doThis',
isRunning: false,
actionName: 'doThis',
path: [],
isAsync: false,
executionId: 1,
operatorId: 0,
namespacePath: [],
type: 'action',
});
});
app.actions.doThis();
}));
test('should track mutations', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(1);
const app = createDefaultOvermind();
yield app.initialized;
app.eventHub.once(_1.EventType.MUTATIONS, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'changeFoo',
isRunning: true,
actionName: 'changeFoo',
mutations: [
{
args: ['bar2'],
method: 'set',
path: 'foo',
hasChangedValue: true,
delimiter: '.',
},
],
executionId: 1,
operatorId: 0,
namespacePath: [],
path: [],
type: 'action',
});
});
app.actions.changeFoo();
}));
test('should track async mutations', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(1);
const app = createDefaultOvermind();
yield app.initialized;
app.eventHub.on(_1.EventType.MUTATIONS, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'waitAndChangeFoo',
isRunning: true,
actionName: 'waitAndChangeFoo',
mutations: [
{
args: ['bar2'],
method: 'set',
path: 'foo',
hasChangedValue: true,
delimiter: '.',
},
],
executionId: 1,
operatorId: 0,
namespacePath: [],
path: [],
type: 'action',
});
});
yield app.actions.waitAndChangeFoo();
}));
test('should track async mutations with async await', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
expect.assertions(1);
const app = createDefaultOvermind();
yield app.initialized;
app.eventHub.on(_1.EventType.MUTATIONS, (data) => {
expect(toJSON(data)).toEqual({
actionId: 'asyncChangeFoo',
isRunning: true,
actionName: 'asyncChangeFoo',
mutations: [
{
args: ['bar2'],
method: 'set',
path: 'foo',
hasChangedValue: true,
delimiter: '.',
},
],
executionId: 1,
operatorId: 0,
path: [],
namespacePath: [],
type: 'action',
});
});
yield app.actions.asyncChangeFoo();
}));
test('should instantiate app with modules', () => {
const foo = {
state: {
foo: 'bar',
},
actions: {
foo() { },
},
};
const bar = {
state: {
bar: 'baz',
},
effects: {
hello: () => 'hello',
},
actions: {
bar() { },
},
};
const config = Object.assign({}, (0, config_1.namespaced)({
foo,
bar,
}));
const app = new _1.Overmind(config);
expect(app.state.foo.foo).toEqual('bar');
expect(app.state.bar.bar).toEqual('baz');
expect(typeof app.actions.foo.foo).toBe('function');
expect(typeof app.actions.bar.bar).toBe('function');
});
test('should instantiate modules with onInitialize', () => {
const result = [];
const app = new _1.Overmind((0, config_1.namespaced)({
foo: {
actions: {
onInitializeOvermind: () => {
result.push('foo');
},
},
},
bar: {
actions: {
onInitializeOvermind: () => {
result.push('bar');
},
},
},
}));
return app.initialized.then(() => {
expect(result).toEqual(['foo', 'bar']);
});
});
test('should allow mutations on passed values', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(() => app.actions.changeValue(app.state.item)).not.toThrow();
expect(app.state.item.isAwesome).toBe(false);
});
test('should allow mutations on passed values in object', () => {
expect.assertions(2);
const app = createDefaultOvermind();
expect(() => app.actions.changeFormValue({
form: app.state.item,
key: 'isAwesome',
value: false,
})).not.toThrow();
expect(app.state.item.isAwesome).toBe(false);
});
test('should rehydrate mutations on hot reload', () => {
expect.assertions(2);
const app = createDefaultOvermind();
app.actions.changeFoo();
expect(app.state.foo).toBe('bar2');
app.reconfigure({
state: {
foo: 'bar',
},
});
expect(app.state.foo).toBe('bar2');
});
test('should rehydrate actions on hot reload', () => {
expect.assertions(2);
const app = createDefaultOvermind();
const changeFoo = app.actions.changeFoo;
app.reconfigure({
state: {
foo: 'bar2',
},
actions: {
changeFoo(context) {
context.state.foo = 'replaced!';
},
},
});
expect(app.state.foo).toBe('bar2');
changeFoo();
expect(app.state.foo).toBe('replaced!');
});
test('should allow actions with optional parameter', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const app = createDefaultOvermind();
app.actions.changeOptionalFoo();
expect(app.state.foo).toBe('default-foo');
yield app.actions.asyncChangeOptionalFoo();
expect(app.state.foo).toBe('async-default-foo');
const newFoo = 'new-foo';
app.actions.changeOptionalFoo(newFoo);
expect(app.state.foo).toBe(newFoo);
const newAsyncFoo = 'new-async-foo';
yield app.actions.asyncChangeOptionalFoo(newAsyncFoo);
expect(app.state.foo).toBe(newAsyncFoo);
}));
});
//# sourceMappingURL=index.test.js.map