196 lines
6.1 KiB
JavaScript
196 lines
6.1 KiB
JavaScript
|
import { test, expect } from 'vitest';
|
||
|
import Updux from './Updux.js';
|
||
|
import { buildEffectsMiddleware } from './effects.js';
|
||
|
import { createAction, withPayload } from './index.js';
|
||
|
test('addEffect signatures', () => {
|
||
|
const someAction = createAction('someAction', withPayload());
|
||
|
const dux = new Updux({
|
||
|
actions: {
|
||
|
someAction,
|
||
|
}
|
||
|
});
|
||
|
dux.addEffect((api) => (next) => (action) => {
|
||
|
expectTypeOf(action).toMatchTypeOf();
|
||
|
expectTypeOf(next).toMatchTypeOf();
|
||
|
expectTypeOf(api).toMatchTypeOf();
|
||
|
});
|
||
|
dux.addEffect('someAction', (api) => (next) => (action) => {
|
||
|
expectTypeOf(action).toMatchTypeOf();
|
||
|
expectTypeOf(next).toMatchTypeOf();
|
||
|
expectTypeOf(api).toMatchTypeOf();
|
||
|
});
|
||
|
dux.addEffect(someAction, (api) => (next) => (action) => {
|
||
|
expectTypeOf(action).toMatchTypeOf();
|
||
|
expectTypeOf(next).toMatchTypeOf();
|
||
|
expectTypeOf(api).toMatchTypeOf();
|
||
|
});
|
||
|
dux.addEffect((action) => (action === null || action === void 0 ? void 0 : action.payload) === 3, (api) => (next) => (action) => {
|
||
|
expectTypeOf(action).toMatchTypeOf();
|
||
|
expectTypeOf(next).toMatchTypeOf();
|
||
|
expectTypeOf(api).toMatchTypeOf();
|
||
|
});
|
||
|
});
|
||
|
test('buildEffectsMiddleware', () => {
|
||
|
let seen = 0;
|
||
|
const mw = buildEffectsMiddleware([
|
||
|
(api) => (next) => (action) => {
|
||
|
seen++;
|
||
|
expect(api).toHaveProperty('getState');
|
||
|
expect(api.getState).toBeTypeOf('function');
|
||
|
expect(api.getState()).toEqual('the state');
|
||
|
expect(action).toHaveProperty('type');
|
||
|
expect(next).toBeTypeOf('function');
|
||
|
expect(api).toHaveProperty('actions');
|
||
|
expect(api.actions.action1()).toHaveProperty('type', 'action1');
|
||
|
api.dispatch.action1();
|
||
|
expect(api.selectors.getFoo(2)).toBe(2);
|
||
|
expect(api.getState.getFoo()).toBe('the state');
|
||
|
expect(api.getState.getBar(2)).toBe('the state2');
|
||
|
next(action);
|
||
|
},
|
||
|
], {
|
||
|
action1: createAction('action1'),
|
||
|
}, {
|
||
|
getFoo: (state) => state,
|
||
|
getBar: (state) => (i) => state + i,
|
||
|
});
|
||
|
expect(seen).toEqual(0);
|
||
|
const dispatch = vi.fn();
|
||
|
mw({ getState: () => 'the state', dispatch })(() => { })({
|
||
|
type: 'noop',
|
||
|
});
|
||
|
expect(seen).toEqual(1);
|
||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'action1' });
|
||
|
});
|
||
|
test('basic', () => {
|
||
|
const dux = new Updux({
|
||
|
initialState: {
|
||
|
loaded: true,
|
||
|
},
|
||
|
actions: {
|
||
|
foo: null,
|
||
|
},
|
||
|
});
|
||
|
let seen = 0;
|
||
|
dux.addEffect((api) => (next) => (action) => {
|
||
|
seen++;
|
||
|
expect(api).toHaveProperty('getState');
|
||
|
expect(api.getState()).toHaveProperty('loaded');
|
||
|
expect(action).toHaveProperty('type');
|
||
|
expect(next).toBeTypeOf('function');
|
||
|
next(action);
|
||
|
});
|
||
|
const store = dux.createStore();
|
||
|
expect(seen).toEqual(0);
|
||
|
store.dispatch.foo();
|
||
|
expect(seen).toEqual(1);
|
||
|
});
|
||
|
test('subdux', () => {
|
||
|
const bar = new Updux({
|
||
|
initialState: 'bar state',
|
||
|
actions: { foo: null },
|
||
|
});
|
||
|
let seen = 0;
|
||
|
bar.addEffect((api) => (next) => (action) => {
|
||
|
seen++;
|
||
|
expect(api.getState()).toBe('bar state');
|
||
|
next(action);
|
||
|
});
|
||
|
const dux = new Updux({
|
||
|
initialState: {
|
||
|
loaded: true,
|
||
|
},
|
||
|
subduxes: {
|
||
|
bar,
|
||
|
},
|
||
|
});
|
||
|
const store = dux.createStore();
|
||
|
expect(seen).toEqual(0);
|
||
|
store.dispatch.foo();
|
||
|
expect(seen).toEqual(1);
|
||
|
});
|
||
|
test('addEffect with actionCreator', () => {
|
||
|
const dux = new Updux({
|
||
|
actions: {
|
||
|
foo: null,
|
||
|
bar: null,
|
||
|
},
|
||
|
});
|
||
|
const next = vi.fn();
|
||
|
const spy = vi.fn();
|
||
|
const [mw] = dux.addEffect(dux.actions.foo, (api) => (next) => (action) => next(spy(action))).effects;
|
||
|
mw({})(next)(dux.actions.bar());
|
||
|
expect(next).toHaveBeenCalled();
|
||
|
expect(spy).not.toHaveBeenCalled();
|
||
|
next.mockReset();
|
||
|
mw({})(next)(dux.actions.foo());
|
||
|
expect(next).toHaveBeenCalled();
|
||
|
expect(spy).toHaveBeenCalled();
|
||
|
});
|
||
|
test('addEffect with function', () => {
|
||
|
const dux = new Updux({
|
||
|
actions: {
|
||
|
foo: () => { },
|
||
|
bar: () => { },
|
||
|
},
|
||
|
});
|
||
|
const next = vi.fn();
|
||
|
const spy = vi.fn();
|
||
|
const [mw] = dux.addEffect((action) => action.type[0] === 'f', (api) => (next) => (action) => next(spy(action))).effects;
|
||
|
mw({})(next)(dux.actions.bar());
|
||
|
expect(next).toHaveBeenCalled();
|
||
|
expect(spy).not.toHaveBeenCalled();
|
||
|
next.mockReset();
|
||
|
mw({})(next)(dux.actions.foo());
|
||
|
expect(next).toHaveBeenCalled();
|
||
|
expect(spy).toHaveBeenCalled();
|
||
|
});
|
||
|
test('catchall addEffect', () => {
|
||
|
const dux = new Updux({
|
||
|
initialState: {
|
||
|
a: 1,
|
||
|
},
|
||
|
});
|
||
|
const spy = vi.fn();
|
||
|
dux.addEffect((api) => (next) => (action) => {
|
||
|
expectTypeOf(api.getState()).toMatchTypeOf();
|
||
|
spy();
|
||
|
next(action);
|
||
|
});
|
||
|
const store = dux.createStore();
|
||
|
expect(spy).not.toHaveBeenCalled();
|
||
|
store.dispatch({ type: 'noop' });
|
||
|
expect(spy).toHaveBeenCalled();
|
||
|
});
|
||
|
test('addEffect with unknown actionCreator adds it', () => {
|
||
|
const foo = createAction('foo');
|
||
|
const dux = new Updux({}).addEffect(foo, () => () => () => { });
|
||
|
expectTypeOf(dux.actions.foo).toMatchTypeOf();
|
||
|
expect(dux.actions.foo()).toMatchObject({ type: 'foo' });
|
||
|
});
|
||
|
test('effects of subduxes', () => {
|
||
|
const foo = new Updux({
|
||
|
initialState: 12,
|
||
|
actions: {
|
||
|
bar: null,
|
||
|
},
|
||
|
selectors: {
|
||
|
addHundred: (x) => x + 100
|
||
|
}
|
||
|
})
|
||
|
.addMutation(createAction('setFoo', withPayload()), (state) => () => state)
|
||
|
.addEffect(({ type: t }) => t === 'doit', (api) => next => action => {
|
||
|
api.dispatch.setFoo(api.getState.addHundred());
|
||
|
});
|
||
|
const dux = new Updux({
|
||
|
subduxes: {
|
||
|
foo
|
||
|
}
|
||
|
});
|
||
|
const store = dux.createStore();
|
||
|
store.dispatch({ type: "doit" });
|
||
|
expect(store.getState()).toMatchObject({ foo: 112 });
|
||
|
});
|
||
|
// TODO subdux effects
|
||
|
// TODO allow to subscribe / unsubscribe effects?
|