-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: tokenized cart ECE base implementation
- Loading branch information
Showing
16 changed files
with
532 additions
and
704 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,5 @@ | ||
Significance: patch | ||
Type: update | ||
Comment: feat: tokenized cart ECE base implementation | ||
|
||
|
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
196 changes: 196 additions & 0 deletions
196
client/tokenized-express-checkout/__tests__/cart-api.test.js
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,196 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import apiFetch from '@wordpress/api-fetch'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import ExpressCheckoutCartApi from '../cart-api'; | ||
|
||
jest.mock( '@wordpress/api-fetch', () => jest.fn() ); | ||
|
||
global.wcpayExpressCheckoutParams = {}; | ||
global.wcpayExpressCheckoutParams.nonce = {}; | ||
global.wcpayExpressCheckoutParams.nonce.store_api_nonce = | ||
'global_store_api_nonce'; | ||
global.wcpayExpressCheckoutParams.nonce.tokenized_cart_nonce = | ||
'global_tokenized_cart_nonce'; | ||
global.wcpayExpressCheckoutParams.nonce.tokenized_cart_session_nonce = | ||
'global_tokenized_cart_session_nonce'; | ||
global.wcpayExpressCheckoutParams.checkout = {}; | ||
global.wcpayExpressCheckoutParams.checkout.currency_code = 'USD'; | ||
|
||
describe( 'ExpressCheckoutCartApi', () => { | ||
afterEach( () => { | ||
jest.resetAllMocks(); | ||
} ); | ||
|
||
it( 'should allow to create an anonymous cart for a specific class instance, without affecting other instances', async () => { | ||
global.wcpayExpressCheckoutParams.button_context = 'product'; | ||
const headers = new Headers(); | ||
headers.append( | ||
'X-WooPayments-Tokenized-Cart-Session', | ||
'tokenized_cart_session' | ||
); | ||
headers.append( 'Nonce', 'nonce-value' ); | ||
apiFetch.mockResolvedValue( { | ||
headers: headers, | ||
json: () => Promise.resolve( {} ), | ||
} ); | ||
|
||
const api = new ExpressCheckoutCartApi(); | ||
const anotherApi = new ExpressCheckoutCartApi(); | ||
|
||
api.useSeparateCart(); | ||
await api.getCart(); | ||
|
||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'GET', | ||
path: expect.stringContaining( '/wc/store/v1/cart' ), | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart-Session': '', | ||
'X-WooPayments-Tokenized-Cart-Session-Nonce': | ||
'global_tokenized_cart_session_nonce', | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
Nonce: 'global_store_api_nonce', | ||
} ), | ||
} ) | ||
); | ||
|
||
apiFetch.mockClear(); | ||
apiFetch.mockResolvedValue( { | ||
headers: new Headers(), | ||
json: () => Promise.resolve( {} ), | ||
} ); | ||
|
||
await api.updateCustomer( { | ||
billing_address: { first_name: 'First' }, | ||
} ); | ||
expect( apiFetch ).toHaveBeenLastCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: expect.stringContaining( | ||
'/wc/store/v1/cart/update-customer' | ||
), | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart': true, | ||
'X-WooPayments-Tokenized-Cart-Session-Nonce': | ||
'global_tokenized_cart_session_nonce', | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
'X-WooPayments-Tokenized-Cart-Session': | ||
'tokenized_cart_session', | ||
Nonce: 'nonce-value', | ||
} ), | ||
data: expect.objectContaining( { | ||
billing_address: { first_name: 'First' }, | ||
} ), | ||
} ) | ||
); | ||
|
||
apiFetch.mockClear(); | ||
apiFetch.mockResolvedValue( { | ||
headers: new Headers(), | ||
json: () => Promise.resolve( {} ), | ||
} ); | ||
await anotherApi.updateCustomer( { | ||
billing_address: { last_name: 'Last' }, | ||
} ); | ||
expect( apiFetch ).toHaveBeenLastCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: expect.stringContaining( | ||
'/wc/store/v1/cart/update-customer' | ||
), | ||
// in this case, no additional headers should have been submitted. | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart': true, | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
Nonce: 'global_store_api_nonce', | ||
} ), | ||
data: expect.objectContaining( { | ||
billing_address: { last_name: 'Last' }, | ||
} ), | ||
} ) | ||
); | ||
} ); | ||
|
||
it( 'should call `/cart/update-customer` with the global headers if the cart is not anonymous', async () => { | ||
global.wcpayExpressCheckoutParams.button_context = 'cart'; | ||
apiFetch.mockResolvedValue( { | ||
headers: new Headers(), | ||
json: () => Promise.resolve( {} ), | ||
} ); | ||
const api = new ExpressCheckoutCartApi(); | ||
|
||
await api.updateCustomer( { | ||
billing_address: { last_name: 'Last' }, | ||
} ); | ||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: expect.stringContaining( | ||
'/wc/store/v1/cart/update-customer' | ||
), | ||
// in this case, no additional headers should have been submitted. | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart': true, | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
} ), | ||
data: expect.objectContaining( { | ||
billing_address: { last_name: 'Last' }, | ||
} ), | ||
} ) | ||
); | ||
} ); | ||
|
||
it( 'should store received header information for subsequent usage', async () => { | ||
global.wcpayExpressCheckoutParams.button_context = 'cart'; | ||
const headers = new Headers(); | ||
headers.append( 'Nonce', 'nonce-value' ); | ||
apiFetch.mockResolvedValue( { | ||
headers, | ||
json: () => Promise.resolve( {} ), | ||
} ); | ||
const api = new ExpressCheckoutCartApi(); | ||
|
||
await api.getCart(); | ||
|
||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'GET', | ||
path: expect.stringContaining( '/wc/store/v1/cart' ), | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart-Session-Nonce': undefined, | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
} ), | ||
} ) | ||
); | ||
|
||
await api.updateCustomer( { | ||
billing_address: { last_name: 'Last' }, | ||
} ); | ||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: expect.stringContaining( | ||
'/wc/store/v1/cart/update-customer' | ||
), | ||
// in this case, no additional headers should have been submitted. | ||
headers: expect.objectContaining( { | ||
'X-WooPayments-Tokenized-Cart-Session-Nonce': undefined, | ||
'X-WooPayments-Tokenized-Cart': true, | ||
'X-WooPayments-Tokenized-Cart-Nonce': | ||
'global_tokenized_cart_nonce', | ||
Nonce: 'nonce-value', | ||
} ), | ||
} ) | ||
); | ||
} ); | ||
} ); |
128 changes: 128 additions & 0 deletions
128
client/tokenized-express-checkout/__tests__/order-api.js
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,128 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import apiFetch from '@wordpress/api-fetch'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import ExpressCheckoutOrderApi from '../order-api'; | ||
|
||
jest.mock( '@wordpress/api-fetch', () => jest.fn() ); | ||
|
||
global.wcpayExpressCheckoutParams = {}; | ||
global.wcpayExpressCheckoutParams.nonce = {}; | ||
global.wcpayExpressCheckoutParams.nonce.store_api_nonce = | ||
'global_store_api_nonce'; | ||
|
||
describe( 'ExpressCheckoutOrderApi', () => { | ||
afterEach( () => { | ||
jest.resetAllMocks(); | ||
} ); | ||
|
||
it( 'gets order data with the provided arguments', async () => { | ||
const api = new ExpressCheckoutOrderApi( { | ||
orderId: '1', | ||
key: 'key_123', | ||
billingEmail: '[email protected]', | ||
} ); | ||
|
||
await api.getCart(); | ||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'GET', | ||
path: expect.stringMatching( | ||
// I am using a regex to ensure the order of the parameters doesn't matter. | ||
/(?=.*\/wc\/store\/v1\/order\/1)(?=.*billing_email=cheese%40toast.com)(?=.*key=key_123)/ | ||
), | ||
} ) | ||
); | ||
} ); | ||
|
||
it( 'places an order', async () => { | ||
const api = new ExpressCheckoutOrderApi( { | ||
orderId: '1', | ||
key: 'key_123', | ||
billingEmail: '[email protected]', | ||
} ); | ||
|
||
await api.placeOrder( { | ||
billing_address: { | ||
first_name: 'Fake', | ||
}, | ||
shipping_address: { | ||
first_name: 'Test', | ||
}, | ||
anythingElse: 'passedThrough', | ||
} ); | ||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: '/wc/store/v1/checkout/1', | ||
headers: expect.objectContaining( { | ||
Nonce: 'global_store_api_nonce', | ||
} ), | ||
data: expect.objectContaining( { | ||
key: 'key_123', | ||
billing_email: '[email protected]', | ||
billing_address: undefined, | ||
shipping_address: undefined, | ||
anythingElse: 'passedThrough', | ||
} ), | ||
} ) | ||
); | ||
} ); | ||
|
||
it( 'places an order with the previous API request data', async () => { | ||
const api = new ExpressCheckoutOrderApi( { | ||
orderId: '1', | ||
key: 'key_123', | ||
billingEmail: '[email protected]', | ||
} ); | ||
|
||
apiFetch.mockResolvedValueOnce( { | ||
billing_address: { | ||
first_name: 'Fake', | ||
last_name: 'Test', | ||
}, | ||
shipping_address: { | ||
first_name: 'Test', | ||
last_name: 'Fake', | ||
}, | ||
} ); | ||
await api.getCart(); | ||
|
||
await api.placeOrder( { | ||
billing_address: { | ||
first_name: 'Fake', | ||
}, | ||
shipping_address: { | ||
first_name: 'Test', | ||
}, | ||
anythingElse: 'passedThrough', | ||
} ); | ||
|
||
expect( apiFetch ).toHaveBeenCalledWith( | ||
expect.objectContaining( { | ||
method: 'POST', | ||
path: '/wc/store/v1/checkout/1', | ||
headers: expect.objectContaining( { | ||
Nonce: 'global_store_api_nonce', | ||
} ), | ||
data: expect.objectContaining( { | ||
key: 'key_123', | ||
billing_email: '[email protected]', | ||
billing_address: expect.objectContaining( { | ||
first_name: 'Fake', | ||
last_name: 'Test', | ||
} ), | ||
shipping_address: expect.objectContaining( { | ||
first_name: 'Test', | ||
last_name: 'Fake', | ||
} ), | ||
anythingElse: 'passedThrough', | ||
} ), | ||
} ) | ||
); | ||
} ); | ||
} ); |
File renamed without changes.
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
Oops, something went wrong.