Skip to content

Commit

Permalink
Merge branch 'develop' into PWA-652
Browse files Browse the repository at this point in the history
  • Loading branch information
dpatil-magento authored Jun 12, 2020
2 parents c63ee35 + 72d2705 commit 112ebca
Show file tree
Hide file tree
Showing 41 changed files with 1,788 additions and 647 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"resolutions": {
"graphql": "~14.3.1",
"**/graphql-cli/npm-run": "~5.0.0",
"**/graphql-cli/graphql-playground-middleware-express": "~1.7.18",
"https-proxy-agent": "~2.2.3"
},
"engines": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,16 @@ import React, { useEffect } from 'react';
import { createTestInstance } from '@magento/peregrine';

import { useGiftCards } from '../useGiftCards';
import { act } from 'react-test-renderer';

import { useLazyQuery, useMutation } from '@apollo/react-hooks';

/*
* Mocked Modules.
*/
jest.mock('@apollo/react-hooks', () => {
const deferredFn = jest.fn();

const cartResult = {
data: {
cart: {
applied_gift_cards: [
{ code: 'unit test card 1' },
{ code: 'unit test card 2' }
]
}
},
error: null,
loading: false
};
const balanceResult = {
data: {
giftCardAccount: {
balance: {
currency: 'USD',
value: '100'
},
code: 'unit test'
}
},
error: null,
loading: false
};
const applyCardResult = {
data: null,
error: null,
loading: false
};
const removeCardResult = {
data: null,
error: null,
loading: false
};

const useLazyQuery = jest.fn(input => {
if (input === 'mock cart') return [deferredFn, cartResult];
return [deferredFn, balanceResult];
});
const useMutation = jest.fn(input => {
if (input === 'mock apply') return [deferredFn, applyCardResult];
return [deferredFn, removeCardResult];
});
const useLazyQuery = jest.fn();
const useMutation = jest.fn();

return { useLazyQuery, useMutation };
});
Expand All @@ -70,7 +29,6 @@ jest.mock('@magento/peregrine/lib/context/cart', () => {
/*
* Member variables.
*/

const log = jest.fn();
const Component = props => {
const talonProps = useGiftCards({ ...props });
Expand All @@ -79,7 +37,45 @@ const Component = props => {
log(talonProps);
}, [talonProps]);

return null;
return <div {...talonProps} id={'giftCard'} />;
};

const deferredFn = jest.fn();

const cartResult = {
data: {
cart: {
applied_gift_cards: [
{ code: 'unit test card 1' },
{ code: 'unit test card 2' }
]
}
},
error: null,
loading: false
};
const balanceResult = {
data: {
giftCardAccount: {
balance: {
currency: 'USD',
value: '100'
},
code: 'unit test'
}
},
error: null,
loading: false
};
const applyCardResult = {
data: null,
error: null,
loading: false
};
const removeCardResult = {
data: null,
error: null,
loading: false
};

const props = {
Expand All @@ -97,8 +93,17 @@ const props = {
/*
* Tests.
*/

test('it returns the proper shape', () => {
useLazyQuery.mockImplementation(input => {
if (input === 'mock cart') return [deferredFn, cartResult];
return [deferredFn, balanceResult];
});

useMutation.mockImplementation(input => {
if (input === 'mock apply') return [deferredFn, applyCardResult];
return [deferredFn, removeCardResult];
});

// Act.
createTestInstance(<Component {...props} />);

Expand All @@ -117,7 +122,54 @@ test('it returns the proper shape', () => {
removeGiftCard: expect.any(Function),
setFormApi: expect.any(Function),
shouldDisplayCardBalance: expect.any(Boolean),
shouldDisplayCardError: expect.any(Boolean),
submitForm: expect.any(Function)
shouldDisplayCardError: expect.any(Boolean)
});
});

test('returns error message with invalid request', () => {
applyCardResult.error = true;

useLazyQuery.mockImplementation(input => {
if (input === 'mock cart') return [deferredFn, cartResult];
return [deferredFn, balanceResult];
});

useMutation.mockImplementation(input => {
if (input === 'mock apply') return [deferredFn, applyCardResult];
return [deferredFn, removeCardResult];
});

// First mount the component so we can pass in a mocked formApi
const component = createTestInstance(<Component {...props} />);
let talonProps = component.root.findByProps({ id: 'giftCard' }).props;

// Mock formApi so the talon can retrieve data
const { setFormApi } = talonProps;
const formApi = {
setValue: jest.fn(),
getValue: jest.fn(),
reset: jest.fn()
};

// Set the formApi and update the component so it has access to the form API
act(() => {
setFormApi(formApi);
component.update(<Component {...props} />);
});

// Retrieve the new talon props, which will have access form api
talonProps = component.root.findByProps({ id: 'giftCard' }).props;

// Call apply gift card, which now has access to the form api
act(() => {
talonProps.applyGiftCard();
});

// Verify the 3 re-render will have the shouldDisplayCardError set to true.
expect(log).toHaveBeenNthCalledWith(
3,
expect.objectContaining({
shouldDisplayCardError: true
})
);
});
113 changes: 37 additions & 76 deletions packages/peregrine/lib/talons/CartPage/GiftCards/useGiftCards.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const actions = {
* @returns {Function} result.removeGiftCard - A callback to remove a gift card from the cart.
* @returns {Boolean} result.shouldDisplayCardBalance - Whether to display the gift card balance to the user
* @returns {Boolean} result.shouldDisplayCardError - Whether to display an error message under the card input field.
* @returns {Function} result.submitForm - Submits the form to apply or check balance of the supplied gift card code.
*/
export const useGiftCards = props => {
const {
Expand Down Expand Up @@ -79,49 +78,42 @@ export const useGiftCards = props => {
}
}, [cartId, getAppliedCards]);

// Submit the form after the apply or check balance actions are taken.
useEffect(() => {
if (
mostRecentAction === actions.APPLY ||
mostRecentAction === actions.CHECK_BALANCE
) {
if (formApi) {
formApi.submitForm();
}
}
}, [formApi, mostRecentAction]);

/*
* useCallback hooks.
*/
const applyGiftCard = useCallback(() => {
// Ensure the mostRecentAction is APPLY before submitting.
if (mostRecentAction === actions.APPLY) {
if (formApi) {
formApi.submitForm();
const applyGiftCard = useCallback(async () => {
setMostRecentAction(actions.APPLY);

const giftCardCode = formApi.getValue('card');

await applyCard({
variables: {
cartId,
giftCardCode
}
} else {
// A useEffect will take care of submitting once this async
// operation finishes.
setMostRecentAction(actions.APPLY);
}
}, [formApi, mostRecentAction]);
});

// Clear the input form after successful apply.
formApi.reset();
}, [formApi, applyCard, cartId]);

const checkGiftCardBalance = useCallback(() => {
// Ensure the mostRecentAction is CHECK_BALANCE before submitting.
if (mostRecentAction === actions.CHECK_BALANCE) {
if (formApi) {
formApi.submitForm();
}
} else {
// A useEffect will take care of submitting once this async
// operation finishes.
setMostRecentAction(actions.CHECK_BALANCE);
}
}, [formApi, mostRecentAction]);
setMostRecentAction(actions.CHECK_BALANCE);

const giftCardCode = formApi.getValue('card');

checkCardBalance({
// Don't cache this one because the card can be used elsewhere
// before it is used again here.
fetchPolicy: 'no-cache',
variables: { giftCardCode }
});
}, [formApi, checkCardBalance]);

const removeGiftCard = useCallback(
async giftCardCode => {
setMostRecentAction(actions.REMOVE);

try {
await removeCard({
variables: {
Expand All @@ -131,50 +123,11 @@ export const useGiftCards = props => {
});
} catch (err) {
// do nothing
} finally {
setMostRecentAction(actions.REMOVE);
}
},
[cartId, removeCard]
);

const submitForm = useCallback(
async values => {
const giftCardCode = values['card'];

if (mostRecentAction === actions.APPLY) {
await applyCard({
variables: {
cartId,
giftCardCode
}
});

// Clear the input form after successful apply.
formApi.reset();
}

if (mostRecentAction === actions.CHECK_BALANCE) {
checkCardBalance({
// Don't cache this one because the card can be used elsewhere
// before it is used again here.
fetchPolicy: 'no-cache',
variables: { giftCardCode }
});
}
},
[applyCard, cartId, checkCardBalance, formApi, mostRecentAction]
);

const errorApplyingCard = Boolean(applyCardResult.error);
const errorCheckingBalance = Boolean(balanceResult.error);
const shouldDisplayCardBalance =
mostRecentAction === actions.CHECK_BALANCE &&
Boolean(balanceResult.data);
const shouldDisplayCardError =
(errorApplyingCard && mostRecentAction === actions.APPLY) ||
(errorCheckingBalance && mostRecentAction === actions.CHECK_BALANCE);

const {
called: applyCardCalled,
loading: applyCardLoading
Expand All @@ -197,6 +150,15 @@ export const useGiftCards = props => {
setIsCartUpdating
]);

const shouldDisplayCardBalance =
mostRecentAction === actions.CHECK_BALANCE &&
Boolean(balanceResult.data);

// We should only display the last card error if the most recent action was apply or check and they had an error
const shouldDisplayCardError =
(mostRecentAction === actions.APPLY && applyCardResult.error) ||
(mostRecentAction === actions.CHECK_BALANCE && balanceResult.error);

return {
applyGiftCard,
checkBalanceData:
Expand All @@ -215,7 +177,6 @@ export const useGiftCards = props => {
removeGiftCard,
setFormApi,
shouldDisplayCardBalance,
shouldDisplayCardError,
submitForm
shouldDisplayCardError
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,10 @@ jest.mock('@apollo/react-hooks', () => {

return {
...jest.requireActual('@apollo/react-hooks'),
useLazyQuery: jest.fn().mockReturnValue([
jest.fn(),
{
data: getSelectedAndAvailableShippingMethodsResult,
loading: false
}
]),
useQuery: jest.fn().mockReturnValue({
data: getSelectedAndAvailableShippingMethodsResult,
loading: false
}),
useMutation: jest.fn().mockReturnValue([
jest.fn(),
{
Expand Down
Loading

0 comments on commit 112ebca

Please # to comment.