diff --git a/packages/js/internal-js-tests/changelog/add-tracks-unit-tests-improvements-product-editor b/packages/js/internal-js-tests/changelog/add-tracks-unit-tests-improvements-product-editor
new file mode 100644
index 00000000000..587546f4efb
--- /dev/null
+++ b/packages/js/internal-js-tests/changelog/add-tracks-unit-tests-improvements-product-editor
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add tests for some product editor tracks
diff --git a/packages/js/internal-js-tests/jest-preset.js b/packages/js/internal-js-tests/jest-preset.js
index ef55ec5f8d0..451e1373986 100644
--- a/packages/js/internal-js-tests/jest-preset.js
+++ b/packages/js/internal-js-tests/jest-preset.js
@@ -51,6 +51,9 @@ module.exports = {
'**/test/*.[jt]s?(x)',
'**/?(*.)test.[jt]s?(x)',
],
+ testPathIgnorePatterns: [
+ '\\.d\\.ts$', // This regex pattern matches any file that ends with .d.ts
+ ],
// The keys for the transformed modules contains the name of the packages that should be transformed.
transformIgnorePatterns: [
'node_modules/(?!(?:\\.pnpm|' + Object.keys( transformModules ).join( '|' ) + ')/)',
diff --git a/packages/js/product-editor/changelog/add-tracks-unit-tests-improvements-product-editor b/packages/js/product-editor/changelog/add-tracks-unit-tests-improvements-product-editor
new file mode 100644
index 00000000000..587546f4efb
--- /dev/null
+++ b/packages/js/product-editor/changelog/add-tracks-unit-tests-improvements-product-editor
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add tests for some product editor tracks
diff --git a/packages/js/product-editor/src/components/feedback-bar/test/feedback-bar.test.tsx b/packages/js/product-editor/src/components/feedback-bar/test/feedback-bar.test.tsx
new file mode 100644
index 00000000000..2c1adbb74ce
--- /dev/null
+++ b/packages/js/product-editor/src/components/feedback-bar/test/feedback-bar.test.tsx
@@ -0,0 +1,44 @@
+/**
+ * External dependencies
+ */
+import { act, fireEvent, render, screen } from '@testing-library/react';
+import React from 'react';
+import { createElement } from '@wordpress/element';
+import { recordEvent } from '@woocommerce/tracks';
+
+/**
+ * Internal dependencies
+ */
+import { FeedbackBar } from '../feedback-bar';
+import { useFeedbackBar } from '../../../hooks/use-feedback-bar';
+
+jest.mock( '../../../hooks/use-feedback-bar', () => ( {
+ ...jest.requireActual( '../../../hooks/use-feedback-bar' ),
+ useFeedbackBar: jest.fn(),
+} ) );
+
+jest.mock( '@woocommerce/tracks', () => ( {
+ ...jest.requireActual( '@woocommerce/tracks' ),
+ recordEvent: jest.fn(),
+} ) );
+
+describe( 'FeedbackBar', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_editor_feedback_bar_turnoff_editor_click event when clicking turn off editor', () => {
+ ( useFeedbackBar as jest.Mock ).mockImplementation( () => ( {
+ hideFeedbackBar: () => {},
+ shouldShowFeedbackBar: true,
+ } ) );
+ render( );
+
+ act( () => {
+ fireEvent.click( screen.getByText( 'turn it off' ) );
+ } );
+ expect( recordEvent ).toBeCalledWith(
+ 'product_editor_feedback_bar_turnoff_editor_click',
+ { product_type: 'testing' }
+ );
+ } );
+} );
diff --git a/packages/js/product-editor/src/components/tabs/test/tabs.spec.tsx b/packages/js/product-editor/src/components/tabs/test/tabs.spec.tsx
index ca034907e20..89f768e2ff2 100644
--- a/packages/js/product-editor/src/components/tabs/test/tabs.spec.tsx
+++ b/packages/js/product-editor/src/components/tabs/test/tabs.spec.tsx
@@ -6,6 +6,8 @@ import { render, fireEvent, screen } from '@testing-library/react';
import { getQuery, navigateTo } from '@woocommerce/navigation';
import { SlotFillProvider } from '@wordpress/components';
import { useState, createElement } from '@wordpress/element';
+import { recordEvent } from '@woocommerce/tracks';
+import { select } from '@wordpress/data';
/**
* Internal dependencies
@@ -15,6 +17,7 @@ import {
TabBlockEdit as Tab,
TabBlockAttributes,
} from '../../../blocks/generic/tab/edit';
+import { TRACKS_SOURCE } from '../../../constants';
jest.mock( '@woocommerce/block-templates', () => ( {
...jest.requireActual( '@woocommerce/block-templates' ),
@@ -27,6 +30,19 @@ jest.mock( '@woocommerce/navigation', () => ( {
getQuery: jest.fn().mockReturnValue( {} ),
} ) );
+jest.mock( '@woocommerce/tracks', () => ( {
+ ...jest.requireActual( '@woocommerce/tracks' ),
+ recordEvent: jest.fn(),
+} ) );
+
+jest.mock( '@wordpress/data', () => {
+ const originalModule = jest.requireActual( '@wordpress/data' );
+ return {
+ ...originalModule,
+ select: jest.fn( ( ...args ) => originalModule.select( ...args ) ),
+ };
+} );
+
const blockProps = {
setAttributes: () => {},
className: '',
@@ -109,6 +125,7 @@ function MockTabs( { onChange = jest.fn() } ) {
describe( 'Tabs', () => {
beforeEach( () => {
+ jest.clearAllMocks();
( getQuery as jest.Mock ).mockReturnValue( {
tab: null,
} );
@@ -224,4 +241,21 @@ describe( 'Tabs', () => {
expect( panel1.classList ).not.toContain( 'is-selected' );
expect( panel2.classList ).toContain( 'is-selected' );
} );
+
+ it( 'should trigger wcadmin_product_tab_click track event when tab is clicked', async () => {
+ ( select as jest.Mock ).mockImplementation( () => ( {
+ getEditedEntityRecord: () => ( {
+ type: 'simple',
+ } ),
+ } ) );
+ render( );
+
+ const button = screen.getByText( 'Test button 2' );
+ fireEvent.click( button );
+ expect( recordEvent ).toBeCalledWith( 'product_tab_click', {
+ product_tab: 'test2',
+ product_type: 'simple',
+ source: TRACKS_SOURCE,
+ } );
+ } );
} );
diff --git a/plugins/woocommerce-admin/client/products/fills/more-menu-items/test/delete-variation-menu-item.test.tsx b/plugins/woocommerce-admin/client/products/fills/more-menu-items/test/delete-variation-menu-item.test.tsx
new file mode 100644
index 00000000000..e8a69e378d3
--- /dev/null
+++ b/plugins/woocommerce-admin/client/products/fills/more-menu-items/test/delete-variation-menu-item.test.tsx
@@ -0,0 +1,58 @@
+/**
+ * External dependencies
+ */
+import { render, fireEvent } from '@testing-library/react';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { recordEvent } from '@woocommerce/tracks';
+import { useParams } from 'react-router-dom';
+
+/**
+ * Internal dependencies
+ */
+import { DeleteVariationMenuItem } from '../delete-variation-menu-item';
+
+jest.mock( '@wordpress/data', () => ( {
+ ...jest.requireActual( '@wordpress/data' ),
+ useDispatch: jest.fn(),
+ useSelect: jest.fn(),
+} ) );
+jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
+
+jest.mock( 'react-router-dom', () => ( { useParams: jest.fn() } ) );
+
+jest.mock( '@wordpress/core-data', () => ( {
+ useEntityId: jest.fn().mockReturnValue( 'variation_1' ),
+ useEntityProp: jest
+ .fn()
+ .mockImplementation( ( _1, _2, propType ) => [ propType ] ),
+} ) );
+
+describe( 'DeleteVariationMenuItem', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_dropdown_option_click track event when clicking the menu', async () => {
+ ( useDispatch as jest.Mock ).mockReturnValue( {
+ deleteProductVariation: () => {},
+ } );
+ ( useSelect as jest.Mock ).mockReturnValue( {
+ type: 'simple',
+ status: 'publish',
+ } );
+ ( useParams as jest.Mock ).mockReturnValue( { productId: 1 } );
+ const { getByText } = render(
+ {} } />
+ );
+ fireEvent.click( getByText( 'Delete variation' ) );
+
+ expect( recordEvent ).toHaveBeenCalledWith(
+ 'product_dropdown_option_click',
+ {
+ product_id: 1,
+ product_status: 'status',
+ selected_option: 'delete_variation',
+ variation_id: 'variation_1',
+ }
+ );
+ } );
+} );
diff --git a/plugins/woocommerce-admin/client/products/fills/test/product-block-editor-fills.test.tsx b/plugins/woocommerce-admin/client/products/fills/test/product-block-editor-fills.test.tsx
new file mode 100644
index 00000000000..0158721252b
--- /dev/null
+++ b/plugins/woocommerce-admin/client/products/fills/test/product-block-editor-fills.test.tsx
@@ -0,0 +1,49 @@
+/**
+ * External dependencies
+ */
+import { render, fireEvent } from '@testing-library/react';
+import { useSelect } from '@wordpress/data';
+import { recordEvent } from '@woocommerce/tracks';
+
+/**
+ * Internal dependencies
+ */
+import { MoreMenuFill } from '../product-block-editor-fills';
+
+jest.mock( '@wordpress/data', () => ( {
+ ...jest.requireActual( '@wordpress/data' ),
+ useSelect: jest.fn(),
+} ) );
+jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) );
+jest.mock( '../more-menu-items', () => ( {
+ ...jest.requireActual( '../more-menu-items' ),
+ ClassicEditorMenuItem: jest.fn().mockImplementation( () => ),
+ FeedbackMenuItem: jest.fn().mockImplementation( ( { onClick } ) => (
+
+
+
+ ) ),
+} ) );
+
+describe( 'MoreMenuFill', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_dropdown_option_click track event when clicking the menu', async () => {
+ ( useSelect as jest.Mock ).mockReturnValue( {
+ type: 'simple',
+ status: 'publish',
+ } );
+ const { getByText } = render( {} } /> );
+ fireEvent.click( getByText( 'Feedback button' ) );
+
+ expect( recordEvent ).toHaveBeenCalledWith(
+ 'product_dropdown_option_click',
+ {
+ product_status: 'publish',
+ product_type: 'simple',
+ selected_option: 'feedback',
+ }
+ );
+ } );
+} );
diff --git a/plugins/woocommerce-admin/client/products/test/product-page.test.tsx b/plugins/woocommerce-admin/client/products/test/product-page.test.tsx
new file mode 100644
index 00000000000..d9e87de706b
--- /dev/null
+++ b/plugins/woocommerce-admin/client/products/test/product-page.test.tsx
@@ -0,0 +1,80 @@
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+import { recordEvent } from '@woocommerce/tracks';
+import { TRACKS_SOURCE } from '@woocommerce/product-editor';
+import { useParams } from 'react-router-dom';
+
+/**
+ * Internal dependencies
+ */
+import ProductPage from '../product-page';
+import ProductVariationPage from '../product-variation-page';
+
+jest.mock( '@woocommerce/tracks', () => ( {
+ recordEvent: jest.fn(),
+} ) );
+jest.mock( 'react-router-dom', () => ( { useParams: jest.fn() } ) );
+
+// Mocks to prevent crashes.
+jest.mock( '@wordpress/api-fetch', () => ( {
+ apiFetch: jest.fn(),
+} ) );
+jest.mock( '@wordpress/core-data', () => ( {
+ apiFetch: jest.fn(),
+} ) );
+jest.mock( '../hooks/use-product-entity-record', () => ( {
+ useProductEntityRecord: jest.fn(),
+} ) );
+jest.mock( '../hooks/use-product-variation-entity-record', () => ( {
+ useProductVariationEntityRecord: jest.fn(),
+} ) );
+jest.mock( '@woocommerce/product-editor', () => ( {
+ ...jest.requireActual( '@woocommerce/product-editor' ),
+ productEditorHeaderApiFetchMiddleware: jest.fn(),
+ productApiFetchMiddleware: jest.fn(),
+ __experimentalInitBlocks: jest.fn().mockImplementation( () => () => {} ),
+} ) );
+
+describe( 'ProductPage', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_add_view on render without product_id defined', () => {
+ ( useParams as jest.Mock ).mockReturnValue( { productId: null } );
+ render( );
+ expect( recordEvent ).toBeCalledWith( 'product_add_view', {
+ source: TRACKS_SOURCE,
+ } );
+ } );
+ it( 'should trigger product_edit_view on render with product_id defined', () => {
+ ( useParams as jest.Mock ).mockReturnValue( { productId: 1 } );
+ render( );
+ expect( recordEvent ).toBeCalledWith( 'product_edit_view', {
+ source: TRACKS_SOURCE,
+ product_id: 1,
+ } );
+ } );
+} );
+
+describe( 'ProductVariationPage', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_add_view track event on render without product_id defined', () => {
+ ( useParams as jest.Mock ).mockReturnValue( { productId: null } );
+ render( );
+ expect( recordEvent ).toBeCalledWith( 'product_add_view', {
+ source: TRACKS_SOURCE,
+ } );
+ } );
+ it( 'should trigger product_edit_view track event on render with product_id defined', () => {
+ ( useParams as jest.Mock ).mockReturnValue( { productId: 1 } );
+ render( );
+ expect( recordEvent ).toBeCalledWith( 'product_edit_view', {
+ source: TRACKS_SOURCE,
+ product_id: 1,
+ } );
+ } );
+} );
diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/global.d.ts b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/global.d.ts
new file mode 100644
index 00000000000..79210a3d85f
--- /dev/null
+++ b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/global.d.ts
@@ -0,0 +1,4 @@
+declare const productScreen: { name: string };
+declare const global: typeof globalThis & {
+ productScreen: typeof productScreen;
+};
diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-edit.test.tsx b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-edit.test.tsx
new file mode 100644
index 00000000000..4231a08f29f
--- /dev/null
+++ b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-edit.test.tsx
@@ -0,0 +1,36 @@
+/**
+ * @jest-environment node
+ */
+
+/**
+ * External dependencies
+ */
+import { recordEvent } from '@woocommerce/tracks';
+
+jest.mock( '@woocommerce/tracks', () => ( {
+ recordEvent: jest.fn(),
+} ) );
+jest.mock( '../shared', () => ( {
+ addExitPageListener: jest.fn().mockImplementation( () => {} ),
+ initProductScreenTracks: jest.fn().mockImplementation( () => {} ),
+ getProductData: jest.fn().mockImplementation( () => ( { product_id: 1 } ) ),
+} ) );
+
+describe( 'Product Screen Tracking', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_edit_view event when productScreen.name is "edit"', () => {
+ global.productScreen = { name: 'edit' };
+ require( '../product-edit' );
+ expect( recordEvent ).toHaveBeenCalledWith( 'product_edit_view', {
+ product_id: 1,
+ } );
+ } );
+
+ it( 'should not trigger product_edit_view event when productScreen.name is not "edit"', () => {
+ global.productScreen = { name: '' };
+ require( '../product-edit' );
+ expect( recordEvent ).not.toHaveBeenCalled();
+ } );
+} );
diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-new.test.tsx b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-new.test.tsx
new file mode 100644
index 00000000000..e20d647fe3e
--- /dev/null
+++ b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/test/product-new.test.tsx
@@ -0,0 +1,33 @@
+/**
+ * @jest-environment node
+ */
+
+/**
+ * External dependencies
+ */
+import { recordEvent } from '@woocommerce/tracks';
+
+jest.mock( '@woocommerce/tracks', () => ( {
+ recordEvent: jest.fn(),
+} ) );
+jest.mock( '../shared', () => ( {
+ addExitPageListener: jest.fn().mockImplementation( () => {} ),
+ initProductScreenTracks: jest.fn().mockImplementation( () => {} ),
+} ) );
+
+describe( 'Product Screen Tracking', () => {
+ beforeEach( () => {
+ jest.clearAllMocks();
+ } );
+ it( 'should trigger product_add_view event when productScreen.name is "new"', () => {
+ global.productScreen = { name: 'new' };
+ require( '../product-new' );
+ expect( recordEvent ).toHaveBeenCalledWith( 'product_add_view' );
+ } );
+
+ it( 'should not trigger product_add_view event when productScreen.name is not "new"', () => {
+ global.productScreen = { name: '' };
+ require( '../product-new' );
+ expect( recordEvent ).not.toHaveBeenCalled();
+ } );
+} );
diff --git a/plugins/woocommerce/changelog/add-tracks-unit-tests-improvements-product-editor b/plugins/woocommerce/changelog/add-tracks-unit-tests-improvements-product-editor
new file mode 100644
index 00000000000..e25aecd09a2
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-tracks-unit-tests-improvements-product-editor
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add tests for some product editor tracks
\ No newline at end of file