typescript
Yanick Champoux 2020-01-27 23:35:01 -05:00
parent 931377b584
commit 4c0d7b366f
3 changed files with 284 additions and 222 deletions

View File

@ -1,6 +1,6 @@
module.exports = {
"roots": [
"./src"
"./dist"
],
"transform": {
"^.+\\.ts$": "ts-jest",

View File

@ -1,228 +1,277 @@
import Updux, { actionCreator } from ".";
import u from "updeep";
import Updux, { actionCreator } from '.';
import u from 'updeep';
import mwUpdux from './middleware_aux';
test("simple effect", () => {
const tracer = jest.fn();
test('simple effect', () => {
const tracer = jest.fn();
const store = new Updux({
effects: {
foo: (api: any) => (next: any) => (action: any) => {
tracer();
next(action);
}
}
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch({ type: "bar" });
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).toHaveBeenCalled();
});
test("effect and sub-effect", () => {
const tracer = jest.fn();
const tracerEffect = (signature: string) => (api: any) => (next: any) => (
action: any
) => {
tracer(signature);
next(action);
};
const store = new Updux({
effects: {
foo: tracerEffect("root")
},
subduxes: {
zzz: {
const store = new Updux({
effects: {
foo: tracerEffect("child")
}
}
}
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch({ type: "bar" });
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).toHaveBeenNthCalledWith(1, "root");
expect(tracer).toHaveBeenNthCalledWith(2, "child");
});
test('"*" effect', () => {
const tracer = jest.fn();
const store = new Updux({
effects: {
"*": api => next => action => {
tracer();
next(action);
}
}
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch({ type: "bar" });
expect(tracer).toHaveBeenCalled();
});
test("async effect", async () => {
function timeout(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const tracer = jest.fn();
const store = new Updux({
effects: {
foo: api => next => async action => {
next(action);
await timeout(1000);
tracer();
}
}
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).not.toHaveBeenCalled();
await timeout(1000);
expect(tracer).toHaveBeenCalled();
});
test("getState is local", () => {
let childState;
let rootState;
let rootFromChild;
const child = new Updux({
initial: { alpha: 12 },
effects: {
doIt: ({ getState, getRootState }) => next => action => {
childState = getState();
rootFromChild = getRootState();
next(action);
}
}
});
const root = new Updux({
initial: { beta: 24 },
subduxes: { child },
effects: {
doIt: ({ getState }) => next => action => {
rootState = getState();
next(action);
}
}
});
const store = root.createStore();
store.dispatch.doIt();
expect(rootState).toEqual({ beta: 24, child: { alpha: 12 } });
expect(rootFromChild).toEqual({ beta: 24, child: { alpha: 12 } });
expect(childState).toEqual({ alpha: 12 });
});
test("middleware as map", () => {
let childState;
let rootState;
let rootFromChild;
const doIt = actionCreator("doIt");
const child = new Updux({
initial: "",
effects: [
[
doIt,
() => next => action => {
next(u({ payload: (p: string) => p + "Child" }, action) as any);
}
]
]
});
const root = new Updux({
initial: { message: "" },
subduxes: { child },
effects: [
[
"^",
() => next => action => {
next(u({ payload: (p: string) => p + "Pre" }, action) as any);
}
],
[
doIt,
() => next => action => {
next(u({ payload: (p: string) => p + "Root" }, action) as any);
}
],
[
"*",
() => next => action => {
next(u({ payload: (p: string) => p + "After" }, action) as any);
}
],
[
"$",
() => next => action => {
next(u({ payload: (p: string) => p + "End" }, action) as any);
}
]
],
mutations: [[doIt, (message: any) => () => ({ message })]]
});
const store = root.createStore();
store.dispatch.doIt("");
expect(store.getState()).toEqual({ message: "PreRootAfterChildEnd" });
});
test("generator", () => {
const updux = new Updux({
initial: 0,
mutations: [["doIt", payload => () => payload]],
effects: [
[
"doIt",
() => {
let i = 0;
return () => (next: any) => (action: any) =>
next({ ...action, payload: ++i });
foo: (api: any) => (next: any) => (action: any) => {
tracer();
next(action);
},
},
true
]
]
});
}).createStore();
const store1 = updux.createStore();
store1.dispatch.doIt();
expect(store1.getState()).toEqual(1);
store1.dispatch.doIt();
expect(store1.getState()).toEqual(2);
updux.actions;
expect(tracer).not.toHaveBeenCalled();
const store2 = updux.createStore();
store2.dispatch.doIt();
expect(store2.getState()).toEqual(1);
store.dispatch({ type: 'bar' });
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).toHaveBeenCalled();
});
test('effect and sub-effect', () => {
const tracer = jest.fn();
const tracerEffect = (signature: string) => (api: any) => (next: any) => (
action: any
) => {
tracer(signature);
next(action);
};
const store = new Updux({
effects: {
foo: tracerEffect('root'),
},
subduxes: {
zzz: {
effects: {
foo: tracerEffect('child'),
},
},
},
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch({ type: 'bar' });
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).toHaveBeenNthCalledWith(1, 'root');
expect(tracer).toHaveBeenNthCalledWith(2, 'child');
});
describe('"*" effect', () => {
test('from the constructor', () => {
const tracer = jest.fn();
const store = new Updux({
effects: {
'*': api => next => action => {
tracer();
next(action);
},
},
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch({ type: 'bar' });
expect(tracer).toHaveBeenCalled();
});
test('from addEffect', () => {
const tracer = jest.fn();
const updux = new Updux({});
updux.addEffect('*', api => next => action => {
tracer();
next(action);
});
expect(tracer).not.toHaveBeenCalled();
updux.createStore().dispatch({ type: 'bar' });
expect(tracer).toHaveBeenCalled();
});
test('action can be modified', () => {
const mw = mwUpdux.middleware;
const next = jest.fn();
mw({dispatch:{}} as any)(next as any)({type: 'bar'});
expect(next).toHaveBeenCalled();
expect(next.mock.calls[0][0]).toMatchObject({meta: 'gotcha'});
});
});
test('async effect', async () => {
function timeout(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const tracer = jest.fn();
const store = new Updux({
effects: {
foo: api => next => async action => {
next(action);
await timeout(1000);
tracer();
},
},
}).createStore();
expect(tracer).not.toHaveBeenCalled();
store.dispatch.foo();
expect(tracer).not.toHaveBeenCalled();
await timeout(1000);
expect(tracer).toHaveBeenCalled();
});
test('getState is local', () => {
let childState;
let rootState;
let rootFromChild;
const child = new Updux({
initial: { alpha: 12 },
effects: {
doIt: ({ getState, getRootState }) => next => action => {
childState = getState();
rootFromChild = getRootState();
next(action);
},
},
});
const root = new Updux({
initial: { beta: 24 },
subduxes: { child },
effects: {
doIt: ({ getState }) => next => action => {
rootState = getState();
next(action);
},
},
});
const store = root.createStore();
store.dispatch.doIt();
expect(rootState).toEqual({ beta: 24, child: { alpha: 12 } });
expect(rootFromChild).toEqual({ beta: 24, child: { alpha: 12 } });
expect(childState).toEqual({ alpha: 12 });
});
test('middleware as map', () => {
let childState;
let rootState;
let rootFromChild;
const doIt = actionCreator('doIt');
const child = new Updux({
initial: '',
effects: [
[
doIt,
() => next => action => {
next(
u(
{ payload: (p: string) => p + 'Child' },
action
) as any
);
},
],
],
});
const root = new Updux({
initial: { message: '' },
subduxes: { child },
effects: [
[
'^',
() => next => action => {
next(
u({ payload: (p: string) => p + 'Pre' }, action) as any
);
},
],
[
doIt,
() => next => action => {
next(
u({ payload: (p: string) => p + 'Root' }, action) as any
);
},
],
[
'*',
() => next => action => {
next(
u(
{ payload: (p: string) => p + 'After' },
action
) as any
);
},
],
[
'$',
() => next => action => {
next(
u({ payload: (p: string) => p + 'End' }, action) as any
);
},
],
],
mutations: [[doIt, (message: any) => () => ({ message })]],
});
const store = root.createStore();
store.dispatch.doIt('');
expect(store.getState()).toEqual({ message: 'PreRootAfterChildEnd' });
});
test('generator', () => {
const updux = new Updux({
initial: 0,
mutations: [['doIt', payload => () => payload]],
effects: [
[
'doIt',
() => {
let i = 0;
return () => (next: any) => (action: any) =>
next({ ...action, payload: ++i });
},
true,
],
],
});
const store1 = updux.createStore();
store1.dispatch.doIt();
expect(store1.getState()).toEqual(1);
store1.dispatch.doIt();
expect(store1.getState()).toEqual(2);
updux.actions;
const store2 = updux.createStore();
store2.dispatch.doIt();
expect(store2.getState()).toEqual(1);
});

13
src/middleware_aux.ts Normal file
View File

@ -0,0 +1,13 @@
import Updux from '.';
const updux = new Updux({
subduxes: {
foo: { initial: "banana" }
}
});
updux.addEffect('*', api => next => action => {
next({...action, meta: "gotcha" });
});
export default updux;