Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PWA-998] Increase Test Coverage in peregrine/lib/talons/SignIn #2998

Merged
merged 2 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions packages/peregrine/lib/talons/SignIn/__tests__/useSignIn.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React from 'react';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook, act } from '@testing-library/react-hooks';

import { useCartContext } from '../../../context/cart';
import { useUserContext } from '../../../context/user';
import defaultOperations from '../signIn.gql';
import { useSignIn } from '../useSignIn';

jest.mock('../../../Apollo/clearCartDataFromCache');
jest.mock('../../../Apollo/clearCustomerDataFromCache');
jest.mock('../../../hooks/useAwaitQuery');
jest.mock('../../../store/actions/cart', () => ({
retrieveCartId: jest.fn().mockReturnValue('new-cart-id')
}));

jest.mock('../../../context/cart', () => ({
useCartContext: jest.fn().mockReturnValue([
{ cartId: 'old-cart-id' },
{
createCart: jest.fn(),
removeCart: jest.fn(),
getCartDetails: jest.fn()
}
])
}));

jest.mock('../../../context/user', () => ({
useUserContext: jest.fn().mockReturnValue([
{
isGettingDetails: false,
getDetailsError: 'getDetails error from redux'
},
{ getUserDetails: jest.fn(), setToken: jest.fn() }
])
}));

const signInVariables = {
email: 'fry@planetexpress.com',
password: 'slurm is the best'
};
const authToken = 'auth-token-123';

const signInMock = {
request: {
query: defaultOperations.signInMutation,
variables: signInVariables
},
result: {
data: { generateCustomerToken: { token: authToken } }
}
};

const mergeCartsMock = {
request: {
query: defaultOperations.mergeCartsMutation,
variables: {
destinationCartId: 'new-cart-id',
sourceCartId: 'old-cart-id'
}
},
result: {
data: null
}
};

const initialProps = {
getCartDetailsQuery: 'getCartDetailsQuery',
setDefaultUsername: jest.fn(),
showCreateAccount: jest.fn(),
showForgotPassword: jest.fn()
};

const renderHookWithProviders = ({
renderHookOptions = { initialProps },
mocks = [signInMock, mergeCartsMock]
} = {}) => {
const wrapper = ({ children }) => (
<MockedProvider mocks={mocks} addTypename={false}>
{children}
</MockedProvider>
);

return renderHook(useSignIn, { wrapper, ...renderHookOptions });
};

test('returns correct shape', () => {
const { result } = renderHookWithProviders();

expect(result.current).toMatchInlineSnapshot(`
Object {
"errors": Map {
"getUserDetailsQuery" => "getDetails error from redux",
"signInMutation" => undefined,
},
"handleCreateAccount": [Function],
"handleForgotPassword": [Function],
"handleSubmit": [Function],
"isBusy": false,
"setFormApi": [Function],
}
`);
});

test('handleSubmit triggers waterfall of operations and actions', async () => {
const [, { getCartDetails }] = useCartContext();
const [, { getUserDetails, setToken }] = useUserContext();

const { result } = renderHookWithProviders();

await act(() => result.current.handleSubmit(signInVariables));

expect(result.current.isBusy).toBe(true);
expect(setToken).toHaveBeenCalledWith(authToken);
expect(getCartDetails).toHaveBeenCalled();
expect(getUserDetails).toHaveBeenCalled();
Comment on lines +115 to +116
Copy link
Contributor Author

@tjwiebell tjwiebell Feb 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to avoid implementation details from bleeding into tests, but included these two checks since we're mocking both of these contexts. Ideally we wouldn't mock these contexts and include them in the helper renderHookWithProviders, but that proved to be a bit difficult with redux in place. I hope once redux is deprecated from these contexts, this will be easier going forward, and we can assert on context values, instead of checking that methods were just called.

});

test('handleSubmit exception is logged and resets state', async () => {
const errorMessage = 'Oh no! Something went wrong :(';
const [, { getUserDetails, setToken }] = useUserContext();
setToken.mockRejectedValue(errorMessage);
jest.spyOn(console, 'error');

const { result } = renderHookWithProviders();

await act(() => result.current.handleSubmit(signInVariables));

expect(result.current.isBusy).toBe(false);
expect(getUserDetails).not.toHaveBeenCalled();
expect(console.error).toHaveBeenCalledWith(errorMessage);
});

test('handleForgotPassword triggers callbacks', () => {
const mockUsername = 'fry@planetexpress.com';
const mockApi = {
getValue: jest.fn().mockReturnValue(mockUsername)
};

const { result } = renderHookWithProviders();
act(() => result.current.setFormApi(mockApi));
act(() => result.current.handleForgotPassword());

expect(initialProps.setDefaultUsername).toHaveBeenCalledWith(mockUsername);
expect(initialProps.showForgotPassword).toHaveBeenCalled();
});

