Merge pull request #32944 from woocommerce/add/tracks-experimental-products
Add tracks for experimental products page
This commit is contained in:
commit
2c5f47a91d
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: Add
|
||||||
|
|
||||||
|
Changed task_view experimental_product key to variant (technically a breaking change but since it was introduced in the same version it is fine) #32944
|
|
@ -8,7 +8,7 @@ import { Slot, Fill } from '@wordpress/components';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { isProductTaskExperimentTreatment } from './use-product-layout-experiment';
|
import { getProductLayoutExperiment } from './use-product-layout-experiment';
|
||||||
|
|
||||||
export const trackView = async ( taskId ) => {
|
export const trackView = async ( taskId ) => {
|
||||||
const activePlugins = wp.data
|
const activePlugins = wp.data
|
||||||
|
@ -25,9 +25,7 @@ export const trackView = async ( taskId ) => {
|
||||||
|
|
||||||
recordEvent( 'task_view', {
|
recordEvent( 'task_view', {
|
||||||
task_name: taskId,
|
task_name: taskId,
|
||||||
experimental_products:
|
variant: await getProductLayoutExperiment(),
|
||||||
window.wcAdminFeatures[ 'experimental-products-task' ] &&
|
|
||||||
( await isProductTaskExperimentTreatment() ),
|
|
||||||
wcs_installed: installedPlugins.includes( 'woocommerce-services' ),
|
wcs_installed: installedPlugins.includes( 'woocommerce-services' ),
|
||||||
wcs_active: activePlugins.includes( 'woocommerce-services' ),
|
wcs_active: activePlugins.includes( 'woocommerce-services' ),
|
||||||
jetpack_installed: installedPlugins.includes( 'jetpack' ),
|
jetpack_installed: installedPlugins.includes( 'jetpack' ),
|
||||||
|
|
|
@ -7,6 +7,12 @@ import apiFetch from '@wordpress/api-fetch';
|
||||||
import { WC_ADMIN_NAMESPACE } from '@woocommerce/data';
|
import { WC_ADMIN_NAMESPACE } from '@woocommerce/data';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { useState } from '@wordpress/element';
|
import { useState } from '@wordpress/element';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import useRecordCompletionTime from '../use-record-completion-time';
|
||||||
|
|
||||||
type UseLoadSampleProductsProps = {
|
type UseLoadSampleProductsProps = {
|
||||||
redirectUrlAfterSuccess: string;
|
redirectUrlAfterSuccess: string;
|
||||||
|
@ -17,8 +23,13 @@ const useLoadSampleProducts = ( {
|
||||||
}: UseLoadSampleProductsProps ) => {
|
}: UseLoadSampleProductsProps ) => {
|
||||||
const [ isRequesting, setIsRequesting ] = useState< boolean >( false );
|
const [ isRequesting, setIsRequesting ] = useState< boolean >( false );
|
||||||
const { createNotice } = useDispatch( 'core/notices' );
|
const { createNotice } = useDispatch( 'core/notices' );
|
||||||
|
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||||
|
|
||||||
const loadSampleProduct = async () => {
|
const loadSampleProduct = async () => {
|
||||||
|
recordEvent( 'tasklist_add_product', {
|
||||||
|
method: 'sample_product',
|
||||||
|
} );
|
||||||
|
recordCompletionTime();
|
||||||
setIsRequesting( true );
|
setIsRequesting( true );
|
||||||
try {
|
try {
|
||||||
await apiFetch( {
|
await apiFetch( {
|
||||||
|
|
|
@ -50,6 +50,7 @@ export const Products = () => {
|
||||||
|
|
||||||
const productTypeListItems = useProductTypeListItems(
|
const productTypeListItems = useProductTypeListItems(
|
||||||
getProductTypes( [ 'subscription' ] ),
|
getProductTypes( [ 'subscription' ] ),
|
||||||
|
[],
|
||||||
{
|
{
|
||||||
onClick: recordCompletionTime,
|
onClick: recordCompletionTime,
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,8 @@ describe( 'Products', () => {
|
||||||
now: jest
|
now: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValueOnce( 0 )
|
.mockReturnValueOnce( 0 )
|
||||||
|
.mockReturnValueOnce( 1000 )
|
||||||
|
.mockReturnValueOnce( 0 )
|
||||||
.mockReturnValueOnce( 1000 ),
|
.mockReturnValueOnce( 1000 ),
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
@ -84,14 +86,21 @@ describe( 'Products', () => {
|
||||||
'FROM A CSV FILE Import all products at once by uploading a CSV file.',
|
'FROM A CSV FILE Import all products at once by uploading a CSV file.',
|
||||||
} )
|
} )
|
||||||
);
|
);
|
||||||
await waitFor( () =>
|
await waitFor( () => {
|
||||||
expect( recordEvent ).toHaveBeenCalledWith(
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_add_product',
|
||||||
|
{ method: 'import' }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
'task_completion_time',
|
'task_completion_time',
|
||||||
{
|
{
|
||||||
task_name: 'products',
|
task_name: 'products',
|
||||||
time: '0-2s',
|
time: '0-2s',
|
||||||
}
|
}
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Text } from '@woocommerce/experimental';
|
||||||
import { Link } from '@woocommerce/components';
|
import { Link } from '@woocommerce/components';
|
||||||
import interpolateComponents from '@automattic/interpolate-components';
|
import interpolateComponents from '@automattic/interpolate-components';
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -13,6 +14,7 @@ import { getAdminLink } from '@woocommerce/settings';
|
||||||
import { ProductType } from './constants';
|
import { ProductType } from './constants';
|
||||||
import CardList from '../experimental-import-products/CardList';
|
import CardList from '../experimental-import-products/CardList';
|
||||||
import './card-layout.scss';
|
import './card-layout.scss';
|
||||||
|
import useRecordCompletionTime from '../use-record-completion-time';
|
||||||
|
|
||||||
type CardProps = {
|
type CardProps = {
|
||||||
items: ( ProductType & {
|
items: ( ProductType & {
|
||||||
|
@ -21,6 +23,8 @@ type CardProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardLayout: React.FC< CardProps > = ( { items } ) => {
|
const CardLayout: React.FC< CardProps > = ( { items } ) => {
|
||||||
|
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woocommerce-products-card-layout">
|
<div className="woocommerce-products-card-layout">
|
||||||
<Text className="woocommerce-products-card-layout__description">
|
<Text className="woocommerce-products-card-layout__description">
|
||||||
|
@ -32,6 +36,10 @@ const CardLayout: React.FC< CardProps > = ( { items } ) => {
|
||||||
sbLink: (
|
sbLink: (
|
||||||
<Link
|
<Link
|
||||||
onClick={ () => {
|
onClick={ () => {
|
||||||
|
recordEvent( 'tasklist_add_product', {
|
||||||
|
method: 'manually',
|
||||||
|
} );
|
||||||
|
recordCompletionTime();
|
||||||
window.location = getAdminLink(
|
window.location = getAdminLink(
|
||||||
'post-new.php?post_type=product&wc_onboarding_active_task=products&tutorial=true'
|
'post-new.php?post_type=product&wc_onboarding_active_task=products&tutorial=true'
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,8 +7,16 @@ import { Text } from '@woocommerce/experimental';
|
||||||
import { ExternalLink } from '@wordpress/components';
|
import { ExternalLink } from '@wordpress/components';
|
||||||
import { Link } from '@woocommerce/components';
|
import { Link } from '@woocommerce/components';
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import useRecordCompletionTime from '../use-record-completion-time';
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer: React.FC = () => {
|
||||||
|
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woocommerce-products-footer">
|
<div className="woocommerce-products-footer">
|
||||||
<Text className="woocommerce-products-footer__selling-somewhere-else">
|
<Text className="woocommerce-products-footer__selling-somewhere-else">
|
||||||
|
@ -23,6 +31,10 @@ const Footer: React.FC = () => {
|
||||||
importCSVLink: (
|
importCSVLink: (
|
||||||
<Link
|
<Link
|
||||||
onClick={ () => {
|
onClick={ () => {
|
||||||
|
recordEvent( 'tasklist_add_product', {
|
||||||
|
method: 'import',
|
||||||
|
} );
|
||||||
|
recordCompletionTime();
|
||||||
window.location = getAdminLink(
|
window.location = getAdminLink(
|
||||||
'edit.php?post_type=product&page=product_importer&wc_onboarding_active_task=products'
|
'edit.php?post_type=product&page=product_importer&wc_onboarding_active_task=products'
|
||||||
);
|
);
|
||||||
|
@ -36,6 +48,12 @@ const Footer: React.FC = () => {
|
||||||
),
|
),
|
||||||
_3rdLink: (
|
_3rdLink: (
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
|
onClick={ () => {
|
||||||
|
recordEvent( 'tasklist_add_product', {
|
||||||
|
method: 'migrate',
|
||||||
|
} );
|
||||||
|
recordCompletionTime();
|
||||||
|
} }
|
||||||
href="https://woocommerce.com/products/cart2cart/?utm_medium=product"
|
href="https://woocommerce.com/products/cart2cart/?utm_medium=product"
|
||||||
type="external"
|
type="external"
|
||||||
>
|
>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import CardLayout from './card-layout';
|
||||||
import { LoadSampleProductType } from './constants';
|
import { LoadSampleProductType } from './constants';
|
||||||
import LoadSampleProductModal from '../components/load-sample-product-modal';
|
import LoadSampleProductModal from '../components/load-sample-product-modal';
|
||||||
import useLoadSampleProducts from '../components/use-load-sample-products';
|
import useLoadSampleProducts from '../components/use-load-sample-products';
|
||||||
|
import useRecordCompletionTime from '../use-record-completion-time';
|
||||||
|
|
||||||
const getOnboardingProductType = (): string[] => {
|
const getOnboardingProductType = (): string[] => {
|
||||||
const onboardingData = getAdminSetting( 'onboarding' );
|
const onboardingData = getAdminSetting( 'onboarding' );
|
||||||
|
@ -57,11 +58,28 @@ export const Products = () => {
|
||||||
experimentLayout,
|
experimentLayout,
|
||||||
] = useProductTaskExperiment();
|
] = useProductTaskExperiment();
|
||||||
|
|
||||||
const productTypes = useProductTypeListItems( getProductTypes() );
|
|
||||||
const surfacedProductTypeKeys = getSurfacedProductTypeKeys(
|
const surfacedProductTypeKeys = getSurfacedProductTypeKeys(
|
||||||
getOnboardingProductType()
|
getOnboardingProductType()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const productTypes = useProductTypeListItems(
|
||||||
|
getProductTypes(),
|
||||||
|
surfacedProductTypeKeys
|
||||||
|
);
|
||||||
|
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||||
|
|
||||||
|
const productTypesWithTimeRecord = useMemo(
|
||||||
|
() =>
|
||||||
|
productTypes.map( ( productType ) => ( {
|
||||||
|
...productType,
|
||||||
|
onClick: () => {
|
||||||
|
productType.onClick();
|
||||||
|
recordCompletionTime();
|
||||||
|
},
|
||||||
|
} ) ),
|
||||||
|
[ recordCompletionTime ]
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loadSampleProduct,
|
loadSampleProduct,
|
||||||
isLoadingSampleProducts,
|
isLoadingSampleProducts,
|
||||||
|
@ -72,12 +90,13 @@ export const Products = () => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const visibleProductTypes = useMemo( () => {
|
const visibleProductTypes = useMemo( () => {
|
||||||
const surfacedProductTypes = productTypes.filter( ( productType ) =>
|
const surfacedProductTypes = productTypesWithTimeRecord.filter(
|
||||||
|
( productType ) =>
|
||||||
surfacedProductTypeKeys.includes( productType.key )
|
surfacedProductTypeKeys.includes( productType.key )
|
||||||
);
|
);
|
||||||
if ( isExpanded ) {
|
if ( isExpanded ) {
|
||||||
// To show product types in same order, we need to push the other product types to the end.
|
// To show product types in same order, we need to push the other product types to the end.
|
||||||
productTypes.forEach(
|
productTypesWithTimeRecord.forEach(
|
||||||
( productType ) =>
|
( productType ) =>
|
||||||
! surfacedProductTypes.includes( productType ) &&
|
! surfacedProductTypes.includes( productType ) &&
|
||||||
surfacedProductTypes.push( productType )
|
surfacedProductTypes.push( productType )
|
||||||
|
|
|
@ -6,12 +6,14 @@ import { List, Link } from '@woocommerce/components';
|
||||||
import { Text } from '@woocommerce/experimental';
|
import { Text } from '@woocommerce/experimental';
|
||||||
import interpolateComponents from '@automattic/interpolate-components';
|
import interpolateComponents from '@automattic/interpolate-components';
|
||||||
import { getAdminLink } from '@woocommerce/settings';
|
import { getAdminLink } from '@woocommerce/settings';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { ProductType } from './constants';
|
import { ProductType } from './constants';
|
||||||
import './stack.scss';
|
import './stack.scss';
|
||||||
|
import useRecordCompletionTime from '../use-record-completion-time';
|
||||||
|
|
||||||
type StackProps = {
|
type StackProps = {
|
||||||
items: ( ProductType & {
|
items: ( ProductType & {
|
||||||
|
@ -26,6 +28,8 @@ const Stack: React.FC< StackProps > = ( {
|
||||||
onClickLoadSampleProduct,
|
onClickLoadSampleProduct,
|
||||||
showOtherOptions = true,
|
showOtherOptions = true,
|
||||||
} ) => {
|
} ) => {
|
||||||
|
const { recordCompletionTime } = useRecordCompletionTime( 'products' );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woocommerce-products-stack">
|
<div className="woocommerce-products-stack">
|
||||||
<List items={ items } />
|
<List items={ items } />
|
||||||
|
@ -40,6 +44,10 @@ const Stack: React.FC< StackProps > = ( {
|
||||||
sbLink: (
|
sbLink: (
|
||||||
<Link
|
<Link
|
||||||
onClick={ () => {
|
onClick={ () => {
|
||||||
|
recordEvent( 'tasklist_add_product', {
|
||||||
|
method: 'manually',
|
||||||
|
} );
|
||||||
|
recordCompletionTime();
|
||||||
window.location = getAdminLink(
|
window.location = getAdminLink(
|
||||||
'post-new.php?post_type=product&wc_onboarding_active_task=products&tutorial=true'
|
'post-new.php?post_type=product&wc_onboarding_active_task=products&tutorial=true'
|
||||||
);
|
);
|
||||||
|
@ -56,6 +64,7 @@ const Stack: React.FC< StackProps > = ( {
|
||||||
href=""
|
href=""
|
||||||
type="wc-admin"
|
type="wc-admin"
|
||||||
onClick={ () => {
|
onClick={ () => {
|
||||||
|
recordCompletionTime();
|
||||||
onClickLoadSampleProduct();
|
onClickLoadSampleProduct();
|
||||||
return false;
|
return false;
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { render } from '@testing-library/react';
|
import { fireEvent, render } from '@testing-library/react';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -9,7 +10,11 @@ import { render } from '@testing-library/react';
|
||||||
import CardLayout from '../card-layout';
|
import CardLayout from '../card-layout';
|
||||||
import { productTypes } from '../constants';
|
import { productTypes } from '../constants';
|
||||||
|
|
||||||
|
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
|
||||||
describe( 'CardLayout', () => {
|
describe( 'CardLayout', () => {
|
||||||
|
beforeEach( () => {
|
||||||
|
( recordEvent as jest.Mock ).mockClear();
|
||||||
|
} );
|
||||||
it( 'should render all products types in CardLayout', () => {
|
it( 'should render all products types in CardLayout', () => {
|
||||||
const { queryByText, queryAllByRole } = render(
|
const { queryByText, queryAllByRole } = render(
|
||||||
<CardLayout
|
<CardLayout
|
||||||
|
@ -26,4 +31,28 @@ describe( 'CardLayout', () => {
|
||||||
|
|
||||||
expect( queryAllByRole( 'link' ) ).toHaveLength( 1 );
|
expect( queryAllByRole( 'link' ) ).toHaveLength( 1 );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'start blank link should fire the tasklist_add_product and completion events', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<CardLayout
|
||||||
|
items={ [
|
||||||
|
{
|
||||||
|
...productTypes[ 0 ],
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
] }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
fireEvent.click( getByText( 'Start blank' ) );
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_add_product',
|
||||||
|
{ method: 'manually' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
|
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -9,8 +13,41 @@ import { render } from '@testing-library/react';
|
||||||
import Footer from '../footer';
|
import Footer from '../footer';
|
||||||
|
|
||||||
describe( 'Footer', () => {
|
describe( 'Footer', () => {
|
||||||
|
beforeEach( () => {
|
||||||
|
( recordEvent as jest.Mock ).mockClear();
|
||||||
|
} );
|
||||||
it( 'should render footer with two links', () => {
|
it( 'should render footer with two links', () => {
|
||||||
const { queryAllByRole } = render( <Footer /> );
|
const { queryAllByRole } = render( <Footer /> );
|
||||||
expect( queryAllByRole( 'link' ) ).toHaveLength( 2 );
|
expect( queryAllByRole( 'link' ) ).toHaveLength( 2 );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'clicking on import CSV should fire event tasklist_add_product with method:import and task_completion_time', () => {
|
||||||
|
const { getByText } = render( <Footer /> );
|
||||||
|
userEvent.click( getByText( 'Import your products from a CSV file' ) );
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_add_product',
|
||||||
|
{ method: 'import' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'clicking on start blank should fire event tasklist_add_product with method:migrate and task_completion_time', () => {
|
||||||
|
const { getByText } = render( <Footer /> );
|
||||||
|
userEvent.click( getByText( 'use a 3rd party migration plugin' ) );
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_add_product',
|
||||||
|
{ method: 'migrate' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { render, waitFor } from '@testing-library/react';
|
import { render, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { useProductTaskExperiment } from '@woocommerce/onboarding';
|
import { useProductTaskExperiment } from '@woocommerce/onboarding';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -25,6 +26,12 @@ jest.mock( '@woocommerce/onboarding', () => ( {
|
||||||
useProductTaskExperiment: jest.fn().mockReturnValue( [ false, 'stacked' ] ),
|
useProductTaskExperiment: jest.fn().mockReturnValue( [ false, 'stacked' ] ),
|
||||||
} ) );
|
} ) );
|
||||||
|
|
||||||
|
jest.mock( '../use-create-product-by-type', () => ( {
|
||||||
|
useCreateProductByType: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue( { createProductByType: jest.fn() } ),
|
||||||
|
} ) );
|
||||||
|
|
||||||
global.fetch = jest.fn().mockImplementation( () =>
|
global.fetch = jest.fn().mockImplementation( () =>
|
||||||
Promise.resolve( {
|
Promise.resolve( {
|
||||||
json: () => Promise.resolve( {} ),
|
json: () => Promise.resolve( {} ),
|
||||||
|
@ -32,6 +39,8 @@ global.fetch = jest.fn().mockImplementation( () =>
|
||||||
} )
|
} )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
|
||||||
|
|
||||||
describe( 'Products', () => {
|
describe( 'Products', () => {
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
@ -65,6 +74,70 @@ describe( 'Products', () => {
|
||||||
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'clicking on suggested product should fire event tasklist_product_template_selection with is_suggested:true and task_completion_time', () => {
|
||||||
|
( getAdminSetting as jest.Mock ).mockImplementation( () => ( {
|
||||||
|
profile: {
|
||||||
|
product_types: [ 'downloads' ],
|
||||||
|
},
|
||||||
|
} ) );
|
||||||
|
const { getByRole } = render( <Products /> );
|
||||||
|
|
||||||
|
userEvent.click(
|
||||||
|
getByRole( 'menuitem', {
|
||||||
|
name:
|
||||||
|
'Digital product A digital product like service, downloadable book, music or video.',
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_product_template_selection',
|
||||||
|
{ is_suggested: true, product_type: 'digital' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'clicking on not-suggested product should fire event tasklist_product_template_selection with is_suggested:false and task_completion_time', async () => {
|
||||||
|
( getAdminSetting as jest.Mock ).mockImplementation( () => ( {
|
||||||
|
profile: {
|
||||||
|
product_types: [ 'downloads' ],
|
||||||
|
},
|
||||||
|
} ) );
|
||||||
|
const { queryByText, getByRole, queryByRole } = render( <Products /> );
|
||||||
|
|
||||||
|
expect( queryByText( 'View more product types' ) ).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.click(
|
||||||
|
getByRole( 'button', { name: 'View more product types' } )
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor( () =>
|
||||||
|
expect( queryByRole( 'menu' )?.childElementCount ).toBe(
|
||||||
|
productTypes.length
|
||||||
|
)
|
||||||
|
);
|
||||||
|
userEvent.click(
|
||||||
|
getByRole( 'menuitem', {
|
||||||
|
name: 'Grouped product A collection of related products.',
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_product_template_selection',
|
||||||
|
{ is_suggested: false, product_type: 'grouped' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
it( 'should render all products type when clicking view more button', async () => {
|
it( 'should render all products type when clicking view more button', async () => {
|
||||||
( getAdminSetting as jest.Mock ).mockImplementation( () => ( {
|
( getAdminSetting as jest.Mock ).mockImplementation( () => ( {
|
||||||
profile: {
|
profile: {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
import { render, waitFor } from '@testing-library/react';
|
import { render, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -10,7 +11,13 @@ import userEvent from '@testing-library/user-event';
|
||||||
import Stack from '../stack';
|
import Stack from '../stack';
|
||||||
import { productTypes } from '../constants';
|
import { productTypes } from '../constants';
|
||||||
|
|
||||||
|
jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
|
||||||
|
|
||||||
describe( 'Stack', () => {
|
describe( 'Stack', () => {
|
||||||
|
beforeEach( () => {
|
||||||
|
( recordEvent as jest.Mock ).mockClear();
|
||||||
|
} );
|
||||||
|
|
||||||
it( 'should render stack with given product type and two links', () => {
|
it( 'should render stack with given product type and two links', () => {
|
||||||
const { queryByText, queryAllByRole } = render(
|
const { queryByText, queryAllByRole } = render(
|
||||||
<Stack
|
<Stack
|
||||||
|
@ -63,5 +70,38 @@ describe( 'Stack', () => {
|
||||||
getByRole( 'link', { name: 'Load Sample Products' } )
|
getByRole( 'link', { name: 'Load Sample Products' } )
|
||||||
);
|
);
|
||||||
await waitFor( () => expect( onClickLoadSampleProduct ).toBeCalled() );
|
await waitFor( () => expect( onClickLoadSampleProduct ).toBeCalled() );
|
||||||
|
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'should fire the tasklist_add_product and task_completion_time events when the "Start Blank" link is clicked', async () => {
|
||||||
|
const onClickLoadSampleProduct = jest.fn();
|
||||||
|
const { getByRole } = render(
|
||||||
|
<Stack
|
||||||
|
onClickLoadSampleProduct={ onClickLoadSampleProduct }
|
||||||
|
items={ [
|
||||||
|
{
|
||||||
|
...productTypes[ 0 ],
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
] }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
userEvent.click( getByRole( 'link', { name: 'Start Blank' } ) );
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
'tasklist_add_product',
|
||||||
|
{ method: 'manually' }
|
||||||
|
);
|
||||||
|
expect( recordEvent ).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
'task_completion_time',
|
||||||
|
{ task_name: 'products', time: '0-2s' }
|
||||||
|
);
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { useState } from '@wordpress/element';
|
||||||
import { ProductTypeKey } from './constants';
|
import { ProductTypeKey } from './constants';
|
||||||
import { createNoticesFromResponse } from '../../../lib/notices';
|
import { createNoticesFromResponse } from '../../../lib/notices';
|
||||||
|
|
||||||
const useCreateProductByType = () => {
|
export const useCreateProductByType = () => {
|
||||||
const { createProductFromTemplate } = useDispatch( ITEMS_STORE_NAME );
|
const { createProductFromTemplate } = useDispatch( ITEMS_STORE_NAME );
|
||||||
const [ isRequesting, setIsRequesting ] = useState< boolean >( false );
|
const [ isRequesting, setIsRequesting ] = useState< boolean >( false );
|
||||||
|
|
||||||
|
@ -54,5 +54,3 @@ const useCreateProductByType = () => {
|
||||||
isRequesting,
|
isRequesting,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useCreateProductByType;
|
|
||||||
|
|
|
@ -2,15 +2,17 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { useMemo } from '@wordpress/element';
|
import { useMemo } from '@wordpress/element';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import useCreateProductByType from './use-create-product-by-type';
|
import { useCreateProductByType } from './use-create-product-by-type';
|
||||||
import { ProductType } from './constants';
|
import { ProductType, ProductTypeKey } from './constants';
|
||||||
|
|
||||||
const useProductTypeListItems = (
|
const useProductTypeListItems = (
|
||||||
_productTypes: ProductType[],
|
_productTypes: ProductType[],
|
||||||
|
suggestedProductTypes: ProductTypeKey[] = [],
|
||||||
{
|
{
|
||||||
onClick,
|
onClick,
|
||||||
}: {
|
}: {
|
||||||
|
@ -25,6 +27,12 @@ const useProductTypeListItems = (
|
||||||
...productType,
|
...productType,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
createProductByType( productType.key );
|
createProductByType( productType.key );
|
||||||
|
recordEvent( 'tasklist_product_template_selection', {
|
||||||
|
product_type: productType.key,
|
||||||
|
is_suggested: suggestedProductTypes.includes(
|
||||||
|
productType.key
|
||||||
|
),
|
||||||
|
} );
|
||||||
if ( typeof onClick === 'function' ) {
|
if ( typeof onClick === 'function' ) {
|
||||||
onClick();
|
onClick();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: Add
|
||||||
|
|
||||||
|
Added events for experimental products page #32944
|
Loading…
Reference in New Issue