modify emitters so it handles non object or invalid object responses correctly. (https://github.com/woocommerce/woocommerce-blocks/pull/2249)
With these changes: - If an observer returns an object wthout a type property an error is thrown and the emitter is aborted with an error type response. - For anything else returned from an observer, it’s discarded. Tests are updated for the new expectations
This commit is contained in:
parent
a005649ab8
commit
493a826e44
|
@ -58,18 +58,29 @@ export const emitEvent = async ( observers, eventType, data ) => {
|
|||
*/
|
||||
export const emitEventWithAbort = async ( observers, eventType, data ) => {
|
||||
const observersByType = getObserversByPriority( observers, eventType );
|
||||
let emitterResponse = true;
|
||||
for ( const observer of observersByType ) {
|
||||
try {
|
||||
const response = await Promise.resolve( observer.callback( data ) );
|
||||
if ( response !== true ) {
|
||||
return response;
|
||||
if (
|
||||
typeof response === 'object' &&
|
||||
typeof response.type === 'undefined'
|
||||
) {
|
||||
throw new Error(
|
||||
'If you want to abort event emitter processing, your observer must return an object with a type property'
|
||||
);
|
||||
}
|
||||
emitterResponse = typeof response === 'object' ? response : true;
|
||||
if ( emitterResponse !== true ) {
|
||||
return emitterResponse;
|
||||
}
|
||||
} catch ( e ) {
|
||||
// we don't handle thrown errors but just console.log for
|
||||
// troubleshooting
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( e );
|
||||
return { type: 'error' };
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return emitterResponse;
|
||||
};
|
||||
|
|
|
@ -43,29 +43,61 @@ describe( 'Testing emitters', () => {
|
|||
} );
|
||||
} );
|
||||
describe( 'Testing emitEventWithAbort()', () => {
|
||||
it(
|
||||
'aborts on non truthy value and does not invoke remaining ' +
|
||||
'observers',
|
||||
async () => {
|
||||
const observers = { test: observerMocks };
|
||||
const response = await emitEventWithAbort(
|
||||
observers,
|
||||
'test',
|
||||
'foo'
|
||||
);
|
||||
expect( console ).not.toHaveErrored();
|
||||
expect( observerB ).toHaveBeenCalledTimes( 1 );
|
||||
expect(
|
||||
observerPromiseWithResolvedValue
|
||||
).not.toHaveBeenCalled();
|
||||
expect( response ).toBe( 10 );
|
||||
}
|
||||
);
|
||||
it( 'does not abort on any return value other than an object with a type property', async () => {
|
||||
observerMocks.delete( 'observerPromiseWithReject' );
|
||||
const observers = { test: observerMocks };
|
||||
const response = await emitEventWithAbort(
|
||||
observers,
|
||||
'test',
|
||||
'foo'
|
||||
);
|
||||
expect( console ).not.toHaveErrored();
|
||||
expect( observerB ).toHaveBeenCalledTimes( 1 );
|
||||
expect( observerPromiseWithResolvedValue ).toHaveBeenCalled();
|
||||
expect( response ).toBe( true );
|
||||
} );
|
||||
it( 'Aborts on a return value with an object that has a type property', async () => {
|
||||
const validObjectResponse = jest
|
||||
.fn()
|
||||
.mockReturnValue( { type: 'success' } );
|
||||
observerMocks.set( 'observerValidObject', {
|
||||
priority: 5,
|
||||
callback: validObjectResponse,
|
||||
} );
|
||||
observerMocks.delete( 'observerPromiseWithReject' );
|
||||
const observers = { test: observerMocks };
|
||||
const response = await emitEventWithAbort(
|
||||
observers,
|
||||
'test',
|
||||
'foo'
|
||||
);
|
||||
expect( console ).not.toHaveErrored();
|
||||
expect( validObjectResponse ).toHaveBeenCalledTimes( 1 );
|
||||
expect( observerPromiseWithResolvedValue ).not.toHaveBeenCalled();
|
||||
expect( response ).toEqual( { type: 'success' } );
|
||||
} );
|
||||
it( 'throws an error on an object returned from observer without a type property', async () => {
|
||||
const failingObjectResponse = jest.fn().mockReturnValue( {} );
|
||||
observerMocks.set( 'observerInvalidObject', {
|
||||
priority: 5,
|
||||
callback: failingObjectResponse,
|
||||
} );
|
||||
const observers = { test: observerMocks };
|
||||
const response = await emitEventWithAbort(
|
||||
observers,
|
||||
'test',
|
||||
'foo'
|
||||
);
|
||||
expect( console ).toHaveErrored();
|
||||
expect( failingObjectResponse ).toHaveBeenCalledTimes( 1 );
|
||||
expect( observerPromiseWithResolvedValue ).not.toHaveBeenCalled();
|
||||
expect( response ).toEqual( { type: 'error' } );
|
||||
} );
|
||||
} );
|
||||
describe( 'Test Priority', () => {
|
||||
it( 'executes observers in expected order by priority', async () => {
|
||||
const a = jest.fn();
|
||||
const b = jest.fn().mockReturnValue( false );
|
||||
const b = jest.fn().mockReturnValue( { type: 'error' } );
|
||||
const observers = {
|
||||
test: new Map( [
|
||||
[ 'observerA', { priority: 200, callback: a } ],
|
||||
|
|
Loading…
Reference in New Issue