import { buildMiddleware } from '.';
import { action } from '../actions';

test('single effect', async () => {
    const effect = (api) => (next) => (action) => {
        expect(action).toMatchObject({ type: 'foo' });
        expect(api).toHaveProperty('actions');
        expect(api).toHaveProperty('selectors');
        next();
    };

    const middleware = buildMiddleware([effect]);

    await new Promise((resolve) => {
        middleware({})(resolve)({ type: 'foo' });
    });
});

test('augmented api', async () => {
    const effect = (api) => (next) => (action) => {
        // selectors
        expect(api.getState.getB()).toEqual(1);
        expect(api.selectors.getB({ a: { b: 2 } })).toEqual(2);

        expect(api.getState()).toMatchObject({ a: { b: 1 } });

        // dispatch
        expect(api.actions.actionOne()).toMatchObject({ type: 'actionOne' });
        api.dispatch.actionOne('the payload');

        next();
    };

    const middleware = buildMiddleware(
        [effect],
        {
            actionOne: action('actionOne'),
        },
        {
            getB: (state) => state.a.b,
        }
    );

    const getState = jest.fn(() => ({ a: { b: 1 } }));
    const dispatch = jest.fn(() => null);

    await new Promise((resolve) => {
        middleware({
            getState,
            dispatch,
        })(resolve)({ type: 'foo' });
    });

    expect(dispatch).toBeCalledTimes(1);
    expect(dispatch).toBeCalledWith({
        type: 'actionOne',
        payload: 'the payload',
    });
});

test('subduxes', async () => {
    const effect1 = (api) => (next) => (action) => {
        next({
            type: action.type,
            payload: [...action.payload, 1],
        });
    };
    const effect2 = (api) => (next) => (action) => {
        next({
            type: action.type,
            payload: [...action.payload, 2],
        });
    };
    const effect3 = (api) => (next) => (action) => {
        next({
            type: action.type,
            payload: [...action.payload, 3],
        });
    };

    const mw = buildMiddleware(
        [effect1],
        {},
        {},
        {
            a: { middleware: effect2 },
            b: { middleware: effect3 },
        }
    );

    mw({})(({ payload }) => {
        expect(payload).toMatchObject([1, 2, 3]);
    })({ type: 'foo', payload: [] });

    // api for subduxes
    const effect = (api) => (next) => (action) => {
        expect(api.getState()).toEqual(3);
        next();
    };

    const mwInner = buildMiddleware([effect]);
    const mwOuter = buildMiddleware(
        [],
        {},
        {},
        { alpha: { middleware: mwInner } }
    );

    await new Promise((resolve) => {
        mwOuter({
            getState: () => ({ alpha: 3 }),
        })(resolve)({ type: 'foo' });
    });
});
