From cbd9e195c78136a41fd526941856c402f577dd39 Mon Sep 17 00:00:00 2001 From: Pedro Fernandes Date: Mon, 8 Jan 2024 16:05:41 +0000 Subject: [PATCH] feat: expose missing address endpoints in v1 feat: add missing address endpoints --- .../deleteDefaultBillingAddress.fixtures.js | 34 +++++++ .../deleteDefaultShippingAddress.fixtures.js | 42 ++++++++ .../deleteDefaultBillingAddress.test.js.snap | 12 +++ .../deleteDefaultShippingAddress.test.js.snap | 12 +++ .../deleteDefaultBillingAddress.test.js | 36 +++++++ .../deleteDefaultShippingAddress.test.js | 38 ++++++++ .../client/deleteDefaultBillingAddress.js | 26 +++++ .../client/deleteDefaultShippingAddress.js | 26 +++++ packages/core/src/addresses/client/index.js | 14 +-- .../addresses/redux/__tests__/reducer.test.js | 84 ++++++++++++++++ .../core/src/addresses/redux/actionTypes.js | 32 +++++++ ...doDeleteDefaultBillingAddress.test.js.snap | 11 +++ ...oDeleteDefaultShippingAddress.test.js.snap | 11 +++ .../doDeleteDefaultBillingAddress.test.js | 95 +++++++++++++++++++ .../doDeleteDefaultShippingAddress.test.js | 94 ++++++++++++++++++ .../actions/doDeleteDefaultBillingAddress.js | 63 ++++++++++++ .../actions/doDeleteDefaultShippingAddress.js | 63 ++++++++++++ .../core/src/addresses/redux/actions/index.js | 2 + packages/core/src/addresses/redux/reducer.js | 64 +++++++++++++ .../__snapshots__/reducer.test.js.snap | 2 + 20 files changed, 755 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/addresses/client/__fixtures__/deleteDefaultBillingAddress.fixtures.js create mode 100644 packages/core/src/addresses/client/__fixtures__/deleteDefaultShippingAddress.fixtures.js create mode 100644 packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultBillingAddress.test.js.snap create mode 100644 packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultShippingAddress.test.js.snap create mode 100644 packages/core/src/addresses/client/__tests__/deleteDefaultBillingAddress.test.js create mode 100644 packages/core/src/addresses/client/__tests__/deleteDefaultShippingAddress.test.js create mode 100644 packages/core/src/addresses/client/deleteDefaultBillingAddress.js create mode 100644 packages/core/src/addresses/client/deleteDefaultShippingAddress.js create mode 100644 packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultBillingAddress.test.js.snap create mode 100644 packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultShippingAddress.test.js.snap create mode 100644 packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultBillingAddress.test.js create mode 100644 packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultShippingAddress.test.js create mode 100644 packages/core/src/addresses/redux/actions/doDeleteDefaultBillingAddress.js create mode 100644 packages/core/src/addresses/redux/actions/doDeleteDefaultShippingAddress.js diff --git a/packages/core/src/addresses/client/__fixtures__/deleteDefaultBillingAddress.fixtures.js b/packages/core/src/addresses/client/__fixtures__/deleteDefaultBillingAddress.fixtures.js new file mode 100644 index 000000000..1a1c2f650 --- /dev/null +++ b/packages/core/src/addresses/client/__fixtures__/deleteDefaultBillingAddress.fixtures.js @@ -0,0 +1,34 @@ +import join from 'proper-url-join'; +import moxios from 'moxios'; + +export default { + success: params => { + moxios.stubRequest( + join('/api/account/v1/users', params.userId, 'addresses/billing/current'), + { + method: 'delete', + status: 204, + }, + ); + }, + failure: params => { + moxios.stubRequest( + join('/api/account/v1/users', params.userId, 'addresses/billing/current'), + { + method: 'delete', + response: { + errors: [ + { + code: 0, + message: 'error', + developerMessage: 'This is developer message', + moreInformation: 'Error more information', + exception: {}, + }, + ], + }, + status: 400, + }, + ); + }, +}; diff --git a/packages/core/src/addresses/client/__fixtures__/deleteDefaultShippingAddress.fixtures.js b/packages/core/src/addresses/client/__fixtures__/deleteDefaultShippingAddress.fixtures.js new file mode 100644 index 000000000..c17eac152 --- /dev/null +++ b/packages/core/src/addresses/client/__fixtures__/deleteDefaultShippingAddress.fixtures.js @@ -0,0 +1,42 @@ +import join from 'proper-url-join'; +import moxios from 'moxios'; + +export default { + success: params => { + moxios.stubRequest( + join( + '/api/account/v1/users', + params.userId, + 'addresses/shipping/current', + ), + { + method: 'delete', + status: 204, + }, + ); + }, + failure: params => { + moxios.stubRequest( + join( + '/api/account/v1/users', + params.userId, + 'addresses/shipping/current', + ), + { + method: 'delete', + response: { + errors: [ + { + code: 0, + message: 'error', + developerMessage: 'This is developer message', + moreInformation: 'Error more information', + exception: {}, + }, + ], + }, + status: 400, + }, + ); + }, +}; diff --git a/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultBillingAddress.test.js.snap b/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultBillingAddress.test.js.snap new file mode 100644 index 000000000..c8169a264 --- /dev/null +++ b/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultBillingAddress.test.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`deleteDefaultBillingAddress should receive a client request error 1`] = ` +Object { + "code": 0, + "developerMessage": "This is developer message", + "exception": Object {}, + "message": "error", + "moreInformation": "Error more information", + "status": 400, +} +`; diff --git a/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultShippingAddress.test.js.snap b/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultShippingAddress.test.js.snap new file mode 100644 index 000000000..2bab1b456 --- /dev/null +++ b/packages/core/src/addresses/client/__tests__/__snapshots__/deleteDefaultShippingAddress.test.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`deleteDefaultShippingAddress should receive a client request error 1`] = ` +Object { + "code": 0, + "developerMessage": "This is developer message", + "exception": Object {}, + "message": "error", + "moreInformation": "Error more information", + "status": 400, +} +`; diff --git a/packages/core/src/addresses/client/__tests__/deleteDefaultBillingAddress.test.js b/packages/core/src/addresses/client/__tests__/deleteDefaultBillingAddress.test.js new file mode 100644 index 000000000..e8cf7d750 --- /dev/null +++ b/packages/core/src/addresses/client/__tests__/deleteDefaultBillingAddress.test.js @@ -0,0 +1,36 @@ +import { deleteDefaultBillingAddress } from '../'; +import client from '../../../helpers/client'; +import fixture from '../__fixtures__/deleteDefaultBillingAddress.fixtures'; +import moxios from 'moxios'; + +describe('deleteDefaultBillingAddress', () => { + const userId = '123456'; + const expectedConfig = undefined; + const spy = jest.spyOn(client, 'delete'); + const expectedUrl = `/account/v1/users/${userId}/addresses/billing/current`; + + beforeEach(() => { + moxios.install(client); + jest.clearAllMocks(); + }); + + afterEach(() => moxios.uninstall(client)); + + it('should handle a client request successfully', async () => { + fixture.success({ userId }); + + expect.assertions(2); + + await expect(deleteDefaultBillingAddress(userId)).resolves.toBe(204); + expect(spy).toHaveBeenCalledWith(expectedUrl, expectedConfig); + }); + + it('should receive a client request error', async () => { + fixture.failure({ userId }); + + expect.assertions(2); + + await expect(deleteDefaultBillingAddress(userId)).rejects.toMatchSnapshot(); + expect(spy).toHaveBeenCalledWith(expectedUrl, expectedConfig); + }); +}); diff --git a/packages/core/src/addresses/client/__tests__/deleteDefaultShippingAddress.test.js b/packages/core/src/addresses/client/__tests__/deleteDefaultShippingAddress.test.js new file mode 100644 index 000000000..826a49c26 --- /dev/null +++ b/packages/core/src/addresses/client/__tests__/deleteDefaultShippingAddress.test.js @@ -0,0 +1,38 @@ +import { deleteDefaultShippingAddress } from '../'; +import client from '../../../helpers/client'; +import fixture from '../__fixtures__/deleteDefaultShippingAddress.fixtures'; +import moxios from 'moxios'; + +describe('deleteDefaultShippingAddress', () => { + const userId = '123456'; + const expectedConfig = undefined; + const spy = jest.spyOn(client, 'delete'); + const expectedUrl = `/account/v1/users/${userId}/addresses/shipping/current`; + + beforeEach(() => { + moxios.install(client); + jest.clearAllMocks(); + }); + + afterEach(() => moxios.uninstall(client)); + + it('should handle a client request successfully', async () => { + fixture.success({ userId }); + + expect.assertions(2); + + await expect(deleteDefaultShippingAddress(userId)).resolves.toBe(204); + expect(spy).toHaveBeenCalledWith(expectedUrl, expectedConfig); + }); + + it('should receive a client request error', async () => { + fixture.failure({ userId }); + + expect.assertions(2); + + await expect( + deleteDefaultShippingAddress(userId), + ).rejects.toMatchSnapshot(); + expect(spy).toHaveBeenCalledWith(expectedUrl, expectedConfig); + }); +}); diff --git a/packages/core/src/addresses/client/deleteDefaultBillingAddress.js b/packages/core/src/addresses/client/deleteDefaultBillingAddress.js new file mode 100644 index 000000000..484beaa14 --- /dev/null +++ b/packages/core/src/addresses/client/deleteDefaultBillingAddress.js @@ -0,0 +1,26 @@ +import client, { adaptError } from '../../helpers/client'; +import join from 'proper-url-join'; + +/** + * Responsible for unsetting the users default billing address. + * + * @function deleteDefaultBillingAddress + * @memberof module:addresses/client + * + * @param {string} userId - Identifier of the user. + * @param {object} [config] - Custom configurations to send to the client + * instance (axios). + * + * @returns {Promise} Promise that will resolve when the call to + * the endpoint finishes. + */ +export default (userId, config) => + client + .delete( + join('/account/v1/users', userId, 'addresses/billing/current'), + config, + ) + .then(response => response.status) + .catch(error => { + throw adaptError(error); + }); diff --git a/packages/core/src/addresses/client/deleteDefaultShippingAddress.js b/packages/core/src/addresses/client/deleteDefaultShippingAddress.js new file mode 100644 index 000000000..0f0a436e2 --- /dev/null +++ b/packages/core/src/addresses/client/deleteDefaultShippingAddress.js @@ -0,0 +1,26 @@ +import client, { adaptError } from '../../helpers/client'; +import join from 'proper-url-join'; + +/** + * Responsible for unsetting the users default shipping address. + * + * @function deleteDefaultShippingAddress + * @memberof module:addresses/client + * + * @param {string} userId - Identifier of the user. + * @param {object} [config] - Custom configurations to send to the client + * instance (axios). + * + * @returns {Promise} Promise that will resolve when the call to + * the endpoint finishes. + */ +export default (userId, config) => + client + .delete( + join('/account/v1/users', userId, 'addresses/shipping/current'), + config, + ) + .then(response => response.status) + .catch(error => { + throw adaptError(error); + }); diff --git a/packages/core/src/addresses/client/index.js b/packages/core/src/addresses/client/index.js index 77de6a250..29585824b 100644 --- a/packages/core/src/addresses/client/index.js +++ b/packages/core/src/addresses/client/index.js @@ -9,14 +9,16 @@ export { default as getPredictions } from './getPredictions'; export { default as getPredictionDetails } from './getPredictionDetails'; -export { default as getAddresses } from './getAddresses'; +export { default as deleteAddress } from './deleteAddress'; +export { default as deleteDefaultBillingAddress } from './deleteDefaultBillingAddress'; +export { default as deleteDefaultContactAddress } from './deleteDefaultContactAddress'; +export { default as deleteDefaultShippingAddress } from './deleteDefaultShippingAddress'; export { default as getAddress } from './getAddress'; +export { default as getAddresses } from './getAddresses'; +export { default as getDefaultContactAddress } from './getDefaultContactAddress'; +export { default as getSchema } from './getSchema'; export { default as postAddress } from './postAddress'; export { default as putAddress } from './putAddress'; -export { default as deleteAddress } from './deleteAddress'; export { default as putDefaultBillingAddress } from './putDefaultBillingAddress'; -export { default as putDefaultShippingAddress } from './putDefaultShippingAddress'; -export { default as getSchema } from './getSchema'; export { default as putDefaultContactAddress } from './putDefaultContactAddress'; -export { default as deleteDefaultContactAddress } from './deleteDefaultContactAddress'; -export { default as getDefaultContactAddress } from './getDefaultContactAddress'; +export { default as putDefaultShippingAddress } from './putDefaultShippingAddress'; diff --git a/packages/core/src/addresses/redux/__tests__/reducer.test.js b/packages/core/src/addresses/redux/__tests__/reducer.test.js index 72e25b98e..8d918287a 100644 --- a/packages/core/src/addresses/redux/__tests__/reducer.test.js +++ b/packages/core/src/addresses/redux/__tests__/reducer.test.js @@ -136,6 +136,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_FAILURE, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_FAILURE, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE, actionTypes.GET_DEFAULT_CONTACT_ADDRESS_FAILURE, ])('should handle %s action type', actionType => { const error = 'foo'; @@ -175,6 +177,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_REQUEST, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_REQUEST, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_REQUEST, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST, actionTypes.GET_DEFAULT_CONTACT_ADDRESS_REQUEST, ])('should handle %s action type', actionType => { expect( @@ -194,6 +198,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_SUCCESS, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_SUCCESS, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_SUCCESS, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, actionTypes.GET_DEFAULT_CONTACT_ADDRESS_SUCCESS, ])('should handle %s action type', actionType => { expect( @@ -215,6 +221,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_FAILURE, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_FAILURE, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE, actionTypes.GET_DEFAULT_CONTACT_ADDRESS_FAILURE, ])('should handle %s action type', actionType => { expect( @@ -770,6 +778,76 @@ describe('Addresses reducers', () => { ).toEqual(expectedResult); }); }); + + describe('delete default billing address', () => { + it('should handle DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS action type', () => { + const state = { + addresses: { + 1: { + id: 1, + address: 'data', + isCurrentBilling: true, + }, + }, + }; + + // Should unmark the previous default as the default address + const expectedResult = { + addresses: { + 1: { + id: 1, + address: 'data', + isCurrentBilling: false, + }, + }, + }; + + expect( + entitiesMapper[actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS]( + state, + { + meta: { addressId: 1, userId: '1' }, + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + }, + ), + ).toEqual(expectedResult); + }); + }); + + describe('delete default shipping address', () => { + it('should handle DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS action type', () => { + const state = { + addresses: { + 1: { + id: 1, + address: 'data', + isCurrentShipping: true, + }, + }, + }; + + // Should unmark the previous default as the default address + const expectedResult = { + addresses: { + 1: { + id: 1, + address: 'data', + isCurrentShipping: false, + }, + }, + }; + + expect( + entitiesMapper[actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS]( + state, + { + meta: { addressId: 1, userId: '1' }, + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, + }, + ), + ).toEqual(expectedResult); + }); + }); }); describe('address() reducer', () => { @@ -802,6 +880,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_REQUEST, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_REQUEST, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_REQUEST, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST, ])('should handle %s action type', actionType => { expect( reducer(undefined, { @@ -822,6 +902,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_FAILURE, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_FAILURE, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE, ])('should handle %s action type', actionType => { expect( reducer(undefined, { @@ -843,6 +925,8 @@ describe('Addresses reducers', () => { actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_SUCCESS, actionTypes.SET_DEFAULT_CONTACT_ADDRESS_SUCCESS, actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_SUCCESS, + actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, ])('should handle %s action type', actionType => { expect( reducer(undefined, { diff --git a/packages/core/src/addresses/redux/actionTypes.js b/packages/core/src/addresses/redux/actionTypes.js index 1ce546e50..087f3016d 100644 --- a/packages/core/src/addresses/redux/actionTypes.js +++ b/packages/core/src/addresses/redux/actionTypes.js @@ -137,5 +137,37 @@ export const GET_DEFAULT_CONTACT_ADDRESS_REQUEST = export const GET_DEFAULT_CONTACT_ADDRESS_SUCCESS = '@farfetch/blackout-core/GET_DEFAULT_CONTACT_ADDRESS_SUCCESS'; +/** + * Action type dispatched when delete default billing address request fails. + */ +export const DELETE_DEFAULT_BILLING_ADDRESS_FAILURE = + '@farfetch/blackout-core/DELETE_DEFAULT_BILLING_ADDRESS_FAILURE'; +/** + * Action type dispatched when delete default billing address request starts. + */ +export const DELETE_DEFAULT_BILLING_ADDRESS_REQUEST = + '@farfetch/blackout-core/DELETE_DEFAULT_BILLING_ADDRESS_REQUEST'; +/** + * Action type dispatched when delete default billing address request succeeds. + */ +export const DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS = + '@farfetch/blackout-core/DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS'; + +/** + * Action type dispatched when delete default shipping address request fails. + */ +export const DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE = + '@farfetch/blackout-core/DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE'; +/** + * Action type dispatched when delete default shipping address request starts. + */ +export const DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST = + '@farfetch/blackout-core/DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST'; +/** + * Action type dispatched when delete default shipping address request succeeds. + */ +export const DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS = + '@farfetch/blackout-core/DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS'; + /** Action type dispatched when reseting the addresses. */ export const RESET_ADDRESSES = '@farfetch/blackout-core/RESET_ADDRESSES'; diff --git a/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultBillingAddress.test.js.snap b/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultBillingAddress.test.js.snap new file mode 100644 index 000000000..dbe8e97c6 --- /dev/null +++ b/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultBillingAddress.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`doDeleteDefaultBillingAddress() action creator should create the correct actions for when the delete default billing address procedure is successful: delete default billing address success payload 1`] = ` +Object { + "meta": Object { + "addressId": "2222222", + "userId": "121212", + }, + "type": "@farfetch/blackout-core/DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS", +} +`; diff --git a/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultShippingAddress.test.js.snap b/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultShippingAddress.test.js.snap new file mode 100644 index 000000000..4abc9f914 --- /dev/null +++ b/packages/core/src/addresses/redux/actions/__tests__/__snapshots__/doDeleteDefaultShippingAddress.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`doDeleteDefaultShippingAddress() action creator should create the correct actions for when the delete default shipping address procedure is successful: delete default shipping address success payload 1`] = ` +Object { + "meta": Object { + "addressId": "2222222", + "userId": "121212", + }, + "type": "@farfetch/blackout-core/DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS", +} +`; diff --git a/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultBillingAddress.test.js b/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultBillingAddress.test.js new file mode 100644 index 000000000..6a707075f --- /dev/null +++ b/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultBillingAddress.test.js @@ -0,0 +1,95 @@ +import { addressId2 } from '../../__fixtures__/addresses.fixtures'; +import { mockStore } from '../../../../../tests'; +import doDeleteDefaultBillingAddress from '../doDeleteDefaultBillingAddress'; +import find from 'lodash/find'; +import reducer, { actionTypes } from '../../'; + +const addressesMockStore = (state = {}) => + mockStore( + { + addresses: reducer(), + entities: { + addresses: { + [addressId2]: { + id: addressId2, + isCurrentBilling: true, + }, + }, + }, + }, + state, + ); + +const expectedConfig = undefined; +let store; + +describe('doDeleteDefaultBillingAddress() action creator', () => { + const deleteDefaultBillingAddress = jest.fn(); + const userId = '121212'; + + beforeEach(() => { + jest.clearAllMocks(); + store = addressesMockStore(); + }); + + it('should create the correct actions for when the delete default billing address procedure fails', async () => { + const action = doDeleteDefaultBillingAddress(deleteDefaultBillingAddress); + const expectedError = new Error( + 'There is no current default billing address', + ); + deleteDefaultBillingAddress.mockRejectedValueOnce(expectedError); + expect.assertions(4); + + try { + await store.dispatch(action(userId, expectedConfig)); + } catch (error) { + expect(error).toStrictEqual(expectedError); + expect(deleteDefaultBillingAddress).toHaveBeenCalledTimes(1); + expect(deleteDefaultBillingAddress).toHaveBeenCalledWith( + userId, + expectedConfig, + ); + expect(store.getActions()).toEqual( + expect.arrayContaining([ + { + meta: { userId, addressId: addressId2 }, + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST, + }, + { + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE, + payload: { error: expectedError }, + meta: { userId, addressId: addressId2 }, + }, + ]), + ); + } + }); + + it('should create the correct actions for when the delete default billing address procedure is successful', async () => { + const action = doDeleteDefaultBillingAddress(deleteDefaultBillingAddress); + deleteDefaultBillingAddress.mockResolvedValueOnce({}); + await store.dispatch(action(userId, expectedConfig)); + + const actionResults = store.getActions(); + + expect.assertions(4); + expect(deleteDefaultBillingAddress).toHaveBeenCalledTimes(1); + expect(deleteDefaultBillingAddress).toHaveBeenCalledWith( + userId, + expectedConfig, + ); + expect(actionResults).toMatchObject([ + { + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST, + }, + { + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + }, + ]); + expect( + find(actionResults, { + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + }), + ).toMatchSnapshot('delete default billing address success payload'); + }); +}); diff --git a/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultShippingAddress.test.js b/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultShippingAddress.test.js new file mode 100644 index 000000000..1e32e7445 --- /dev/null +++ b/packages/core/src/addresses/redux/actions/__tests__/doDeleteDefaultShippingAddress.test.js @@ -0,0 +1,94 @@ +import { addressId2 } from '../../__fixtures__/addresses.fixtures'; +import { mockStore } from '../../../../../tests'; +import doDeleteDefaultShippingAddress from '../doDeleteDefaultShippingAddress'; +import find from 'lodash/find'; +import reducer, { actionTypes } from '../../'; + +const addressesMockStore = (state = {}) => + mockStore( + { + addresses: reducer(), + entities: { + addresses: { + [addressId2]: { + id: addressId2, + isCurrentShipping: true, + }, + }, + }, + }, + state, + ); + +const expectedConfig = undefined; +let store; + +describe('doDeleteDefaultShippingAddress() action creator', () => { + const deleteDefaultShippingAddress = jest.fn(); + const userId = '121212'; + + beforeEach(() => { + jest.clearAllMocks(); + store = addressesMockStore(); + }); + + it('should create the correct actions for when the delete default shipping address procedure fails', async () => { + const action = doDeleteDefaultShippingAddress(deleteDefaultShippingAddress); + const expectedError = new Error('delete default shipping address error'); + + deleteDefaultShippingAddress.mockRejectedValueOnce(expectedError); + expect.assertions(4); + + try { + await store.dispatch(action(userId, expectedConfig)); + } catch (error) { + expect(error).toBe(expectedError); + expect(deleteDefaultShippingAddress).toHaveBeenCalledTimes(1); + expect(deleteDefaultShippingAddress).toHaveBeenCalledWith( + userId, + expectedConfig, + ); + expect(store.getActions()).toEqual( + expect.arrayContaining([ + { + meta: { userId, addressId: addressId2 }, + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST, + }, + { + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE, + payload: { error: expectedError }, + meta: { userId, addressId: addressId2 }, + }, + ]), + ); + } + }); + + it('should create the correct actions for when the delete default shipping address procedure is successful', async () => { + const action = doDeleteDefaultShippingAddress(deleteDefaultShippingAddress); + deleteDefaultShippingAddress.mockResolvedValueOnce({}); + await store.dispatch(action(userId, expectedConfig)); + + const actionResults = store.getActions(); + + expect.assertions(4); + expect(deleteDefaultShippingAddress).toHaveBeenCalledTimes(1); + expect(deleteDefaultShippingAddress).toHaveBeenCalledWith( + userId, + expectedConfig, + ); + expect(actionResults).toMatchObject([ + { + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST, + }, + { + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, + }, + ]); + expect( + find(actionResults, { + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, + }), + ).toMatchSnapshot('delete default shipping address success payload'); + }); +}); diff --git a/packages/core/src/addresses/redux/actions/doDeleteDefaultBillingAddress.js b/packages/core/src/addresses/redux/actions/doDeleteDefaultBillingAddress.js new file mode 100644 index 000000000..cbdd1b13f --- /dev/null +++ b/packages/core/src/addresses/redux/actions/doDeleteDefaultBillingAddress.js @@ -0,0 +1,63 @@ +import * as actionTypes from '../actionTypes.js'; +import { getDefaultAddress } from '../reducer.js'; + +/** + * @callback DeleteDefaultBillingAddressThunkFactory + * @param {string} userId - Identifier of the user. + * @param {object} [config] - Custom configurations to send to the client + * instance (axios). + * + * @returns {Function} Thunk to be dispatched to the redux store. + */ + +/** + * Responsible for deleting the users default billing address. + * + * @function doDeleteDefaultBillingAddress + * @memberof module:addresses/actions + * + * @param {Function} deleteDefaultBillingAddress - Delete default billing address client. + * + * @returns {DeleteDefaultBillingAddressThunkFactory} Thunk factory. + */ + +export default deleteDefaultBillingAddress => + (userId, config) => + async (dispatch, getState) => { + let addressId; + + try { + addressId = getDefaultAddress( + getState()?.entities?.addresses, + 'isCurrentBilling', + )?.id; + + if (!addressId) { + throw new Error('There is no current default billing address'); + } + + dispatch({ + meta: { userId, addressId }, + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST, + }); + + const result = await deleteDefaultBillingAddress(userId, config); + + dispatch({ + meta: { userId, addressId }, + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS, + }); + + return result; + } catch (error) { + if (addressId) { + dispatch({ + meta: { userId, addressId }, + payload: { error }, + type: actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE, + }); + } + + throw error; + } + }; diff --git a/packages/core/src/addresses/redux/actions/doDeleteDefaultShippingAddress.js b/packages/core/src/addresses/redux/actions/doDeleteDefaultShippingAddress.js new file mode 100644 index 000000000..5e85dcfea --- /dev/null +++ b/packages/core/src/addresses/redux/actions/doDeleteDefaultShippingAddress.js @@ -0,0 +1,63 @@ +import * as actionTypes from '../actionTypes.js'; +import { getDefaultAddress } from '../reducer.js'; + +/** + * @callback DeleteDefaultShippingAddressThunkFactory + * @param {string} userId - Identifier of the user. + * @param {object} [config] - Custom configurations to send to the client + * instance (axios). + * + * @returns {Function} Thunk to be dispatched to the redux store. + */ + +/** + * Responsible for deleting the users default shipping address. + * + * @function doDeleteDefaultShippingAddress + * @memberof module:addresses/actions + * + * @param {Function} deleteDefaultShippingAddress - Delete default shipping address client. + * + * @returns {DeleteDefaultShippingAddressThunkFactory} Thunk factory. + */ + +export default deleteDefaultShippingAddress => + (userId, config) => + async (dispatch, getState) => { + let addressId; + + try { + addressId = getDefaultAddress( + getState()?.entities?.addresses, + 'isCurrentShipping', + )?.id; + + if (!addressId) { + throw new Error('There is no current default shipping address'); + } + + dispatch({ + meta: { userId, addressId }, + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST, + }); + + const result = await deleteDefaultShippingAddress(userId, config); + + dispatch({ + meta: { userId, addressId }, + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS, + }); + + return result; + } catch (error) { + if (addressId) { + dispatch({ + meta: { userId, addressId }, + payload: { error }, + type: actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE, + }); + } + + throw error; + } + }; diff --git a/packages/core/src/addresses/redux/actions/index.js b/packages/core/src/addresses/redux/actions/index.js index 3cbfbfb6f..2bdeb09ca 100644 --- a/packages/core/src/addresses/redux/actions/index.js +++ b/packages/core/src/addresses/redux/actions/index.js @@ -19,5 +19,7 @@ export { default as doSetDefaultShippingAddress } from './doSetDefaultShippingAd export { default as doGetAddressSchema } from './doGetAddressSchema'; export { default as doSetDefaultContactAddress } from './doSetDefaultContactAddress'; export { default as doDeleteDefaultContactAddress } from './doDeleteDefaultContactAddress'; +export { default as doDeleteDefaultBillingAddress } from './doDeleteDefaultBillingAddress'; +export { default as doDeleteDefaultShippingAddress } from './doDeleteDefaultShippingAddress'; export { default as doGetDefaultContactAddress } from './doGetDefaultContactAddress'; export { default as reset } from './reset'; diff --git a/packages/core/src/addresses/redux/reducer.js b/packages/core/src/addresses/redux/reducer.js index f28a74b70..62d0ffe74 100644 --- a/packages/core/src/addresses/redux/reducer.js +++ b/packages/core/src/addresses/redux/reducer.js @@ -83,6 +83,8 @@ const error = (state = INITIAL_STATE.error, action = {}) => { case actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_FAILURE: case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_FAILURE: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE: case actionTypes.GET_DEFAULT_CONTACT_ADDRESS_FAILURE: return action.payload.error; case actionTypes.GET_PREDICTION_REQUEST: @@ -96,6 +98,8 @@ const error = (state = INITIAL_STATE.error, action = {}) => { case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_REQUEST: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_REQUEST: case actionTypes.GET_DEFAULT_CONTACT_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST: return INITIAL_STATE.error; default: return state; @@ -114,6 +118,8 @@ const isLoading = (state = INITIAL_STATE.isLoading, action = {}) => { case actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_REQUEST: case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_REQUEST: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST: case actionTypes.GET_DEFAULT_CONTACT_ADDRESS_REQUEST: return true; case actionTypes.GET_PREDICTION_FAILURE: @@ -136,6 +142,10 @@ const isLoading = (state = INITIAL_STATE.isLoading, action = {}) => { case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_SUCCESS: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_SUCCESS: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS: case actionTypes.GET_DEFAULT_CONTACT_ADDRESS_FAILURE: case actionTypes.GET_DEFAULT_CONTACT_ADDRESS_SUCCESS: return INITIAL_STATE.isLoading; @@ -298,6 +308,54 @@ export const entitiesMapper = { draftState.addresses[addressId].isPreferredAddress = false; }); }, + [actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS]: (state, action) => { + const { addressId } = action.meta; + + return produce(state, draftState => { + if (!draftState) { + return draftState; + } + + const addresses = draftState.addresses; + + if (!addresses) { + return draftState; + } + + // Unmark the selected address as default + const defaultBillingAddress = addresses[addressId]; + + if (defaultBillingAddress) { + defaultBillingAddress.isCurrentBilling = false; + } + + return draftState; + }); + }, + [actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS]: (state, action) => { + const { addressId } = action.meta; + + return produce(state, draftState => { + if (!draftState) { + return draftState; + } + + const addresses = draftState.addresses; + + if (!addresses) { + return draftState; + } + + // Unmark the selected address as default + const defaultShippingAddress = addresses[addressId]; + + if (defaultShippingAddress) { + defaultShippingAddress.isCurrentShipping = false; + } + + return draftState; + }); + }, }; export const address = (state = INITIAL_STATE.address, action = {}) => { @@ -314,6 +372,8 @@ export const address = (state = INITIAL_STATE.address, action = {}) => { case actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_REQUEST: case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_REQUEST: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_REQUEST: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_REQUEST: return { isLoading: { ...state.isLoading, @@ -328,6 +388,8 @@ export const address = (state = INITIAL_STATE.address, action = {}) => { case actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_SUCCESS: case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_SUCCESS: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_SUCCESS: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS: return { ...state, isLoading: { @@ -342,6 +404,8 @@ export const address = (state = INITIAL_STATE.address, action = {}) => { case actionTypes.SET_DEFAULT_SHIPPING_ADDRESS_FAILURE: case actionTypes.SET_DEFAULT_CONTACT_ADDRESS_FAILURE: case actionTypes.DELETE_DEFAULT_CONTACT_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_BILLING_ADDRESS_FAILURE: + case actionTypes.DELETE_DEFAULT_SHIPPING_ADDRESS_FAILURE: return { ...state, isLoading: { diff --git a/packages/core/src/entities/redux/__tests__/__snapshots__/reducer.test.js.snap b/packages/core/src/entities/redux/__tests__/__snapshots__/reducer.test.js.snap index 71d0f6f18..305225bcd 100644 --- a/packages/core/src/entities/redux/__tests__/__snapshots__/reducer.test.js.snap +++ b/packages/core/src/entities/redux/__tests__/__snapshots__/reducer.test.js.snap @@ -5,7 +5,9 @@ Object { "addresses": Object { "@farfetch/blackout-core/CREATE_ADDRESS_SUCCESS": [Function], "@farfetch/blackout-core/DELETE_ADDRESS_SUCCESS": [Function], + "@farfetch/blackout-core/DELETE_DEFAULT_BILLING_ADDRESS_SUCCESS": [Function], "@farfetch/blackout-core/DELETE_DEFAULT_CONTACT_ADDRESS_SUCCESS": [Function], + "@farfetch/blackout-core/DELETE_DEFAULT_SHIPPING_ADDRESS_SUCCESS": [Function], "@farfetch/blackout-core/GET_ADDRESS_SCHEMA_SUCCESS": [Function], "@farfetch/blackout-core/SET_DEFAULT_BILLING_ADDRESS_SUCCESS": [Function], "@farfetch/blackout-core/SET_DEFAULT_CONTACT_ADDRESS_SUCCESS": [Function],