@wordpress/redux-routine
Version:
Redux middleware for generator coroutines.
172 lines (147 loc) • 4.77 kB
JavaScript
/**
* External dependencies
*/
import { createStore, applyMiddleware } from 'redux';
/**
* Internal dependencies
*/
import createMiddleware from '../';
describe( 'createMiddleware', () => {
function createStoreWithMiddleware( middleware ) {
const reducer = ( state = null, action ) => action.nextState || state;
return createStore( reducer, applyMiddleware( middleware ) );
}
it( 'should not alter dispatch flow of uncontrolled action', () => {
const middleware = createMiddleware();
const store = createStoreWithMiddleware( middleware );
store.dispatch( { type: 'CHANGE', nextState: 1 } );
expect( store.getState() ).toBe( 1 );
} );
it( 'should dispatch yielded actions', () => {
const middleware = createMiddleware();
const store = createStoreWithMiddleware( middleware );
function* createAction() {
yield { type: 'CHANGE', nextState: 1 };
}
store.dispatch( createAction() );
expect( store.getState() ).toBe( 1 );
} );
it( 'should continue only once control condition resolves', async () => {
const middleware = createMiddleware( {
WAIT: () => new Promise( ( resolve ) => resolve() ),
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
yield { type: 'WAIT' };
yield { type: 'CHANGE', nextState: 1 };
}
await store.dispatch( createAction() );
expect( store.getState() ).toBe( 1 );
} );
it( 'should throw if promise rejects', async () => {
expect.hasAssertions();
const middleware = createMiddleware( {
WAIT_FAIL: () =>
new Promise( ( resolve, reject ) => reject( 'Message' ) ),
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
try {
yield { type: 'WAIT_FAIL' };
} catch ( error ) {
// eslint-disable-next-line jest/no-conditional-expect
expect( error ).toBe( 'Message' );
}
}
await store.dispatch( createAction() );
} );
it( 'should throw if promise throws', () => {
expect.hasAssertions();
const middleware = createMiddleware( {
WAIT_FAIL: () =>
new Promise( () => {
throw new Error( 'Message' );
} ),
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
try {
yield { type: 'WAIT_FAIL' };
} catch ( error ) {
// eslint-disable-next-line jest/no-conditional-expect
expect( error.message ).toBe( 'Message' );
}
}
return store.dispatch( createAction() );
} );
// Currently this test will not error even under conditions producing it but
// instead will have an uncaught error/warning printed in the cli console:
// - (node:37109) UnhandledPromiseRejectionWarning: TypeError: Cannot read
// property 'type' of null (and others)
// See this github thread for context:
// https://github.com/facebook/jest/issues/3251
it( 'should handle a null returned from a caught promise error', () => {
expect.hasAssertions();
const middleware = createMiddleware( {
WAIT_FAIL: () =>
new Promise( () => {
throw new Error( 'Message' );
} ),
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
try {
yield { type: 'WAIT_FAIL' };
} catch ( error ) {
// eslint-disable-next-line jest/no-conditional-expect
expect( error.message ).toBe( 'Message' );
return null;
}
}
store.dispatch( createAction() );
} );
it( 'assigns sync controlled return value into yield assignment', () => {
const middleware = createMiddleware( {
RETURN_TWO: () => 2,
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
const nextState = yield { type: 'RETURN_TWO' };
yield { type: 'CHANGE', nextState };
}
store.dispatch( createAction() );
expect( store.getState() ).toBe( 2 );
} );
it( 'assigns async controlled return value into yield assignment', async () => {
const middleware = createMiddleware( {
WAIT: ( action ) =>
new Promise( ( resolve ) => {
resolve( action.value );
} ),
} );
const store = createStoreWithMiddleware( middleware );
function* createAction() {
const nextState = yield { type: 'WAIT', value: 2 };
return { type: 'CHANGE', nextState };
}
await store.dispatch( createAction() );
expect( store.getState() ).toBe( 2 );
} );
it(
'does not recurse when action like object returns from a sync ' +
'control',
() => {
const post = { type: 'post' };
const middleware = createMiddleware( {
UPDATE: () => post,
} );
const store = createStoreWithMiddleware( middleware );
function* getPostAction() {
const nextState = yield { type: 'UPDATE' };
return { type: 'CHANGE', nextState };
}
store.dispatch( getPostAction() );
expect( store.getState() ).toEqual( post );
}
);
} );