This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 221
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…8163) * Allow observers to set billingAddress by returning billingData This is required since we didn't correctly deprecate billingData when we changed the name to billingAddress * Add link to original PR * Set billingAddress when observer errors * Rename shippingData to shippingAddress * Add isBillingAddress and isShippingAddress type guards * Add tests for new type guards * Only set billing and shipping if they are valid objects * Add tests for __internalEmitPaymentProcessingEvent thunk * Update deprecated version * Return promise from this function to aid with testability * Add tests for shippingAddress and paymentMethodData * Ensure correct value is used to set shipping address * Move test data out of tests to aid with reusability * Improve success callback name * Add mocked __internalSetPaymentMethodData to correct object It was in registry, but should be in dispatch as the action is on the same store as the thunk. Registry is used for actions on other stores. * Add test for failed observers * Add test for mixed observers * Add comments explaining the destructure & deprecation
- Loading branch information
Showing
4 changed files
with
390 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import * as wpDataFunctions from '@wordpress/data'; | ||
import { EventObserversType } from '@woocommerce/base-context'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { PAYMENT_STORE_KEY } from '../index'; | ||
import { __internalEmitPaymentProcessingEvent } from '../thunks'; | ||
|
||
/** | ||
* If an observer returns billingAddress, shippingAddress, or paymentData, then the values of these | ||
* should be updated in the data stores. | ||
*/ | ||
const testShippingAddress = { | ||
first_name: 'test', | ||
last_name: 'test', | ||
company: 'test', | ||
address_1: 'test', | ||
address_2: 'test', | ||
city: 'test', | ||
state: 'test', | ||
postcode: 'test', | ||
country: 'test', | ||
phone: 'test', | ||
}; | ||
const testBillingAddress = { | ||
...testShippingAddress, | ||
email: 'test@test.com', | ||
}; | ||
const testPaymentMethodData = { | ||
payment_method: 'test', | ||
}; | ||
|
||
describe( 'wc/store/payment thunks', () => { | ||
const testPaymentProcessingCallback = jest.fn(); | ||
const testPaymentProcessingCallback2 = jest.fn(); | ||
const currentObservers: EventObserversType = { | ||
payment_processing: new Map(), | ||
}; | ||
currentObservers.payment_processing.set( 'test', { | ||
callback: testPaymentProcessingCallback, | ||
priority: 10, | ||
} ); | ||
currentObservers.payment_processing.set( 'test2', { | ||
callback: testPaymentProcessingCallback2, | ||
priority: 10, | ||
} ); | ||
|
||
describe( '__internalEmitPaymentProcessingEvent', () => { | ||
beforeEach( () => { | ||
jest.resetAllMocks(); | ||
} ); | ||
it( 'calls all registered observers', async () => { | ||
const { | ||
__internalEmitPaymentProcessingEvent: | ||
__internalEmitPaymentProcessingEventFromStore, | ||
} = wpDataFunctions.dispatch( PAYMENT_STORE_KEY ); | ||
await __internalEmitPaymentProcessingEventFromStore( | ||
currentObservers, | ||
jest.fn() | ||
); | ||
expect( testPaymentProcessingCallback ).toHaveBeenCalled(); | ||
expect( testPaymentProcessingCallback2 ).toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'sets metadata if successful observers return it', async () => { | ||
const testSuccessCallbackWithMetadata = jest.fn().mockReturnValue( { | ||
type: 'success', | ||
meta: { | ||
billingAddress: testBillingAddress, | ||
shippingAddress: testShippingAddress, | ||
paymentMethodData: testPaymentMethodData, | ||
}, | ||
} ); | ||
|
||
currentObservers.payment_processing.set( 'test3', { | ||
callback: testSuccessCallbackWithMetadata, | ||
priority: 10, | ||
} ); | ||
|
||
const setBillingAddressMock = jest.fn(); | ||
const setShippingAddressMock = jest.fn(); | ||
const setPaymentMethodDataMock = jest.fn(); | ||
const registryMock = { | ||
dispatch: jest.fn().mockImplementation( ( store: string ) => { | ||
return { | ||
...wpDataFunctions.dispatch( store ), | ||
setBillingAddress: setBillingAddressMock, | ||
setShippingAddress: setShippingAddressMock, | ||
}; | ||
} ), | ||
}; | ||
|
||
// Await here because the function returned by the __internalEmitPaymentProcessingEvent action creator | ||
// (a thunk) returns a Promise. | ||
await __internalEmitPaymentProcessingEvent( | ||
currentObservers, | ||
jest.fn() | ||
)( { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - it would be too much work to mock the entire registry, so we only mock dispatch on it, | ||
// which is all we need to test this thunk. | ||
registry: registryMock, | ||
dispatch: { | ||
...wpDataFunctions.dispatch( PAYMENT_STORE_KEY ), | ||
__internalSetPaymentMethodData: setPaymentMethodDataMock, | ||
}, | ||
} ); | ||
|
||
expect( setBillingAddressMock ).toHaveBeenCalledWith( | ||
testBillingAddress | ||
); | ||
expect( setShippingAddressMock ).toHaveBeenCalledWith( | ||
testShippingAddress | ||
); | ||
expect( setPaymentMethodDataMock ).toHaveBeenCalledWith( | ||
testPaymentMethodData | ||
); | ||
} ); | ||
it( 'sets metadata if failed observers return it', async () => { | ||
const testFailingCallbackWithMetadata = jest.fn().mockReturnValue( { | ||
type: 'failure', | ||
meta: { | ||
billingAddress: testBillingAddress, | ||
paymentMethodData: testPaymentMethodData, | ||
}, | ||
} ); | ||
|
||
currentObservers.payment_processing.set( 'test4', { | ||
callback: testFailingCallbackWithMetadata, | ||
priority: 10, | ||
} ); | ||
|
||
const setBillingAddressMock = jest.fn(); | ||
const setPaymentMethodDataMock = jest.fn(); | ||
const registryMock = { | ||
dispatch: jest.fn().mockImplementation( ( store: string ) => { | ||
return { | ||
...wpDataFunctions.dispatch( store ), | ||
setBillingAddress: setBillingAddressMock, | ||
}; | ||
} ), | ||
}; | ||
|
||
// Await here because the function returned by the __internalEmitPaymentProcessingEvent action creator | ||
// (a thunk) returns a Promise. | ||
await __internalEmitPaymentProcessingEvent( | ||
currentObservers, | ||
jest.fn() | ||
)( { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - it would be too much work to mock the entire registry, so we only mock dispatch on it, | ||
// which is all we need to test this thunk. | ||
registry: registryMock, | ||
dispatch: { | ||
...wpDataFunctions.dispatch( PAYMENT_STORE_KEY ), | ||
__internalSetPaymentMethodData: setPaymentMethodDataMock, | ||
}, | ||
} ); | ||
|
||
expect( setBillingAddressMock ).toHaveBeenCalledWith( | ||
testBillingAddress | ||
); | ||
expect( setPaymentMethodDataMock ).toHaveBeenCalledWith( | ||
testPaymentMethodData | ||
); | ||
} ); | ||
it( 'sets payment status to error if one observer is successful, but another errors', async () => { | ||
const testErrorCallbackWithMetadata = jest | ||
.fn() | ||
.mockImplementation( () => { | ||
return { | ||
type: 'error', | ||
}; | ||
} ); | ||
|
||
const testSuccessCallback = jest.fn().mockReturnValue( { | ||
type: 'success', | ||
} ); | ||
|
||
currentObservers.payment_processing.set( 'test5', { | ||
callback: testErrorCallbackWithMetadata, | ||
priority: 10, | ||
} ); | ||
currentObservers.payment_processing.set( 'test6', { | ||
callback: testSuccessCallback, | ||
priority: 9, | ||
} ); | ||
|
||
const setPaymentErrorMock = jest.fn(); | ||
const setPaymentSuccessMock = jest.fn(); | ||
const registryMock = { | ||
dispatch: jest | ||
.fn() | ||
.mockImplementation( wpDataFunctions.dispatch ), | ||
}; | ||
|
||
// Await here because the function returned by the __internalEmitPaymentProcessingEvent action creator | ||
// (a thunk) returns a Promise. | ||
await __internalEmitPaymentProcessingEvent( | ||
currentObservers, | ||
jest.fn() | ||
)( { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - it would be too much work to mock the entire registry, so we only mock dispatch on it, | ||
// which is all we need to test this thunk. | ||
registry: registryMock, | ||
dispatch: { | ||
...wpDataFunctions.dispatch( PAYMENT_STORE_KEY ), | ||
__internalSetPaymentError: setPaymentErrorMock, | ||
__internalSetPaymentSuccess: setPaymentSuccessMock, | ||
}, | ||
} ); | ||
|
||
// The observer throwing will cause this. | ||
//expect( console ).toHaveErroredWith( new Error( 'test error' ) ); | ||
expect( setPaymentErrorMock ).toHaveBeenCalled(); | ||
expect( setPaymentSuccessMock ).not.toHaveBeenCalled(); | ||
} ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import type { BillingAddress, ShippingAddress } from '@woocommerce/settings'; | ||
import { objectHasProp } from '@woocommerce/types'; | ||
|
||
export const isShippingAddress = ( | ||
address: unknown | ||
): address is ShippingAddress => { | ||
const keys = [ | ||
'first_name', | ||
'last_name', | ||
'company', | ||
'address_1', | ||
'address_2', | ||
'city', | ||
'state', | ||
'postcode', | ||
'country', | ||
'phone', | ||
]; | ||
return keys.every( ( key ) => objectHasProp( address, key ) ); | ||
}; | ||
export const isBillingAddress = ( | ||
address: unknown | ||
): address is BillingAddress => { | ||
return isShippingAddress( address ) && objectHasProp( address, 'email' ); | ||
}; |
Oops, something went wrong.