updux/dist/effects.test.js

196 lines
6.1 KiB
JavaScript
Raw Permalink Normal View History

2025-01-31 18:16:41 +00:00
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?