test('handleCreateAccount triggers callbacks', () => {
const mockUsername = 'fry@planetexpress.com';
const mockApi = {
getValue: jest.fn().mockReturnValue(mockUsername)
};

const { result } = renderHookWithProviders();
act(() => result.current.setFormApi(mockApi));
act(() => result.current.handleCreateAccount());

expect(initialProps.setDefaultUsername).toHaveBeenCalledWith(mockUsername);
expect(initialProps.showCreateAccount).toHaveBeenCalled();
});

test('mutation error is returned by talon', async () => {
const signInErrorMock = {
request: signInMock.request,
error: new Error('Uh oh! There was an error signing in :(')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we seriously have a sad smiley in the error message 🤣 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

He why not is funny 😸

};

const { result } = renderHookWithProviders({ mocks: [signInErrorMock] });
await act(() => result.current.handleSubmit(signInVariables));

expect(result.current.errors.get('signInMutation')).toMatchInlineSnapshot(
`[Error: Uh oh! There was an error signing in :(]`
);
});
1 change: 1 addition & 0 deletions packages/peregrine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"devDependencies": {
"@magento/eslint-config": "~1.5.0",
"@testing-library/react-hooks": "~5.0.3",
"intl": "~1.2.5",
"intl-locales-supported": "~1.8.12",
"react": "~17.0.1",
Expand Down
51 changes: 51 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,18 @@
dependencies:
defer-to-connect "^1.0.1"

"@testing-library/react-hooks@~5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-5.0.3.tgz#dd0d2048817b013b266d35ca45e3ea48a19fd87e"
integrity sha512-UrnnRc5II7LMH14xsYNm/WRch/67cBafmrSQcyFh0v+UUmSf1uzfB7zn5jQXSettGwOSxJwdQUN7PgkT0w22Lg==
dependencies:
"@babel/runtime" "^7.12.5"
"@types/react" ">=16.9.0"
"@types/react-dom" ">=16.9.0"
"@types/react-test-renderer" ">=16.9.0"
filter-console "^0.1.1"
react-error-boundary "^3.1.0"

"@types/anymatch@*":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
Expand Down Expand Up @@ -3262,13 +3274,27 @@
"@types/react" "*"
"@types/reactcss" "*"

"@types/react-dom@>=16.9.0":
version "17.0.0"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a"
integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==
dependencies:
"@types/react" "*"

"@types/react-syntax-highlighter@11.0.4":
version "11.0.4"
resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd"
integrity sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg==
dependencies:
"@types/react" "*"

"@types/react-test-renderer@>=16.9.0":
version "17.0.0"
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.0.tgz#9be47b375eeb906fced37049e67284a438d56620"
integrity sha512-nvw+F81OmyzpyIE1S0xWpLonLUZCMewslPuA8BtjSKc5XEbn8zEQBXS7KuOLHTNnSOEM2Pum50gHOoZ62tqTRg==
dependencies:
"@types/react" "*"

"@types/react@*":
version "16.9.17"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.17.tgz#58f0cc0e9ec2425d1441dd7b623421a867aa253e"
Expand All @@ -3277,6 +3303,14 @@
"@types/prop-types" "*"
csstype "^2.2.0"

"@types/react@>=16.9.0":
version "17.0.1"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.1.tgz#eb1f1407dea8da3bc741879c1192aa703ab9975b"
integrity sha512-w8t9f53B2ei4jeOqf/gxtc2Sswnc3LBK5s0DyJcg5xd10tMHXts2N31cKjWfH9IC/JvEPa/YF1U4YeP1t4R6HQ==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"

"@types/reactcss@*":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@types/reactcss/-/reactcss-1.2.3.tgz#af28ae11bbb277978b99d04d1eedfd068ca71834"
Expand Down Expand Up @@ -6416,6 +6450,11 @@ csstype@^2.2.0, csstype@^2.5.7:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431"
integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==

csstype@^3.0.2:
version "3.0.6"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef"
integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==

csv-parse@~4.4.6:
version "4.4.7"
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.4.7.tgz#069de0875b92780ca74a018c9880ab41cb3517a1"
Expand Down Expand Up @@ -8198,6 +8237,11 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"

filter-console@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/filter-console/-/filter-console-0.1.1.tgz#6242be28982bba7415bcc6db74a79f4a294fa67c"
integrity sha512-zrXoV1Uaz52DqPs+qEwNJWJFAWZpYJ47UNmpN9q4j+/EYsz85uV0DC9k8tRND5kYmoVzL0W+Y75q4Rg8sRJCdg==

finalhandler@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
Expand Down Expand Up @@ -14322,6 +14366,13 @@ react-draggable@^4.0.3:
classnames "^2.2.5"
prop-types "^15.6.0"

react-error-boundary@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.0.tgz#9487443df2f9ba1db90d8ab52351814907ea4af3"
integrity sha512-lmPrdi5SLRJR+AeJkqdkGlW/CRkAUvZnETahK58J4xb5wpbfDngasEGu+w0T1iXEhVrYBJZeW+c4V1hILCnMWQ==
dependencies:
"@babel/runtime" "^7.12.5"

react-error-overlay@^6.0.7:
version "6.0.8"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de"
Expand Down