/**
* External dependencies
*/
import TestRenderer, { act } from 'react-test-renderer';
import { createRegistry, RegistryProvider } from '@wordpress/data';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
/**
* Internal dependencies
*/
import * as mockUseStoreCart from '../use-store-cart';
import { useStoreCartItemQuantity } from '../use-store-cart-item-quantity';
jest.mock( '../use-store-cart', () => ( {
useStoreCart: jest.fn(),
} ) );
jest.mock( '@woocommerce/block-data', () => ( {
__esModule: true,
CART_STORE_KEY: 'test/store',
} ) );
// Make debounce instantaneous.
jest.mock( 'use-debounce', () => ( {
useDebounce: ( a ) => [ a ],
} ) );
describe( 'useStoreCartItemQuantity', () => {
let registry, renderer;
const getWrappedComponents = ( Component ) => (
);
const getTestComponent = ( options ) => () => {
const props = useStoreCartItemQuantity( options );
return
;
};
let mockRemoveItemFromCart;
let mockChangeCartItemQuantity;
const setupMocks = ( { isPendingDelete, isPendingQuantity } ) => {
mockRemoveItemFromCart = jest
.fn()
.mockReturnValue( { type: 'removeItemFromCartAction' } );
mockChangeCartItemQuantity = jest
.fn()
.mockReturnValue( { type: 'changeCartItemQuantityAction' } );
registry.registerStore( storeKey, {
reducer: () => ( {} ),
actions: {
removeItemFromCart: mockRemoveItemFromCart,
changeCartItemQuantity: mockChangeCartItemQuantity,
},
selectors: {
isItemPendingDelete: jest
.fn()
.mockReturnValue( isPendingDelete ),
isItemPendingQuantity: jest
.fn()
.mockReturnValue( isPendingQuantity ),
},
} );
};
beforeEach( () => {
registry = createRegistry();
renderer = null;
} );
afterEach( () => {
mockRemoveItemFromCart.mockReset();
mockChangeCartItemQuantity.mockReset();
} );
describe( 'with no errors and not pending', () => {
beforeEach( () => {
setupMocks( { isPendingDelete: false, isPendingQuantity: false } );
mockUseStoreCart.useStoreCart.mockReturnValue( {
cartErrors: {},
} );
} );
it( 'update quantity value should happen instantly', () => {
const TestComponent = getTestComponent( {
key: '123',
quantity: 1,
} );
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent )
);
} );
const { setItemQuantity, quantity } =
renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
expect( quantity ).toBe( 1 );
act( () => {
setItemQuantity( 2 );
} );
const { quantity: newQuantity } =
renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
expect( newQuantity ).toBe( 2 );
} );
it( 'removeItem should call the dispatch action', () => {
const TestComponent = getTestComponent( {
key: '123',
quantity: 1,
} );
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent )
);
} );
const { removeItem } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
act( () => {
removeItem();
} );
expect( mockRemoveItemFromCart ).toHaveBeenCalledWith( '123' );
} );
it( 'setItemQuantity should call the dispatch action', () => {
const TestComponent = getTestComponent( {
key: '123',
quantity: 1,
} );
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent )
);
} );
const { setItemQuantity } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
act( () => {
setItemQuantity( 2 );
} );
expect( mockChangeCartItemQuantity.mock.calls ).toEqual( [
[ '123', 2 ],
] );
} );
} );
it( 'should expose store errors', () => {
const mockCartErrors = [ { message: 'Test error' } ];
setupMocks( {
isPendingDelete: false,
isPendingQuantity: false,
} );
mockUseStoreCart.useStoreCart.mockReturnValue( {
cartErrors: mockCartErrors,
} );
const TestComponent = getTestComponent( {
key: '123',
quantity: 1,
} );
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent )
);
} );
const { cartItemQuantityErrors } =
renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
expect( cartItemQuantityErrors ).toEqual( mockCartErrors );
} );
it( 'isPendingDelete should depend on the value provided by the store', () => {
setupMocks( {
isPendingDelete: true,
isPendingQuantity: false,
} );
mockUseStoreCart.useStoreCart.mockReturnValue( {
cartErrors: {},
} );
const TestComponent = getTestComponent( {
key: '123',
quantity: 1,
} );
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent )
);
} );
const { isPendingDelete } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
expect( isPendingDelete ).toBe( true );
} );
} );
/**
* Check a product object to see if it can be purchased.
*
* @param {Object} product Product object.
* @return {boolean} True if purchasable.
*/
export const productIsPurchasable = ( product ) => {
return product.is_purchasable || false;
};
/**
* Check if the product is supported by the blocks add to cart form.
*
* @param {Object} product Product object.
* @return {boolean} True if supported.
*/
export const productSupportsAddToCartForm = ( product ) => {
/**
* @todo Define supported product types for add to cart form.
*
* When introducing the form-element registration system, include a method of defining if a
* product type has support.
*
* If, as an example, we went with an inner block system for the add to cart form, we could allow
* a type to be registered along with it's default Block template. Registered types would then be
* picked up here, as well as the core types which would be defined elsewhere.
*/
const supportedTypes = [ 'simple', 'variable' ];
return supportedTypes.includes( product.type || 'simple' );
};