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

Set cart page is loading while fetching data from network #2454

Merged
merged 6 commits into from
Jun 16, 2020
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
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { createTestInstance } from '@magento/peregrine';
import { useQuery } from '@apollo/react-hooks';

import { useCartPage } from '../useCartPage';
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure how granular can we get with testing but I feel we have to add tests around conditionals and effects. For instance, check if isCartUpdating is being updated on loading change.


jest.mock('react', () => {
const React = jest.requireActual('react');
const spy = jest.spyOn(React, 'useState');

return {
...React,
useState: spy
};
});

jest.mock('@apollo/react-hooks', () => {
const runQuery = jest.fn();
const queryResult = {
called: false,
data: null,
error: null,
loading: false
};
const useLazyQuery = jest.fn(() => [runQuery, queryResult]);
const useQuery = jest.fn(() => queryResult);

return { useLazyQuery };
return { useQuery };
});

jest.mock('@magento/peregrine/lib/context/app', () => {
Expand Down Expand Up @@ -67,6 +77,43 @@ test('it returns the proper shape', () => {
handleSignIn: expect.any(Function),
isCartUpdating: expect.any(Boolean),
isSignedIn: expect.any(Boolean),
setIsCartUpdating: expect.any(Function)
setIsCartUpdating: expect.any(Function),
shouldShowLoadingIndicator: expect.any(Boolean)
});
});

test('it calls setIsCartUpdating true when loading is true', () => {
// Arrange.
useQuery.mockReturnValueOnce({
called: true,
data: { cart: { total_quantity: 0 } },
loading: true
});
// isCartUpdating
useState.mockReturnValueOnce([false, jest.fn()]);

// Act.
createTestInstance(<Component />);

// Assert.
const { setIsCartUpdating } = log.mock.calls[0][0];
expect(setIsCartUpdating).toBeCalledWith(true);
});

test('it calls setIsCartUpdating false when loading is false', () => {
// Arrange.
useQuery.mockReturnValueOnce({
called: true,
data: { cart: { total_quantity: 0 } },
loading: false
});
// isCartUpdating
useState.mockReturnValueOnce([false, jest.fn()]);

// Act.
createTestInstance(<Component />);

// Assert.
const { setIsCartUpdating } = log.mock.calls[0][0];
expect(setIsCartUpdating).toBeCalledWith(false);
});
30 changes: 15 additions & 15 deletions packages/peregrine/lib/talons/CartPage/useCartPage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { useLazyQuery } from '@apollo/react-hooks';
import { useQuery } from '@apollo/react-hooks';

