Commit 84c617828f3 for woocommerce

commit 84c617828f3be3013b86f0d01a17ce84919bb698
Author: Taha Paksu <3295+tpaksu@users.noreply.github.com>
Date:   Tue Mar 3 20:09:52 2026 +0300

    [WOOPLUG-6346] Add unified shipping partner Tracks events on task list (#63438)

diff --git a/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/index.js b/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/index.js
index d7d78e2a09f..eb6246a84cd 100644
--- a/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/index.js
+++ b/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/index.js
@@ -65,6 +65,7 @@ export class Shipping extends Component {

 		this.storeLocationCompleted = false;
 		this.shippingPartners = props.shippingPartners;
+		this.impressionFired = false;

 		this.jetpackAuthRedirectUrl = getAdminLink( 'admin.php?page=wc-admin' );
 	}
@@ -136,6 +137,58 @@ export class Shipping extends Component {
 		this.setState( { isPending: false, shippingZones } );
 	}

+	getShippingPartnerTrackingProps() {
+		const { countryCode, shippingPartners = [] } = this.props;
+		const pluginSlugs = shippingPartners
+			.map( ( partner ) => partner.slug )
+			.filter(
+				( slug ) => typeof slug === 'string' && slug.trim().length > 0
+			)
+			.join( ',' );
+		return {
+			context: 'tasklist',
+			country: countryCode,
+			plugins: pluginSlugs,
+		};
+	}
+
+	recordInstallAndActivateEvents( selectedPlugin, success ) {
+		const trackingBase = {
+			...this.getShippingPartnerTrackingProps(),
+			selected_plugin: selectedPlugin,
+		};
+
+		if ( success ) {
+			recordEvent( 'shipping_partner_install', {
+				...trackingBase,
+				success: true,
+			} );
+			recordEvent( 'shipping_partner_activate', {
+				...trackingBase,
+				success: true,
+			} );
+		} else {
+			const { installedPlugins = [] } = this.props;
+			const wasInstalled = installedPlugins.includes( selectedPlugin );
+
+			if ( wasInstalled ) {
+				recordEvent( 'shipping_partner_install', {
+					...trackingBase,
+					success: true,
+				} );
+				recordEvent( 'shipping_partner_activate', {
+					...trackingBase,
+					success: false,
+				} );
+			} else {
+				recordEvent( 'shipping_partner_install', {
+					...trackingBase,
+					success: false,
+				} );
+			}
+		}
+	}
+
 	componentDidUpdate( prevProps, prevState ) {
 		const { countryCode, countryName, settings } = this.props;
 		const {
@@ -157,6 +210,21 @@ export class Shipping extends Component {
 			}
 		}

+		if (
+			step === 'label_printing' &&
+			prevState.step !== 'label_printing' &&
+			! this.impressionFired
+		) {
+			const { shippingPartners = [] } = this.props;
+			if ( shippingPartners.length > 0 ) {
+				recordEvent(
+					'shipping_partner_impression',
+					this.getShippingPartnerTrackingProps()
+				);
+				this.impressionFired = true;
+			}
+		}
+
 		const isCompleteAddress = Boolean(
 			storeAddress && defaultCountry && storePostCode
 		);
@@ -350,26 +418,26 @@ export class Shipping extends Component {
 						<Plugins
 							onComplete={ ( _plugins, response ) => {
 								createNoticesFromResponse( response );
-								recordEvent(
-									'tasklist_shipping_label_printing',
-									{
-										install: true,
-										plugins_to_activate: pluginsToActivate,
-									}
+								this.recordInstallAndActivateEvents(
+									pluginsToActivate[ 0 ],
+									true
 								);
 								this.completeStep();
 							} }
-							onError={ ( errors, response ) =>
-								createNoticesFromResponse( response )
-							}
-							onSkip={ () => {
-								recordEvent(
-									'tasklist_shipping_label_printing',
-									{
-										install: false,
-										plugins_to_activate: pluginsToActivate,
-									}
+							onError={ ( errors, response ) => {
+								createNoticesFromResponse( response );
+								this.recordInstallAndActivateEvents(
+									pluginsToActivate[ 0 ],
+									false
 								);
+							} }
+							onClick={ () => {
+								recordEvent( 'shipping_partner_click', {
+									...this.getShippingPartnerTrackingProps(),
+									selected_plugin: pluginsToActivate[ 0 ],
+								} );
+							} }
+							onSkip={ () => {
 								invalidateResolutionForStoreSelector();
 								getHistory().push( getNewPath( {}, '/', {} ) );
 								onComplete();
@@ -483,13 +551,9 @@ export class Shipping extends Component {
 																createNoticesFromResponse(
 																	response
 																);
-																recordEvent(
-																	'tasklist_shipping_label_printing',
-																	{
-																		install: true,
-																		plugins_to_activate:
-																			pluginsForPartner,
-																	}
+																this.recordInstallAndActivateEvents(
+																	shippingMethod.slug,
+																	true
 																);
 																invalidateResolutionForStoreSelector();
 																this.completeStep();
@@ -497,11 +561,25 @@ export class Shipping extends Component {
 															onError={ (
 																errors,
 																response
-															) =>
+															) => {
 																createNoticesFromResponse(
 																	response
-																)
-															}
+																);
+																this.recordInstallAndActivateEvents(
+																	shippingMethod.slug,
+																	false
+																);
+															} }
+															onClick={ () => {
+																recordEvent(
+																	'shipping_partner_click',
+																	{
+																		...this.getShippingPartnerTrackingProps(),
+																		selected_plugin:
+																			shippingMethod.slug,
+																	}
+																);
+															} }
 															installText={ __(
 																'Install and enable',
 																'woocommerce'
@@ -566,22 +644,33 @@ export class Shipping extends Component {
 											createNoticesFromResponse(
 												response
 											);
-											recordEvent(
-												'tasklist_shipping_label_printing',
-												{
-													install: true,
-													plugins_to_activate:
-														pluginsToActivate,
-												}
+											this.recordInstallAndActivateEvents(
+												pluginsToPromote[ 0 ]?.slug,
+												true
 											);
 											invalidateResolutionForStoreSelector();
 											this.completeStep();
 										} }
-										onError={ ( errors, response ) =>
+										onError={ ( errors, response ) => {
 											createNoticesFromResponse(
 												response
-											)
-										}
+											);
+											this.recordInstallAndActivateEvents(
+												pluginsToPromote[ 0 ]?.slug,
+												false
+											);
+										} }
+										onClick={ () => {
+											recordEvent(
+												'shipping_partner_click',
+												{
+													...this.getShippingPartnerTrackingProps(),
+													selected_plugin:
+														pluginsToPromote[ 0 ]
+															?.slug,
+												}
+											);
+										} }
 										onSkip={
 											onShippingPluginInstalltionSkip
 										}
@@ -687,7 +776,8 @@ const ShippingWrapper = compose(
 	withSelect( ( select ) => {
 		const { getSettings, isUpdateSettingsRequesting } =
 			select( settingsStore );
-		const { getActivePlugins, isJetpackConnected } = select( pluginsStore );
+		const { getActivePlugins, getInstalledPlugins, isJetpackConnected } =
+			select( pluginsStore );
 		const { getCountry } = select( COUNTRIES_STORE_NAME );

 		const { general: settings = {} } = getSettings( 'general' );
@@ -701,6 +791,7 @@ const ShippingWrapper = compose(
 		const country = countryCode ? getCountry( countryCode ) : null;
 		const countryName = country ? country.name : null;
 		const activePlugins = getActivePlugins();
+		const installedPlugins = getInstalledPlugins();

 		return {
 			countryCode,
@@ -708,6 +799,7 @@ const ShippingWrapper = compose(
 			isUpdateSettingsRequesting: isUpdateSettingsRequesting( 'general' ),
 			settings,
 			activePlugins,
+			installedPlugins,
 			isJetpackConnected: isJetpackConnected(),
 			shippingPartners,
 		};
diff --git a/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/test/index.tsx b/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/test/index.tsx
index 5ebfcb82a12..1c2c5297c4a 100644
--- a/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/test/index.tsx
+++ b/plugins/woocommerce/client/admin/client/task-lists/fills/shipping/test/index.tsx
@@ -97,4 +97,289 @@ describe( 'Shipping', () => {
 	it( 'treats missing slugs as non-installable partners', () => {
 		expect( hasInstallableSlug( {} ) ).toBe( false );
 	} );
+
+	describe( 'shipping partner impression tracking', () => {
+		it( 'should fire shipping_partner_impression when entering label_printing step with partners', () => {
+			const shippingPartners = [
+				{
+					id: 'woocommerce-shipping',
+					name: 'WooCommerce Shipping',
+					slug: 'woocommerce-shipping',
+				},
+				{
+					id: 'shipstation',
+					name: 'ShipStation',
+					slug: 'woocommerce-shipstation-integration',
+				},
+			];
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+			} );
+
+			// Simulate componentDidMount
+			component.setState = jest.fn();
+			component.state = { ...component.state, step: 'store_location' };
+
+			// Simulate stepping to label_printing
+			component.componentDidUpdate(
+				{ ...props, shippingPartners },
+				{ step: 'rates' }
+			);
+
+			// Should not fire yet because step is store_location, not label_printing
+			expect( recordEvent ).not.toHaveBeenCalledWith(
+				'shipping_partner_impression',
+				expect.anything()
+			);
+
+			// Now simulate the step being label_printing
+			component.state = { ...component.state, step: 'label_printing' };
+			component.componentDidUpdate(
+				{ ...props, shippingPartners },
+				{ step: 'rates' }
+			);
+
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_impression',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins:
+						'woocommerce-shipping,woocommerce-shipstation-integration',
+				}
+			);
+		} );
+
+		it( 'should not fire shipping_partner_impression when there are no shipping partners', () => {
+			( recordEvent as jest.Mock ).mockClear();
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners: [],
+			} );
+
+			component.setState = jest.fn();
+			component.state = { ...component.state, step: 'label_printing' };
+
+			component.componentDidUpdate(
+				{ ...props, shippingPartners: [] },
+				{ step: 'rates' }
+			);
+
+			expect( recordEvent ).not.toHaveBeenCalledWith(
+				'shipping_partner_impression',
+				expect.anything()
+			);
+		} );
+
+		it( 'should only fire shipping_partner_impression once', () => {
+			( recordEvent as jest.Mock ).mockClear();
+
+			const shippingPartners = [
+				{
+					id: 'woocommerce-shipping',
+					name: 'WooCommerce Shipping',
+					slug: 'woocommerce-shipping',
+				},
+			];
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+			} );
+
+			component.setState = jest.fn();
+			component.state = { ...component.state, step: 'label_printing' };
+
+			// First transition into label_printing
+			component.componentDidUpdate(
+				{ ...props, shippingPartners },
+				{ step: 'rates' }
+			);
+
+			// Second transition (e.g., re-entering)
+			component.componentDidUpdate(
+				{ ...props, shippingPartners },
+				{ step: 'rates' }
+			);
+
+			const impressionCalls = (
+				recordEvent as jest.Mock
+			 ).mock.calls.filter(
+				( call ) => call[ 0 ] === 'shipping_partner_impression'
+			);
+			expect( impressionCalls ).toHaveLength( 1 );
+		} );
+	} );
+
+	describe( 'recordInstallAndActivateEvents', () => {
+		const shippingPartners = [
+			{
+				id: 'woocommerce-shipping',
+				name: 'WooCommerce Shipping',
+				slug: 'woocommerce-shipping',
+			},
+		];
+
+		it( 'should fire both install and activate success events on success', () => {
+			( recordEvent as jest.Mock ).mockClear();
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+				installedPlugins: [],
+			} );
+
+			component.recordInstallAndActivateEvents(
+				'woocommerce-shipping',
+				true
+			);
+
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_install',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins: 'woocommerce-shipping',
+					selected_plugin: 'woocommerce-shipping',
+					success: true,
+				}
+			);
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_activate',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins: 'woocommerce-shipping',
+					selected_plugin: 'woocommerce-shipping',
+					success: true,
+				}
+			);
+		} );
+
+		it( 'should fire install failure only when plugin was not installed', () => {
+			( recordEvent as jest.Mock ).mockClear();
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+				installedPlugins: [],
+			} );
+
+			component.recordInstallAndActivateEvents(
+				'woocommerce-shipping',
+				false
+			);
+
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_install',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins: 'woocommerce-shipping',
+					selected_plugin: 'woocommerce-shipping',
+					success: false,
+				}
+			);
+			expect( recordEvent ).not.toHaveBeenCalledWith(
+				'shipping_partner_activate',
+				expect.anything()
+			);
+		} );
+
+		it( 'should fire install success and activate failure when plugin was installed but activation failed', () => {
+			( recordEvent as jest.Mock ).mockClear();
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+				installedPlugins: [ 'woocommerce-shipping' ],
+			} );
+
+			component.recordInstallAndActivateEvents(
+				'woocommerce-shipping',
+				false
+			);
+
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_install',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins: 'woocommerce-shipping',
+					selected_plugin: 'woocommerce-shipping',
+					success: true,
+				}
+			);
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'shipping_partner_activate',
+				{
+					context: 'tasklist',
+					country: 'US',
+					plugins: 'woocommerce-shipping',
+					selected_plugin: 'woocommerce-shipping',
+					success: false,
+				}
+			);
+		} );
+	} );
+
+	describe( 'getShippingPartnerTrackingProps', () => {
+		it( 'should return correct tracking props', () => {
+			const shippingPartners = [
+				{
+					id: 'woocommerce-shipping',
+					name: 'WooCommerce Shipping',
+					slug: 'woocommerce-shipping',
+				},
+				{
+					id: 'shipstation',
+					name: 'ShipStation',
+					slug: 'woocommerce-shipstation-integration',
+				},
+			];
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+			} );
+
+			const trackingProps = component.getShippingPartnerTrackingProps();
+			expect( trackingProps ).toEqual( {
+				context: 'tasklist',
+				country: 'US',
+				plugins:
+					'woocommerce-shipping,woocommerce-shipstation-integration',
+			} );
+		} );
+
+		it( 'should filter out partners without slugs', () => {
+			const shippingPartners = [
+				{
+					id: 'woocommerce-shipping',
+					name: 'WooCommerce Shipping',
+					slug: 'woocommerce-shipping',
+				},
+				{
+					id: 'envia',
+					name: 'Envia',
+					slug: '',
+				},
+			];
+
+			const component = new Shipping( {
+				...props,
+				shippingPartners,
+			} );
+
+			const trackingProps = component.getShippingPartnerTrackingProps();
+			expect( trackingProps ).toEqual( {
+				context: 'tasklist',
+				country: 'US',
+				plugins: 'woocommerce-shipping',
+			} );
+		} );
+	} );
 } );