Commit da01771f612 for woocommerce

commit da01771f612799b6ea36efb26f2d8685fbe17bcf
Author: Jason Kytros <jason.kytros@automattic.com>
Date:   Fri Jun 26 11:47:42 2026 +0300

    Add recommended tax solutions to tax settings (#65786)

    * Add tax extension recommendations to settings

    * Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin

    * Add tax recommendation tracking events

    * Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin

    * Remove duplicate tax settings changefiles

    * Remove recommended badge from tax recommendations

    * Update recommendation tests to await async clicks

    * Fix recommendation test lint errors

    * Remove shipping test changes from PR

    * Fix tax recommendation plugin aliases and logo

    * Use WordPress.org Anrok logo in tax recommendations

    * Remove unused tax recommendation badge remnants

    * Filter and track tax recommendations by country

    * Normalize tax recommendation country checks

    * Add active state coverage for tax recommendations

    * Updated copy

    * Updated Anrok copy

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/65786-tax-settings-recommendations b/plugins/woocommerce/changelog/65786-tax-settings-recommendations
new file mode 100644
index 00000000000..b97e6860e41
--- /dev/null
+++ b/plugins/woocommerce/changelog/65786-tax-settings-recommendations
@@ -0,0 +1,4 @@
+Significance: patch
+Type: add
+
+Added Tax extensions recommendations.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/client/embedded-body-layout/embedded-body-layout.tsx b/plugins/woocommerce/client/admin/client/embedded-body-layout/embedded-body-layout.tsx
index 63a85518b1d..7be6d6b5905 100644
--- a/plugins/woocommerce/client/admin/client/embedded-body-layout/embedded-body-layout.tsx
+++ b/plugins/woocommerce/client/admin/client/embedded-body-layout/embedded-body-layout.tsx
@@ -15,6 +15,7 @@ import QueryString, { parse } from 'qs';
  */
 import { PaymentRecommendations } from '../payments';
 import { ShippingRecommendations } from '../shipping';
+import { TaxRecommendations } from '../tax';
 import { AbandonedCartRecoveryRecommendations } from '../abandoned-cart-recovery';
 import { EmbeddedBodyProps } from './embedded-body-props';
 import './style.scss';
@@ -30,6 +31,7 @@ function isWPPage(
 const EMBEDDED_BODY_COMPONENT_LIST: React.ElementType[] = [
 	PaymentRecommendations,
 	ShippingRecommendations,
+	TaxRecommendations,
 	AbandonedCartRecoveryRecommendations,
 ];

diff --git a/plugins/woocommerce/client/admin/client/task-lists/fills/tax/utils.ts b/plugins/woocommerce/client/admin/client/task-lists/fills/tax/utils.ts
index 7752dbdba05..087dbee3b9f 100644
--- a/plugins/woocommerce/client/admin/client/task-lists/fills/tax/utils.ts
+++ b/plugins/woocommerce/client/admin/client/task-lists/fills/tax/utils.ts
@@ -9,6 +9,45 @@ import { TaskType } from '@woocommerce/data';
  */
 export const AUTOMATION_PLUGINS = [ 'woocommerce-services' ];

+/**
+ * Countries where WooCommerce Tax automated taxes are supported.
+ *
+ * Mirrors the support gate used for automated taxes in core.
+ */
+export const WOOCOMMERCE_TAX_SUPPORTED_COUNTRIES = [
+	'US',
+	'CA',
+	'AU',
+	'GB',
+	'AT',
+	'BE',
+	'BG',
+	'HR',
+	'CY',
+	'CZ',
+	'DK',
+	'EE',
+	'FI',
+	'FR',
+	'DE',
+	'GR',
+	'HU',
+	'IE',
+	'IT',
+	'LV',
+	'LT',
+	'LU',
+	'MT',
+	'NL',
+	'PL',
+	'PT',
+	'RO',
+	'SK',
+	'SI',
+	'ES',
+	'SE',
+];
+
 /**
  * Check if a store has a complete address given general settings.
  *
@@ -53,6 +92,24 @@ export type TaxChildProps = {
 	children?: React.ReactNode;
 };

+/**
+ * Check if WooCommerce Tax is supported for a given store country.
+ *
+ * @param {string|null} countryCode Country code.
+ * @return {boolean} If WooCommerce Tax is supported.
+ */
+export const supportsWooCommerceTax = (
+	countryCode: string | null
+): boolean => {
+	if ( ! countryCode ) {
+		return false;
+	}
+
+	return WOOCOMMERCE_TAX_SUPPORTED_COUNTRIES.includes(
+		countryCode.trim().toUpperCase()
+	);
+};
+
 /**
  * Check if a given country is supported by Avalara.
  *
diff --git a/plugins/woocommerce/client/admin/client/tax/index.ts b/plugins/woocommerce/client/admin/client/tax/index.ts
new file mode 100644
index 00000000000..fe605d9e621
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/index.ts
@@ -0,0 +1 @@
+export * from './tax-recommendations-wrapper';
diff --git a/plugins/woocommerce/client/admin/client/tax/tax-recommendations-wrapper.tsx b/plugins/woocommerce/client/admin/client/tax/tax-recommendations-wrapper.tsx
new file mode 100644
index 00000000000..734e260b578
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/tax-recommendations-wrapper.tsx
@@ -0,0 +1,43 @@
+/**
+ * External dependencies
+ */
+import { lazy, Suspense } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { EmbeddedBodyProps } from '../embedded-body-layout/embedded-body-props';
+import RecommendationsEligibilityWrapper from '../settings-recommendations/recommendations-eligibility-wrapper';
+
+const TaxRecommendationsLoader = lazy(
+	() =>
+		import(
+			/* webpackChunkName: "tax-recommendations" */ './tax-recommendations'
+		)
+);
+
+export const TaxRecommendations = ( {
+	page,
+	tab,
+	section,
+}: EmbeddedBodyProps ) => {
+	if ( page !== 'wc-settings' ) {
+		return null;
+	}
+
+	if ( tab !== 'tax' ) {
+		return null;
+	}
+
+	if ( Boolean( section ) ) {
+		return null;
+	}
+
+	return (
+		<RecommendationsEligibilityWrapper>
+			<Suspense fallback={ null }>
+				<TaxRecommendationsLoader />
+			</Suspense>
+		</RecommendationsEligibilityWrapper>
+	);
+};
diff --git a/plugins/woocommerce/client/admin/client/tax/tax-recommendations.scss b/plugins/woocommerce/client/admin/client/tax/tax-recommendations.scss
new file mode 100644
index 00000000000..5a3ffe1dea0
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/tax-recommendations.scss
@@ -0,0 +1,43 @@
+.woocommerce-recommended-tax-extensions-wrapper {
+	padding-bottom: 60px;
+}
+
+.woocommerce-recommended-tax-extensions {
+	.woocommerce-list__item {
+		> .woocommerce-list__item-inner {
+			align-items: flex-start;
+		}
+
+		&:hover {
+			background-color: $white;
+
+			.woocommerce-list__item-title {
+				color: $gray-900;
+			}
+		}
+	}
+
+	.woocommerce-list__item-title {
+		font-size: 14px;
+		color: $gray-900;
+		font-weight: 600;
+	}
+
+	.woocommerce-list__item-after .components-button {
+		margin-left: $gap-small;
+	}
+
+	.woocommerce-list__item-text,
+	.woocommerce-recommended-tax__header-heading {
+		max-width: 749px;
+	}
+}
+
+.woocommerce-tax-recommendation-item {
+	&__logo {
+		display: block;
+		width: 36px;
+		height: 36px;
+		object-fit: contain;
+	}
+}
diff --git a/plugins/woocommerce/client/admin/client/tax/tax-recommendations.tsx b/plugins/woocommerce/client/admin/client/tax/tax-recommendations.tsx
new file mode 100644
index 00000000000..ec01f0e180d
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/tax-recommendations.tsx
@@ -0,0 +1,385 @@
+/**
+ * External dependencies
+ */
+import { Button, CardFooter, ExternalLink } from '@wordpress/components';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { Children, useEffect, useRef, useState } from '@wordpress/element';
+import { __, sprintf } from '@wordpress/i18n';
+import { Text } from '@woocommerce/experimental';
+import { PluginNames, pluginsStore, settingsStore } from '@woocommerce/data';
+import { getAdminLink } from '@woocommerce/settings';
+import { recordEvent } from '@woocommerce/tracks';
+
+/**
+ * Internal dependencies
+ */
+import taxLogo from '../task-lists/fills/tax/woocommerce-tax/logo.png';
+import { createNoticesFromResponse } from '../lib/notices';
+import {
+	DismissableList,
+	DismissableListHeading,
+} from '../settings-recommendations/dismissable-list';
+import { supportsWooCommerceTax } from '../task-lists/fills/tax/utils';
+import { TrackedLink } from '~/components/tracked-link/tracked-link';
+import { getCountryCode } from '~/dashboard/utils';
+import './tax-recommendations.scss';
+
+const ANROK_LOGO_URL = 'https://ps.w.org/anrok-tax/assets/icon.svg';
+
+type TaxRecommendation = {
+	id: 'anrok-tax' | 'woocommerce-tax';
+	title: string;
+	description: string;
+	productUrl: string;
+	logo: React.ReactNode;
+	pluginSlugs: string[];
+};
+
+const useInstallPlugin = () => {
+	const [ pluginsBeingSetup, setPluginsBeingSetup ] = useState<
+		Array< string >
+	>( [] );
+
+	const { installPlugins, activatePlugins } = useDispatch( pluginsStore );
+
+	const handleInstall = ( slugs: string[] ): PromiseLike< void > => {
+		if ( pluginsBeingSetup.length > 0 ) {
+			return Promise.resolve();
+		}
+
+		setPluginsBeingSetup( slugs );
+
+		return installPlugins( slugs as Partial< PluginNames >[] )
+			.then( () => {
+				setPluginsBeingSetup( [] );
+			} )
+			.catch( ( response: { errors: Record< string, string > } ) => {
+				createNoticesFromResponse( response );
+				setPluginsBeingSetup( [] );
+
+				return Promise.reject();
+			} );
+	};
+
+	const handleActivate = ( slugs: string[] ): PromiseLike< void > => {
+		if ( pluginsBeingSetup.length > 0 ) {
+			return Promise.resolve();
+		}
+
+		setPluginsBeingSetup( slugs );
+
+		return activatePlugins( slugs as Partial< PluginNames >[] )
+			.then( () => {
+				setPluginsBeingSetup( [] );
+			} )
+			.catch( ( response: { errors: Record< string, string > } ) => {
+				createNoticesFromResponse( response );
+				setPluginsBeingSetup( [] );
+
+				return Promise.reject();
+			} );
+	};
+
+	return [ pluginsBeingSetup, handleInstall, handleActivate ] as const;
+};
+
+const TaxRecommendationItem = ( {
+	pluginSlug,
+	isPluginInstalled,
+	isPluginActive,
+	pluginsBeingSetup,
+	onInstallClick,
+	onActivateClick,
+	title,
+	description,
+	productUrl,
+	logo,
+}: TaxRecommendation & {
+	pluginSlug: string;
+	isPluginInstalled: boolean;
+	isPluginActive: boolean;
+	pluginsBeingSetup: Array< string >;
+	onInstallClick: ( slugs: string[] ) => PromiseLike< void >;
+	onActivateClick: ( slugs: string[] ) => PromiseLike< void >;
+} ) => {
+	const { createSuccessNotice } = useDispatch( 'core/notices' );
+
+	const handleLearnMoreClick = () => {
+		recordEvent( 'settings_tax_recommendation_click', {
+			extension: title,
+		} );
+	};
+
+	const handleClick = () => {
+		const trackingBase = {
+			context: 'settings',
+			selected_plugin: pluginSlug,
+		};
+
+		recordEvent( 'tax_partner_click', trackingBase );
+		recordEvent( 'settings_tax_recommendation_setup_click', {
+			plugin: pluginSlug,
+			action: isPluginInstalled ? 'activate' : 'install',
+		} );
+
+		const action = isPluginInstalled ? onActivateClick : onInstallClick;
+		const eventName = isPluginInstalled
+			? 'tax_partner_activate'
+			: 'tax_partner_install';
+
+		action( [ pluginSlug ] ).then(
+			() => {
+				recordEvent( eventName, {
+					...trackingBase,
+					success: true,
+				} );
+				createSuccessNotice(
+					isPluginInstalled
+						? sprintf(
+								/* translators: %s: extension name. */
+								__( '%s activated!', 'woocommerce' ),
+								title
+						  )
+						: sprintf(
+								/* translators: %s: extension name. */
+								__( '%s is installed!', 'woocommerce' ),
+								title
+						  ),
+					{}
+				);
+			},
+			() => {
+				recordEvent( eventName, {
+					...trackingBase,
+					success: false,
+				} );
+			}
+		);
+	};
+
+	return (
+		<div className="woocommerce-list__item-inner woocommerce-tax-recommendation-item">
+			<div className="woocommerce-list__item-before">{ logo }</div>
+			<div className="woocommerce-list__item-text">
+				<span className="woocommerce-list__item-title">{ title }</span>
+				<span className="woocommerce-list__item-content">
+					{ description }
+					<br />
+					<ExternalLink
+						href={ productUrl }
+						onClick={ handleLearnMoreClick }
+					>
+						{ __( 'Learn more', 'woocommerce' ) }
+					</ExternalLink>
+				</span>
+			</div>
+			<div className="woocommerce-list__item-after">
+				{ isPluginActive ? (
+					<Button
+						variant="secondary"
+						aria-disabled="true"
+						aria-label={ sprintf(
+							/* translators: %s: extension name. */
+							__( '%s is already active', 'woocommerce' ),
+							title
+						) }
+					>
+						{ __( 'Active', 'woocommerce' ) }
+					</Button>
+				) : (
+					<Button
+						variant={ isPluginInstalled ? 'primary' : 'secondary' }
+						onClick={ handleClick }
+						isBusy={ pluginsBeingSetup.includes( pluginSlug ) }
+						disabled={ pluginsBeingSetup.length > 0 }
+					>
+						{ isPluginInstalled
+							? __( 'Activate', 'woocommerce' )
+							: __( 'Install', 'woocommerce' ) }
+					</Button>
+				) }
+			</div>
+		</div>
+	);
+};
+
+const TaxRecommendationsList = ( {
+	children,
+}: {
+	children: React.ReactNode;
+} ) => (
+	<DismissableList
+		className="woocommerce-recommended-tax-extensions"
+		dismissOptionName="woocommerce_settings_tax_recommendations_hidden"
+	>
+		<DismissableListHeading>
+			<Text variant="title.small" as="p" size="20" lineHeight="28px">
+				{ __( 'Recommended tax solutions', 'woocommerce' ) }
+			</Text>
+			<Text
+				className="woocommerce-recommended-tax__header-heading"
+				variant="caption"
+				as="p"
+				size="12"
+				lineHeight="16px"
+			>
+				{ __(
+					'Explore tax extensions that can help automate calculations and compliance for your store.',
+					'woocommerce'
+				) }
+			</Text>
+		</DismissableListHeading>
+		<ul className="woocommerce-list">
+			{ Children.map( children, ( item ) => (
+				<li className="woocommerce-list__item">{ item }</li>
+			) ) }
+		</ul>
+		<CardFooter>
+			<TrackedLink
+				message={ __(
+					// translators: {{Link}} is a placeholder for a html element.
+					'Visit {{Link}}the WooCommerce Marketplace{{/Link}} to find more tax solutions.',
+					'woocommerce'
+				) }
+				targetUrl={ getAdminLink(
+					'admin.php?page=wc-admin&tab=extensions&path=/extensions&category=operations'
+				) }
+				linkType="wc-admin"
+				eventName="settings_tax_recommendation_visit_marketplace_click"
+			/>
+		</CardFooter>
+	</DismissableList>
+);
+
+const getPluginSlugForAction = (
+	pluginSlugs: string[],
+	installedPlugins: string[],
+	activePlugins: string[]
+) =>
+	pluginSlugs.find(
+		( pluginSlug ) =>
+			activePlugins.includes( pluginSlug ) ||
+			installedPlugins.includes( pluginSlug )
+	) ?? pluginSlugs[ 0 ];
+
+const TaxRecommendations = () => {
+	const [ pluginsBeingSetup, handleInstall, handleActivate ] =
+		useInstallPlugin();
+	const { activePlugins, installedPlugins, countryCode } = useSelect(
+		( select ) => {
+			const settings = select( settingsStore ).getSettings( 'general' );
+			const { getActivePlugins, getInstalledPlugins } =
+				select( pluginsStore );
+
+			return {
+				activePlugins: getActivePlugins() ?? [],
+				installedPlugins: getInstalledPlugins() ?? [],
+				countryCode: getCountryCode(
+					settings.general?.woocommerce_default_country
+				),
+			};
+		},
+		[]
+	) ?? {
+		activePlugins: [],
+		installedPlugins: [],
+		countryCode: '',
+	};
+
+	const recommendations: TaxRecommendation[] = [
+		{
+			id: 'woocommerce-tax',
+			title: __( 'WooCommerce Tax', 'woocommerce' ),
+			description: __(
+				'Free, one-click tool to automate essential sales tax on every WooCommerce order.',
+				'woocommerce'
+			),
+			productUrl: 'https://woocommerce.com/products/tax/',
+			pluginSlugs: [ 'woocommerce-services', 'woocommerce-tax' ],
+			logo: (
+				<img
+					className="woocommerce-tax-recommendation-item__logo"
+					src={ taxLogo }
+					alt=""
+				/>
+			),
+		},
+		{
+			id: 'anrok-tax',
+			title: __( 'Anrok', 'woocommerce' ),
+			description: __(
+				'Advanced tax compliance for growing brands selling within the US and around the globe.',
+				'woocommerce'
+			),
+			productUrl: 'https://woocommerce.com/products/anrok-tax/',
+			pluginSlugs: [ 'anrok-tax' ],
+			logo: (
+				<img
+					className="woocommerce-tax-recommendation-item__logo"
+					src={ ANROK_LOGO_URL }
+					alt=""
+				/>
+			),
+		},
+	];
+	const visibleRecommendations = recommendations.filter(
+		( recommendation ) =>
+			recommendation.id === 'anrok-tax' ||
+			supportsWooCommerceTax( countryCode )
+	);
+	const visiblePluginSlugs = visibleRecommendations
+		.map( ( recommendation ) => recommendation.pluginSlugs[ 0 ] )
+		.join( ',' );
+	const impressionFired = useRef( false );
+
+	useEffect( () => {
+		if (
+			countryCode &&
+			visibleRecommendations.length > 0 &&
+			! impressionFired.current
+		) {
+			recordEvent( 'tax_partner_impression', {
+				context: 'settings',
+				country: countryCode,
+				plugins: visiblePluginSlugs,
+			} );
+			impressionFired.current = true;
+		}
+	}, [ countryCode, visiblePluginSlugs, visibleRecommendations.length ] );
+
+	return (
+		<div className="woocommerce-recommended-tax-extensions-wrapper">
+			<TaxRecommendationsList>
+				{ visibleRecommendations.map( ( recommendation ) => {
+					const isPluginActive = recommendation.pluginSlugs.some(
+						( pluginSlug ) => activePlugins.includes( pluginSlug )
+					);
+					const isPluginInstalled =
+						isPluginActive ||
+						recommendation.pluginSlugs.some( ( pluginSlug ) =>
+							installedPlugins.includes( pluginSlug )
+						);
+
+					return (
+						<TaxRecommendationItem
+							key={ recommendation.id }
+							pluginSlug={ getPluginSlugForAction(
+								recommendation.pluginSlugs,
+								installedPlugins,
+								activePlugins
+							) }
+							isPluginInstalled={ isPluginInstalled }
+							isPluginActive={ isPluginActive }
+							pluginsBeingSetup={ pluginsBeingSetup }
+							onInstallClick={ handleInstall }
+							onActivateClick={ handleActivate }
+							{ ...recommendation }
+						/>
+					);
+				} ) }
+			</TaxRecommendationsList>
+		</div>
+	);
+};
+
+export default TaxRecommendations;
diff --git a/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations-wrapper.test.tsx b/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations-wrapper.test.tsx
new file mode 100644
index 00000000000..ea0c4d6fd86
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations-wrapper.test.tsx
@@ -0,0 +1,134 @@
+/**
+ * External dependencies
+ */
+import { render } from '@testing-library/react';
+import { useSelect } from '@wordpress/data';
+import { useUser } from '@woocommerce/data';
+
+/**
+ * Internal dependencies
+ */
+import { TaxRecommendations } from '../tax-recommendations-wrapper';
+
+jest.mock( '@wordpress/data', () => ( {
+	...jest.requireActual( '@wordpress/data' ),
+	useSelect: jest.fn(),
+} ) );
+
+jest.mock( '@woocommerce/data', () => ( {
+	...jest.requireActual( '@woocommerce/data' ),
+	useUser: jest.fn(),
+} ) );
+
+jest.mock( '@wordpress/element', () => ( {
+	...jest.requireActual( '@wordpress/element' ),
+	Suspense: () => <div>Recommended tax solutions</div>,
+} ) );
+
+describe( 'TaxRecommendations', () => {
+	beforeEach( () => {
+		( useSelect as jest.Mock ).mockImplementation( ( fn ) =>
+			fn( () => ( {
+				getOption: () => 'yes',
+				hasFinishedResolution: () => true,
+			} ) )
+		);
+
+		( useUser as jest.Mock ).mockReturnValue( {
+			currentUserCan: () => true,
+		} );
+	} );
+
+	it( 'should not render when page is not wc-settings', () => {
+		const { queryByText } = render(
+			<TaxRecommendations
+				page="wc-admin"
+				tab="tax"
+				section={ undefined }
+			/>
+		);
+
+		expect(
+			queryByText( 'Recommended tax solutions' )
+		).not.toBeInTheDocument();
+	} );
+
+	it( 'should not render when tab is not tax', () => {
+		const { queryByText } = render(
+			<TaxRecommendations
+				page="wc-settings"
+				tab="shipping"
+				section={ undefined }
+			/>
+		);
+
+		expect(
+			queryByText( 'Recommended tax solutions' )
+		).not.toBeInTheDocument();
+	} );
+
+	it( 'should not render when section is not empty', () => {
+		const { queryByText } = render(
+			<TaxRecommendations
+				page="wc-settings"
+				tab="tax"
+				section="standard"
+			/>
+		);
+
+		expect(
+			queryByText( 'Recommended tax solutions' )
+		).not.toBeInTheDocument();
+	} );
+
+	it( 'should not render when marketplace suggestions are disabled', () => {
+		( useSelect as jest.Mock ).mockImplementation( ( fn ) =>
+			fn( () => ( {
+				getOption: () => 'no',
+				hasFinishedResolution: () => true,
+			} ) )
+		);
+
+		const { queryByText } = render(
+			<TaxRecommendations
+				page="wc-settings"
+				tab="tax"
+				section={ undefined }
+			/>
+		);
+
+		expect(
+			queryByText( 'Recommended tax solutions' )
+		).not.toBeInTheDocument();
+	} );
+
+	it( 'should not render when the current user cannot install plugins', () => {
+		( useUser as jest.Mock ).mockReturnValue( {
+			currentUserCan: () => false,
+		} );
+
+		const { queryByText } = render(
+			<TaxRecommendations
+				page="wc-settings"
+				tab="tax"
+				section={ undefined }
+			/>
+		);
+
+		expect(
+			queryByText( 'Recommended tax solutions' )
+		).not.toBeInTheDocument();
+	} );
+
+	it( 'should render on the default tax settings section', () => {
+		const { getByText } = render(
+			<TaxRecommendations
+				page="wc-settings"
+				tab="tax"
+				section={ undefined }
+			/>
+		);
+
+		expect( getByText( 'Recommended tax solutions' ) ).toBeInTheDocument();
+	} );
+} );
diff --git a/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations.test.tsx b/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations.test.tsx
new file mode 100644
index 00000000000..503940ec45c
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/tax/test/tax-recommendations.test.tsx
@@ -0,0 +1,337 @@
+/**
+ * External dependencies
+ */
+import { act, render, screen, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { recordEvent } from '@woocommerce/tracks';
+
+/**
+ * Internal dependencies
+ */
+import TaxRecommendations from '../tax-recommendations';
+
+jest.mock( '@wordpress/data', () => ( {
+	...jest.requireActual( '@wordpress/data' ),
+	useDispatch: jest.fn(),
+	useSelect: jest.fn(),
+} ) );
+
+jest.mock( '@woocommerce/tracks', () => ( {
+	recordEvent: jest.fn(),
+} ) );
+
+jest.mock( '~/components/tracked-link/tracked-link', () => ( {
+	TrackedLink: ( { message } ) => <div>{ message }</div>,
+} ) );
+
+jest.mock( '../../settings-recommendations/dismissable-list', () => ( {
+	DismissableList: ( { children } ) => children,
+	DismissableListHeading: ( { children } ) => children,
+} ) );
+
+jest.mock( '../../lib/notices', () => ( {
+	createNoticesFromResponse: () => null,
+} ) );
+
+const clickAndFlush = async ( element: Element ) => {
+	await act( async () => {
+		await userEvent.click( element );
+		await Promise.resolve();
+	} );
+};
+
+describe( 'TaxRecommendations', () => {
+	const installPluginsMock = jest.fn().mockResolvedValue( undefined );
+	const activatePluginsMock = jest.fn().mockResolvedValue( undefined );
+	const createSuccessNoticeMock = jest.fn();
+	let installedPlugins: string[] = [];
+	let activePlugins: string[] = [];
+	let countryCode = 'US';
+
+	beforeEach( () => {
+		installPluginsMock.mockClear();
+		activatePluginsMock.mockClear();
+		createSuccessNoticeMock.mockClear();
+		( recordEvent as jest.Mock ).mockClear();
+		installedPlugins = [];
+		activePlugins = [];
+		countryCode = 'US';
+
+		( useSelect as jest.Mock ).mockImplementation( ( fn ) =>
+			fn( () => ( {
+				getSettings: () => ( {
+					general: {
+						woocommerce_default_country: countryCode,
+					},
+				} ),
+				getInstalledPlugins: () => installedPlugins,
+				getActivePlugins: () => activePlugins,
+			} ) )
+		);
+
+		( useDispatch as jest.Mock ).mockImplementation( ( store ) => {
+			if ( store === 'core/notices' ) {
+				return {
+					createSuccessNotice: createSuccessNoticeMock,
+				};
+			}
+
+			return {
+				installPlugins: installPluginsMock,
+				activatePlugins: activatePluginsMock,
+			};
+		} );
+	} );
+
+	it( 'renders WooCommerce Tax and Anrok with install buttons when no related plugins are present', () => {
+		render( <TaxRecommendations /> );
+		expect( screen.getByText( 'WooCommerce Tax' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Anrok' ) ).toBeInTheDocument();
+		expect( screen.getAllByText( 'Install' ) ).toHaveLength( 2 );
+	} );
+
+	it( 'shows Activate when Anrok is installed but inactive', () => {
+		installedPlugins = [ 'anrok-tax' ];
+
+		render( <TaxRecommendations /> );
+
+		expect( screen.getByText( 'WooCommerce Tax' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Anrok' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Activate' ) ).toBeInTheDocument();
+	} );
+
+	it( 'shows a disabled Active button when Anrok is already active', async () => {
+		activePlugins = [ 'anrok-tax' ];
+
+		render( <TaxRecommendations /> );
+
+		const activeButton = screen.getByRole( 'button', {
+			name: 'Anrok is already active',
+		} );
+
+		expect( activeButton ).toHaveTextContent( 'Active' );
+		expect( activeButton ).toHaveAttribute( 'aria-disabled', 'true' );
+
+		await clickAndFlush( activeButton );
+
+		expect( installPluginsMock ).not.toHaveBeenCalled();
+		expect( activatePluginsMock ).not.toHaveBeenCalled();
+	} );
+
+	it( 'shows Active for WooCommerce Tax when the services alias is active', () => {
+		activePlugins = [ 'woocommerce-services' ];
+
+		render( <TaxRecommendations /> );
+
+		expect(
+			screen.getByRole( 'button', {
+				name: 'WooCommerce Tax is already active',
+			} )
+		).toHaveTextContent( 'Active' );
+	} );
+
+	it( 'renders only Anrok for unsupported countries', () => {
+		countryCode = 'BR';
+
+		render( <TaxRecommendations /> );
+
+		expect(
+			screen.queryByText( 'WooCommerce Tax' )
+		).not.toBeInTheDocument();
+		expect( screen.getByText( 'Anrok' ) ).toBeInTheDocument();
+		expect( screen.getByText( 'Install' ) ).toBeInTheDocument();
+	} );
+
+	it( 'fires tax_partner_impression with both recommendations for supported countries', () => {
+		render( <TaxRecommendations /> );
+
+		expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_impression', {
+			context: 'settings',
+			country: 'US',
+			plugins: 'woocommerce-services,anrok-tax',
+		} );
+	} );
+
+	it( 'fires tax_partner_impression with only Anrok for unsupported countries', () => {
+		countryCode = 'BR';
+
+		render( <TaxRecommendations /> );
+
+		expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_impression', {
+			context: 'settings',
+			country: 'BR',
+			plugins: 'anrok-tax',
+		} );
+	} );
+
+	it( 'does not fire tax_partner_impression before the store country is available', () => {
+		countryCode = '';
+
+		render( <TaxRecommendations /> );
+
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'tax_partner_impression',
+			expect.anything()
+		);
+	} );
+
+	it( 'installs WooCommerce Tax using the WooCommerce Services slug', async () => {
+		render( <TaxRecommendations /> );
+
+		const wooCommerceTaxItem = screen
+			.getByText( 'WooCommerce Tax' )
+			.closest( '.woocommerce-list__item' );
+
+		expect( wooCommerceTaxItem ).not.toBeNull();
+
+		await clickAndFlush(
+			within( wooCommerceTaxItem as HTMLElement ).getByRole( 'button', {
+				name: 'Install',
+			} )
+		);
+
+		await waitFor( () => {
+			expect( installPluginsMock ).toHaveBeenCalledWith( [
+				'woocommerce-services',
+			] );
+		} );
+
+		expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_click', {
+			context: 'settings',
+			selected_plugin: 'woocommerce-services',
+		} );
+		expect( recordEvent ).toHaveBeenCalledWith(
+			'settings_tax_recommendation_setup_click',
+			{
+				plugin: 'woocommerce-services',
+				action: 'install',
+			}
+		);
+
+		await waitFor( () => {
+			expect( createSuccessNoticeMock ).toHaveBeenCalledWith(
+				'WooCommerce Tax is installed!',
+				expect.anything()
+			);
+		} );
+
+		await waitFor( () => {
+			expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_install', {
+				context: 'settings',
+				selected_plugin: 'woocommerce-services',
+				success: true,
+			} );
+		} );
+	} );
+
+	it( 'activates the installed WooCommerce Tax alias when one is already present', async () => {
+		installedPlugins = [ 'woocommerce-tax' ];
+
+		render( <TaxRecommendations /> );
+
+		const wooCommerceTaxItem = screen
+			.getByText( 'WooCommerce Tax' )
+			.closest( '.woocommerce-list__item' );
+
+		expect( wooCommerceTaxItem ).not.toBeNull();
+
+		await clickAndFlush(
+			within( wooCommerceTaxItem as HTMLElement ).getByRole( 'button', {
+				name: 'Activate',
+			} )
+		);
+
+		await waitFor( () => {
+			expect( activatePluginsMock ).toHaveBeenCalledWith( [
+				'woocommerce-tax',
+			] );
+		} );
+
+		expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_click', {
+			context: 'settings',
+			selected_plugin: 'woocommerce-tax',
+		} );
+		expect( recordEvent ).toHaveBeenCalledWith(
+			'settings_tax_recommendation_setup_click',
+			{
+				plugin: 'woocommerce-tax',
+				action: 'activate',
+			}
+		);
+
+		await waitFor( () => {
+			expect( createSuccessNoticeMock ).toHaveBeenCalledWith(
+				'WooCommerce Tax activated!',
+				expect.anything()
+			);
+		} );
+
+		await waitFor( () => {
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'tax_partner_activate',
+				{
+					context: 'settings',
+					selected_plugin: 'woocommerce-tax',
+					success: true,
+				}
+			);
+		} );
+	} );
+
+	it( 'records a failed tax_partner_install event when install fails', async () => {
+		installPluginsMock.mockRejectedValueOnce( undefined );
+
+		render( <TaxRecommendations /> );
+
+		const wooCommerceTaxItem = screen
+			.getByText( 'WooCommerce Tax' )
+			.closest( '.woocommerce-list__item' );
+
+		expect( wooCommerceTaxItem ).not.toBeNull();
+
+		await clickAndFlush(
+			within( wooCommerceTaxItem as HTMLElement ).getByRole( 'button', {
+				name: 'Install',
+			} )
+		);
+
+		await waitFor( () => {
+			expect( recordEvent ).toHaveBeenCalledWith( 'tax_partner_install', {
+				context: 'settings',
+				selected_plugin: 'woocommerce-services',
+				success: false,
+			} );
+		} );
+	} );
+
+	it( 'records a failed tax_partner_activate event when activation fails', async () => {
+		activatePluginsMock.mockRejectedValueOnce( undefined );
+		installedPlugins = [ 'anrok-tax' ];
+
+		render( <TaxRecommendations /> );
+
+		const anrokItem = screen
+			.getByText( 'Anrok' )
+			.closest( '.woocommerce-list__item' );
+
+		expect( anrokItem ).not.toBeNull();
+
+		await clickAndFlush(
+			within( anrokItem as HTMLElement ).getByRole( 'button', {
+				name: 'Activate',
+			} )
+		);
+
+		await waitFor( () => {
+			expect( recordEvent ).toHaveBeenCalledWith(
+				'tax_partner_activate',
+				{
+					context: 'settings',
+					selected_plugin: 'anrok-tax',
+					success: false,
+				}
+			);
+		} );
+	} );
+} );