redux-promise-middleware-actions
Version:
Redux action creator for making async actions with redux-promise-middleware
215 lines (184 loc) • 6.16 kB
text/typescript
import assert from 'assert';
import {
createAction,
createAsyncAction,
onFulfilled,
onPending,
onRejected,
} from '../src/actions';
describe('actions', () => {
const TYPE = 'TEST_ACTION';
describe('createAction', () => {
it('should create an action object with type and no payload', () => {
const action = createAction(TYPE);
assert.deepEqual(action(), { type: TYPE });
});
it('should output action name on toString()', () => {
const action = createAction(TYPE);
assert.strictEqual(action.toString(), TYPE);
});
it('should add the input to the payload', () => {
const action = createAction(TYPE, (input: number) => input);
assert.strictEqual(action(1234).payload, 1234);
});
it('should execute the action creator and add it to the payload', () => {
const action = createAction(TYPE, (a: number, b: number) => a + b);
assert.strictEqual(action(40, 2).payload, 42);
});
describe('metadataCreator', () => {
it('should not have metadata', () => {
const action = createAction(TYPE, (n: number) => ({ n }));
assert.strictEqual('meta' in action(42), false);
});
it('should forward same payload and metadata', () => {
const action = createAction(
TYPE,
(n: number) => ({ n }),
(n: number) => ({ n })
);
assert.deepEqual(action(42), {
type: TYPE,
payload: { n: 42 },
meta: { n: 42 },
});
});
it('should have different payload and metadata', () => {
const action = createAction(
TYPE,
(n: number) => ({ n }),
() => ({ asdf: 1234 })
);
assert.deepEqual(action(42), {
type: TYPE,
payload: { n: 42 },
meta: { asdf: 1234 },
});
});
it('should have only metadata', () => {
const action = createAction(
TYPE,
() => undefined,
() => ({ asdf: 1234 })
);
assert.deepEqual(action(), {
type: TYPE,
payload: undefined,
meta: { asdf: 1234 },
});
});
});
});
describe('createAsyncAction', () => {
const action = createAsyncAction(TYPE, (s: string) => Promise.resolve(s));
it('should create an action with correct type', () => {
assert.strictEqual(action('x').type, TYPE);
});
it('should create an action with Promise payload', async () => {
assert(action('x').payload instanceof Promise);
});
it('should create an action with correct payload', async () => {
assert.strictEqual(await action('x').payload, 'x');
});
it('should throw on toString()', () => {
assert.throws(
() => action.toString(),
new RegExp(
`Async action ${TYPE} must be handled with pending, fulfilled or rejected`
)
);
});
it('should create action with 0 arguments', async () => {
const action0 = createAsyncAction(TYPE, () => Promise.resolve(42));
assert.strictEqual(await action0().payload, 42);
});
it('should create action with 1 arguments', async () => {
const action1 = createAsyncAction(TYPE, (n: number) =>
Promise.resolve(n)
);
assert.strictEqual(await action1(42).payload, 42);
});
it('should create action with 2 arguments', async () => {
const action1 = createAsyncAction(TYPE, (n: number, s: string) =>
Promise.resolve({ n, s })
);
assert.deepEqual(await action1(42, 'hello').payload, {
n: 42,
s: 'hello',
});
});
it('should create action with 3 arguments', async () => {
const action1 = createAsyncAction(
TYPE,
(n: number, s: string, b: boolean) => Promise.resolve({ n, s, b })
);
assert.deepEqual(await action1(42, 'hello', true).payload, {
n: 42,
s: 'hello',
b: true,
});
});
describe('pending', () => {
it('should have a pending action prop', () => {
assert('pending' in action);
});
it('should output pending action name on toString()', () => {
assert.strictEqual(action.pending.toString(), onPending(TYPE));
});
});
describe('fulfilled', () => {
it('should have a fulfilled action prop', () => {
assert('fulfilled' in action);
});
it('should output fulfilled action name on toString()', () => {
assert.strictEqual(action.fulfilled.toString(), onFulfilled(TYPE));
});
it('should create a fulfilled action with payload', () => {
assert.deepEqual(action.fulfilled('payload'), {
type: onFulfilled(TYPE),
payload: 'payload',
});
});
});
describe('rejected', () => {
it('should have a rejected action prop', () => {
assert('rejected' in action);
});
it('should output rejected action name on toString()', () => {
assert.strictEqual(action.rejected.toString(), onRejected(TYPE));
});
it('should create a rejected action with payload', () => {
const err = new Error('error');
assert.deepEqual(action.rejected(err), {
type: onRejected(TYPE),
payload: err,
});
});
});
describe('promiseTypeDelimiter', () => {
const actionDelimiter = createAsyncAction(
TYPE,
(s: string) => Promise.resolve(s),
undefined,
{ promiseTypeDelimiter: '#' }
);
it('should use custom delimiter in pending action type', () => {
assert.strictEqual(
actionDelimiter.pending.toString(),
onPending(TYPE, '#')
);
});
it('should use custom delimiter in fulfilled action type', () => {
assert.strictEqual(
actionDelimiter.fulfilled.toString(),
onFulfilled(TYPE, '#')
);
});
it('should use custom delimiter in rejected action type', () => {
assert.strictEqual(
actionDelimiter.rejected.toString(),
onRejected(TYPE, '#')
);
});
});
});
});