import { useAppContext } from '@magento/peregrine/lib/context/app';
import { useUserContext } from '@magento/peregrine/lib/context/user';
Expand All @@ -16,10 +16,11 @@ export const useCartPage = props => {

const [isCartUpdating, setIsCartUpdating] = useState(false);

const [fetchCartData, { data }] = useLazyQuery(getCartDetails, {
// TODO: Purposely overfetch and hit the network until all components
// are correctly updating the cache. Will be fixed by PWA-321.
fetchPolicy: 'cache-and-network'
const { called, data, loading } = useQuery(getCartDetails, {
fetchPolicy: 'cache-and-network',
tjwiebell marked this conversation as resolved.
Show resolved Hide resolved
// Don't make this call if we don't have a cartId
skip: !cartId,
variables: { cartId }
});

const handleSignIn = useCallback(() => {
Expand All @@ -28,20 +29,19 @@ export const useCartPage = props => {
}, [toggleDrawer]);

useEffect(() => {
if (cartId) {
fetchCartData({
variables: {
cartId
}
});
}
}, [cartId, fetchCartData]);
// Let the cart page know it is updating while we're waiting on network data.
setIsCartUpdating(loading);
}, [loading]);

const hasItems = !!(data && data.cart.total_quantity);
const shouldShowLoadingIndicator = called && loading && !hasItems;

return {
hasItems: !!(data && data.cart.total_quantity),
hasItems,
handleSignIn,
isSignedIn,
isCartUpdating,
setIsCartUpdating
setIsCartUpdating,
shouldShowLoadingIndicator
};
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,83 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders a loading indicator when talon indicates 1`] = `
<div>
<span>
<svg
fill="none"
height={64}
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width={64}
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="12"
x2="12"
y1="2"
y2="6"
/>
<line
x1="12"
x2="12"
y1="18"
y2="22"
/>
<line
x1="4.93"
x2="7.76"
y1="4.93"
y2="7.76"
/>
<line
x1="16.24"
x2="19.07"
y1="16.24"
y2="19.07"
/>
<line
x1="2"
x2="6"
y1="12"
y2="12"
/>
<line
x1="18"
x2="22"
y1="12"
y2="12"
/>
<line
x1="4.93"
x2="7.76"
y1="19.07"
y2="16.24"
/>
<line
x1="16.24"
x2="19.07"
y1="7.76"
y2="4.93"
/>
</svg>
</span>
<span>
Fetching Data...
</span>
</div>
`;

exports[`renders components if cart has items 1`] = `
<div>
<div>
<h1>
Cart
</h1>
<button
onClick={[Function]}
onClick={[MockFunction]}
type="button"
>
<span>
Expand All @@ -18,12 +88,12 @@ exports[`renders components if cart has items 1`] = `
<div>
<div>
<ProductListing
setIsCartUpdating={[Function]}
setIsCartUpdating={[MockFunction]}
/>
</div>
<div>
<PriceAdjustments
setIsCartUpdating={[Function]}
setIsCartUpdating={[MockFunction]}
/>
</div>
<div>
Expand All @@ -44,7 +114,7 @@ exports[`renders empty cart text (no adjustments, list or summary) if cart is em
Cart
</h1>
<button
onClick={[Function]}
onClick={[MockFunction]}
type="button"
>
<span>
Expand Down
103 changes: 48 additions & 55 deletions packages/venia-ui/lib/components/CartPage/__tests__/cartPage.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { createTestInstance } from '@magento/peregrine';
import { useLazyQuery } from '@apollo/react-hooks';
import { useCartPage } from '@magento/peregrine/lib/talons/CartPage/useCartPage';

import CartPage from '../cartPage';
import { HeadProvider } from '../../Head';
Expand All @@ -10,43 +10,23 @@ jest.mock('../PriceAdjustments', () => 'PriceAdjustments');
jest.mock('../PriceSummary', () => 'PriceSummary');
jest.mock('../ProductListing', () => 'ProductListing');

jest.mock('@apollo/react-hooks', () => {
const runQuery = jest.fn();
const queryResult = {
data: null,
error: null,
loading: false
};
const useLazyQuery = jest.fn(() => [runQuery, queryResult]);

return { useLazyQuery };
});

jest.mock('@magento/peregrine/lib/context/app', () => {
const api = {
toggleDrawer: jest.fn()
};
const state = {};
const useAppContext = jest.fn(() => [state, api]);

return { useAppContext };
});

jest.mock('@magento/peregrine/lib/context/cart', () => {
const api = {};
const state = { cartId: 'cart123' };
const useCartContext = jest.fn(() => [state, api]);
jest.mock('@magento/peregrine/lib/talons/CartPage/useCartPage', () => {
const useCartPageTalon = jest.requireActual(
'@magento/peregrine/lib/talons/CartPage/useCartPage'
);
const spy = jest.spyOn(useCartPageTalon, 'useCartPage');

return { useCartContext };
return Object.assign(useCartPageTalon, { useCartPage: spy });
});

jest.mock('@magento/peregrine/lib/context/user', () => {
const api = {};
const state = { isSignedIn: false };
const useUserContext = jest.fn(() => [state, api]);

return { useUserContext };
});
const talonProps = {
hasItems: false,
handleSignIn: jest.fn(),
isSignedIn: false,
isCartUpdating: false,
setIsCartUpdating: jest.fn(),
shouldShowLoadingIndicator: false
};

beforeAll(() => {
/**
Expand All @@ -64,46 +44,59 @@ afterAll(() => {
ReactDOM.createPortal.mockClear();
});

test('renders a loading indicator when talon indicates', () => {
// Arrange.
const myTalonProps = {
...talonProps,
shouldShowLoadingIndicator: true
};
useCartPage.mockReturnValueOnce(myTalonProps);

// Act.
const instance = createTestInstance(
<HeadProvider>
<CartPage />
</HeadProvider>
);

// Assert.
expect(instance.toJSON()).toMatchSnapshot();
});

test('renders empty cart text (no adjustments, list or summary) if cart is empty', () => {
useLazyQuery.mockReturnValueOnce([
jest.fn(),
{
data: {
cart: {
total_quantity: 0
}
}
}
]);
// Arrange.
useCartPage.mockReturnValueOnce(talonProps);

// Act.
const instance = createTestInstance(
<HeadProvider>
<CartPage />
</HeadProvider>
);

// Assert.
expect(document.getElementsByTagName('title')[0].innerHTML).toBe(
'Cart - Venia'
);
expect(instance.toJSON()).toMatchSnapshot();
});

test('renders components if cart has items', () => {
useLazyQuery.mockReturnValueOnce([
jest.fn(),
{
data: {
cart: {
total_quantity: 1
}
}
}
]);
// Arrange.
const myTalonProps = {
...talonProps,
hasItems: true
};
useCartPage.mockReturnValueOnce(myTalonProps);

// Act.
const instance = createTestInstance(
<HeadProvider>
<CartPage />
</HeadProvider>
);

// Assert.
expect(document.getElementsByTagName('title')[0].innerHTML).toBe(
'Cart - Venia'
);
Expand Down
Loading