Commit d569e1bbba8 for woocommerce

commit d569e1bbba868f3f7575c4e73eb478ca5674c854
Author: louwie17 <lourensschep@gmail.com>
Date:   Tue Nov 10 17:53:04 2026 +0100

    Hide dashboard status widget sales rows without Analytics, drop legacy report coupling (#70537)

    * Hide status widget sales rows without Analytics and drop legacy report coupling

    The WooCommerce Status dashboard widget no longer falls back to legacy
    reports when Analytics is off: the net sales and top seller rows are hidden,
    while order and stock rows stay. The woocommerce_dashboard_status_widget_reports
    filter still fires, so an extension can still supply sales data.

    The woocommerce_after_dashboard_status_widget_parameter filter now only builds
    a WC_Admin_Report when a callback listens to woocommerce_after_dashboard_status_widget,
    because extensions still call $reports->sales_sparkline() there.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_015zLZuyA6EbDi4AZPQLKroq

    * Fill in legacy status widget data when another callback hooks the reports filter

    A callback on woocommerce_dashboard_status_widget_reports that adjusts
    report_data->net_sales got null with Analytics off and crashed the widget.
    Legacy reports now fill in the sales data first when another callback is
    hooked, as on trunk; without one the sales rows stay hidden. Also document
    the filter's array shape and use string assertions in the tests.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_015zLZuyA6EbDi4AZPQLKroq

    * Pass the legacy report to other status widget parameter filter callbacks

    Callbacks on woocommerce_after_dashboard_status_widget_parameter that run
    after core received a WC_Admin_Report on trunk; build it for them too.
    Share the "another callback is hooked" check, and only reject Analytics
    links in the Analytics-off widget test, since the subscriptions row also
    links to wc-admin.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_015zLZuyA6EbDi4AZPQLKroq

    ---------

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/wooplug-7854-dashboard-status-widget-legacy-reports b/plugins/woocommerce/changelog/wooplug-7854-dashboard-status-widget-legacy-reports
new file mode 100644
index 00000000000..720fe39409d
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-7854-dashboard-status-widget-legacy-reports
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Hide the sales rows in the WooCommerce Status dashboard widget when Analytics is turned off, instead of using legacy reports.
diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-dashboard.php b/plugins/woocommerce/includes/admin/class-wc-admin-dashboard.php
index 2497ef7ee6e..98cdb81dc22 100644
--- a/plugins/woocommerce/includes/admin/class-wc-admin-dashboard.php
+++ b/plugins/woocommerce/includes/admin/class-wc-admin-dashboard.php
@@ -170,27 +170,47 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
 			//phpcs:ignore
 			$is_wc_admin_disabled = apply_filters( 'woocommerce_admin_disabled', false ) || ! FeaturesUtil::feature_is_enabled( 'analytics' );

-			$status_widget_reports = array(
-				'net_sales_link'      => 'admin.php?page=wc-admin&path=%2Fanalytics%2Frevenue&chart=net_revenue&orderby=net_revenue&period=month&compare=previous_period',
-				'top_seller_link'     => 'admin.php?page=wc-admin&filter=single_product&path=%2Fanalytics%2Fproducts&products=',
-				'lowstock_link'       => 'admin.php?page=wc-admin&type=lowstock&path=%2Fanalytics%2Fstock',
-				'outofstock_link'     => 'admin.php?page=wc-admin&type=outofstock&path=%2Fanalytics%2Fstock',
-				'report_data'         => null,
-				'get_sales_sparkline' => array( $this, 'get_sales_sparkline' ),
-			);
-
 			if ( $is_wc_admin_disabled ) {
+				$default_reports = array(
+					'net_sales_link'      => 'admin.php?page=wc-reports&tab=orders&range=month',
+					'top_seller_link'     => 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=',
+					'lowstock_link'       => 'admin.php?page=wc-reports&tab=stock&report=low_in_stock',
+					'outofstock_link'     => 'admin.php?page=wc-reports&tab=stock&report=out_of_stock',
+					'report_data'         => null,
+					'get_sales_sparkline' => null,
+				);
+
 				/**
-				 * Filter to change the reports of the status widget on the Dashboard page.
+				 * Filters the data of the status widget on the Dashboard page when Analytics is disabled.
 				 *
-				 * Please note that this filter is mainly for backward compatibility with the legacy reports.
-				 * It's not recommended to use this filter to change the data of this widget.
+				 * The net sales and top seller rows only show when `report_data` and `get_sales_sparkline` are both set.
+				 * They are null by default; when another callback is hooked here, legacy reports fill them in first.
+				 * This filter is kept for backward compatibility; it's not recommended for new code.
 				 *
 				 * @since 9.5.0
+				 *
+				 * @param array $status_widget_reports {
+				 *     Status widget data.
+				 *
+				 *     @type string        $net_sales_link      Admin URL path for the net sales row.
+				 *     @type string        $top_seller_link     Admin URL path for the top seller row; the product ID is appended.
+				 *     @type string        $lowstock_link       Admin URL path for the low in stock row.
+				 *     @type string        $outofstock_link     Admin URL path for the out of stock row.
+				 *     @type object|null   $report_data         Sales data with a `net_sales` property.
+				 *     @type callable|null $get_sales_sparkline Called with ( $product_id, $days, $type ) and returns an array with `total` and `data`.
+				 * }
 				 */
-				$status_widget_reports = apply_filters( 'woocommerce_dashboard_status_widget_reports', $status_widget_reports );
+				$status_widget_reports = apply_filters( 'woocommerce_dashboard_status_widget_reports', $default_reports );
+				$status_widget_reports = is_array( $status_widget_reports ) ? array_merge( $default_reports, $status_widget_reports ) : $default_reports;
 			} else {
-				$status_widget_reports['report_data'] = $this->get_wc_admin_performance_data();
+				$status_widget_reports = array(
+					'net_sales_link'      => 'admin.php?page=wc-admin&path=%2Fanalytics%2Frevenue&chart=net_revenue&orderby=net_revenue&period=month&compare=previous_period',
+					'top_seller_link'     => 'admin.php?page=wc-admin&filter=single_product&path=%2Fanalytics%2Fproducts&products=',
+					'lowstock_link'       => 'admin.php?page=wc-admin&type=lowstock&path=%2Fanalytics%2Fstock',
+					'outofstock_link'     => 'admin.php?page=wc-admin&type=outofstock&path=%2Fanalytics%2Fstock',
+					'report_data'         => $this->get_wc_admin_performance_data(),
+					'get_sales_sparkline' => array( $this, 'get_sales_sparkline' ),
+				);
 			}

 			echo '<ul class="wc_status_list">';
@@ -213,7 +233,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
 					),
 				);

-				if ( $report_data && is_callable( $get_sales_sparkline ) ) {
+				if ( $report_data && ! is_wp_error( $report_data ) && is_callable( $get_sales_sparkline ) ) {
 					$sparkline = call_user_func_array( $get_sales_sparkline, array( '', $days ) );
 					$sparkline = $this->sales_sparkline_markup( 'sales', $days, $sparkline['total'], $sparkline['data'] );
 					?>
@@ -232,8 +252,8 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
 					<?php
 				}

-				$top_seller = $this->get_top_seller();
-				if ( $top_seller && $top_seller->qty && is_callable( $get_sales_sparkline ) ) {
+				$top_seller = is_callable( $get_sales_sparkline ) ? $this->get_top_seller() : null;
+				if ( $top_seller && $top_seller->qty ) {
 					$sparkline = call_user_func_array( $get_sales_sparkline, array( $top_seller->product_id, $days, 'count' ) );
 					$sparkline = $this->sales_sparkline_markup( 'count', $days, $sparkline['total'], $sparkline['data'] );
 					?>
diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-reports.php b/plugins/woocommerce/includes/admin/class-wc-admin-reports.php
index b41721799c5..68b57757c02 100644
--- a/plugins/woocommerce/includes/admin/class-wc-admin-reports.php
+++ b/plugins/woocommerce/includes/admin/class-wc-admin-reports.php
@@ -29,8 +29,68 @@ class WC_Admin_Reports {
 	 * Register the hook handlers for integrating with admin.
 	 */
 	public static function register_hook_handlers() {
-		add_filter( 'woocommerce_after_dashboard_status_widget_parameter', array( __CLASS__, 'get_report_instance' ) );
-		add_filter( 'woocommerce_dashboard_status_widget_reports', array( __CLASS__, 'replace_dashboard_status_widget_reports' ) );
+		add_filter( 'woocommerce_after_dashboard_status_widget_parameter', array( __CLASS__, 'get_status_widget_report_instance' ) );
+		add_filter( 'woocommerce_dashboard_status_widget_reports', array( __CLASS__, 'maybe_replace_dashboard_status_widget_reports' ) );
+	}
+
+	/**
+	 * Fill in legacy sales data for the dashboard status widget, but only when another callback is hooked to the filter.
+	 *
+	 * Those callbacks were written against the legacy data, for example to adjust `report_data->net_sales`.
+	 *
+	 * @internal
+	 * @since 11.4.0
+	 *
+	 * @param mixed $status_widget_reports The data to display in the status widget.
+	 * @return mixed
+	 */
+	public static function maybe_replace_dashboard_status_widget_reports( $status_widget_reports ) {
+		if ( ! self::has_other_callbacks( 'woocommerce_dashboard_status_widget_reports' ) || ! is_array( $status_widget_reports ) ) {
+			return $status_widget_reports;
+		}
+
+		return self::replace_dashboard_status_widget_reports( $status_widget_reports );
+	}
+
+	/**
+	 * Provide a legacy report to the status widget hooks, but only when another callback can receive it.
+	 *
+	 * That is a listener on `woocommerce_after_dashboard_status_widget`, or another callback on this filter.
+	 *
+	 * @internal
+	 * @since 11.4.0
+	 *
+	 * @param mixed $reports The value passed to `woocommerce_after_dashboard_status_widget`.
+	 * @return mixed
+	 */
+	public static function get_status_widget_report_instance( $reports ) {
+		if ( ! has_action( 'woocommerce_after_dashboard_status_widget' ) && ! self::has_other_callbacks( 'woocommerce_after_dashboard_status_widget_parameter' ) ) {
+			return $reports;
+		}
+
+		return self::get_report_instance();
+	}
+
+	/**
+	 * Check whether a hook has callbacks besides the one of this class that is running it.
+	 *
+	 * @param string $hook_name Hook name.
+	 * @return bool
+	 */
+	private static function has_other_callbacks( string $hook_name ): bool {
+		global $wp_filter;
+
+		$hook = $wp_filter[ $hook_name ] ?? null;
+		if ( ! $hook instanceof WP_Hook ) {
+			return false;
+		}
+
+		$callback_count = 0;
+		foreach ( $hook->callbacks as $callbacks ) {
+			$callback_count += count( $callbacks );
+		}
+
+		return $callback_count > 1;
 	}

 	/**
@@ -90,6 +150,8 @@ class WC_Admin_Reports {
 	/**
 	 * Filter handler for replacing the data of the status widget on the Dashboard page.
 	 *
+	 * Core only calls this when another callback is hooked to `woocommerce_dashboard_status_widget_reports`.
+	 *
 	 * @param array $status_widget_reports The data to display in the status widget.
 	 */
 	public static function replace_dashboard_status_widget_reports( $status_widget_reports ) {
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/admin/class-wc-tests-admin-dashboard.php b/plugins/woocommerce/tests/legacy/unit-tests/admin/class-wc-tests-admin-dashboard.php
index 62a8ac83015..b04065939f6 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/admin/class-wc-tests-admin-dashboard.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/admin/class-wc-tests-admin-dashboard.php
@@ -25,7 +25,6 @@ class WC_Tests_Admin_Dashboard extends WC_Unit_Test_Case {

 		// Mock http request to performance endpoint.
 		add_filter( 'rest_pre_dispatch', array( $this, 'mock_rest_responses' ), 10, 3 );
-		add_filter( 'woocommerce_dashboard_status_widget_reports', array( $this, 'mock_replace_dashboard_status_widget_reports' ) );
 	}

 	/**
@@ -34,7 +33,6 @@ class WC_Tests_Admin_Dashboard extends WC_Unit_Test_Case {
 	public function tearDown(): void {
 		parent::tearDown();
 		remove_filter( 'rest_pre_dispatch', array( $this, 'mock_rest_responses' ), 10 );
-		remove_filter( 'woocommerce_dashboard_status_widget_reports', array( $this, 'mock_replace_dashboard_status_widget_reports' ) );
 	}

 	/**
@@ -162,7 +160,7 @@ class WC_Tests_Admin_Dashboard extends WC_Unit_Test_Case {
 	}

 	/**
-	 * Ways Analytics can be turned off, each of which must bring back the legacy report links.
+	 * Ways Analytics can be turned off.
 	 *
 	 * @return array<string, array{callable}>
 	 */
@@ -183,30 +181,77 @@ class WC_Tests_Admin_Dashboard extends WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox Should use the legacy status widget reports filter when Analytics is disabled.
+	 * @testdox Should hide the sales rows, but keep the order and stock rows, when Analytics is disabled.
 	 * @dataProvider provide_analytics_disabled_setups
 	 *
 	 * @param callable $disable_analytics Turns Analytics off.
 	 */
-	public function test_status_widget_with_analytics_disabled( callable $disable_analytics ) {
-		$this->skip_if_hpos_enabled( 'We don\'t support legacy reports on HPOS' );
+	public function test_status_widget_hides_sales_rows_with_analytics_disabled( callable $disable_analytics ) {
 		wp_set_current_user( $this->user );
+		update_option( 'woocommerce_manage_stock', 'yes' );
 		$order = WC_Helper_Order::create_order();
 		$order->set_status( OrderStatus::COMPLETED );
 		$order->save();

 		$disable_analytics();

-		$this->expectOutputRegex( '/50\.00 worth in the/' );
+		ob_start();
+		( new WC_Admin_Dashboard() )->status_widget_content();
+		$widget_output = ob_get_clean();
+
+		$this->assertStringNotContainsString( 'sales-this-month', $widget_output, 'Net sales row should be hidden without Analytics.' );
+		$this->assertStringNotContainsString( 'best-seller-this-month', $widget_output, 'Top seller row should be hidden without Analytics.' );
+		$this->assertStringContainsString( 'processing-orders', $widget_output );
+		$this->assertStringContainsString( 'on-hold-orders', $widget_output );
+		$this->assertStringContainsString( 'page=wc-reports&#038;tab=stock&#038;report=low_in_stock', $widget_output );
+		$this->assertStringContainsString( 'page=wc-reports&#038;tab=stock&#038;report=out_of_stock', $widget_output );
+		$this->assertStringNotContainsString( 'path=%2Fanalytics', $widget_output, 'No status widget link should point to Analytics while it is disabled.' );
+	}
+
+	/**
+	 * @testdox Should show sales rows, linked to legacy Reports, when a filter supplies only sales data while Analytics is disabled.
+	 */
+	public function test_status_widget_reports_filter_still_supplies_sales_rows_with_analytics_disabled() {
+		wp_set_current_user( $this->user );
+		$order = WC_Helper_Order::create_order();
+		$order->set_status( OrderStatus::COMPLETED );
+		$order->save();
+
+		add_filter( 'woocommerce_admin_disabled', '__return_true' );
+		add_filter( 'woocommerce_dashboard_status_widget_reports', array( $this, 'mock_replace_dashboard_status_widget_reports' ) );

+		ob_start();
 		( new WC_Admin_Dashboard() )->status_widget_content();
+		$widget_output = ob_get_clean();

-		$widget_output = $this->getActualOutput();
-		$this->assertMatchesRegularExpression( '/page\=wc-reports\&\#038\;tab\=orders\&\#038\;range\=month/', $widget_output );
-		$this->assertMatchesRegularExpression( '/page\=wc-reports\&\#038\;tab\=orders\&\#038\;report\=sales_by_product/', $widget_output );
-		$this->assertMatchesRegularExpression( '/page\=wc-reports\&\#038\;tab\=stock\&\#038\;report\=low_in_stock/', $widget_output );
-		$this->assertMatchesRegularExpression( '/page\=wc-reports\&\#038\;tab\=stock\&\#038\;report\=out_of_stock/', $widget_output );
-		$this->assertDoesNotMatchRegularExpression( '/page\=wc-admin/', $widget_output, 'No status widget link should point to Analytics while it is disabled.' );
+		$this->assertStringContainsString( '50.00 worth in the', $widget_output );
+		$this->assertStringContainsString( 'page=wc-reports&#038;tab=orders&#038;range=month', $widget_output );
+		$this->assertStringContainsString( 'page=wc-reports&#038;tab=orders&#038;report=sales_by_product', $widget_output );
+	}
+
+	/**
+	 * @testdox Should show legacy sales data adjusted by a reports filter callback when Analytics is disabled.
+	 */
+	public function test_status_widget_reports_filter_can_adjust_legacy_net_sales_with_analytics_disabled() {
+		wp_set_current_user( $this->user );
+		add_filter( 'woocommerce_admin_disabled', '__return_true' );
+		include_once WC_ABSPATH . 'includes/admin/class-wc-admin-reports.php';
+		WC_Admin_Reports::register_hook_handlers();
+		add_filter(
+			'woocommerce_dashboard_status_widget_reports',
+			function ( $reports ) {
+				$reports['report_data']->net_sales = 4321;
+				return $reports;
+			},
+			20
+		);
+
+		ob_start();
+		( new WC_Admin_Dashboard() )->status_widget_content();
+		$widget_output = ob_get_clean();
+
+		$this->assertStringContainsString( 'sales-this-month', $widget_output );
+		$this->assertStringContainsString( '4,321.00', $widget_output );
 	}

 	/**
@@ -258,10 +303,6 @@ class WC_Tests_Admin_Dashboard extends WC_Unit_Test_Case {
 		$report_data            = new stdClass();
 		$report_data->net_sales = 123;

-		$status_widget_reports['net_sales_link']      = 'admin.php?page=wc-reports&tab=orders&range=month';
-		$status_widget_reports['top_seller_link']     = 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=';
-		$status_widget_reports['lowstock_link']       = 'admin.php?page=wc-reports&tab=stock&report=low_in_stock';
-		$status_widget_reports['outofstock_link']     = 'admin.php?page=wc-reports&tab=stock&report=out_of_stock';
 		$status_widget_reports['report_data']         = $report_data;
 		$status_widget_reports['get_sales_sparkline'] = function () {
 			return array(
diff --git a/plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-test.php b/plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-test.php
index ed4483ebb8c..1dc7b03f67b 100644
--- a/plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-test.php
+++ b/plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-test.php
@@ -27,7 +27,7 @@ final class WC_Admin_Reports_Test extends WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox Should point the dashboard status widget at legacy Reports, which it only uses when Analytics is disabled.
+	 * @testdox Should keep pointing the legacy status widget reports at legacy Reports for code that still calls them.
 	 */
 	public function test_replace_dashboard_status_widget_reports_uses_legacy_report_links(): void {
 		$reports = \WC_Admin_Reports::replace_dashboard_status_widget_reports( array() );
@@ -37,4 +37,105 @@ final class WC_Admin_Reports_Test extends WC_Unit_Test_Case {
 		$this->assertSame( 'admin.php?page=wc-reports&tab=stock&report=low_in_stock', $reports['lowstock_link'] );
 		$this->assertSame( 'admin.php?page=wc-reports&tab=stock&report=out_of_stock', $reports['outofstock_link'] );
 	}
+
+	/**
+	 * @testdox Should hook only the conditional legacy handlers into the dashboard status widget.
+	 */
+	public function test_register_hook_handlers_hooks_conditional_status_widget_handlers(): void {
+		\WC_Admin_Reports::register_hook_handlers();
+
+		$this->assertFalse( has_filter( 'woocommerce_dashboard_status_widget_reports', array( \WC_Admin_Reports::class, 'replace_dashboard_status_widget_reports' ) ) );
+		$this->assertSame( 10, has_filter( 'woocommerce_dashboard_status_widget_reports', array( \WC_Admin_Reports::class, 'maybe_replace_dashboard_status_widget_reports' ) ) );
+		$this->assertSame( 10, has_filter( 'woocommerce_after_dashboard_status_widget_parameter', array( \WC_Admin_Reports::class, 'get_status_widget_report_instance' ) ) );
+	}
+
+	/**
+	 * @testdox Should leave the status widget sales data empty when no other callback is hooked to the reports filter.
+	 */
+	public function test_status_widget_reports_stay_empty_without_other_callbacks(): void {
+		remove_all_filters( 'woocommerce_dashboard_status_widget_reports' );
+		\WC_Admin_Reports::register_hook_handlers();
+
+		$reports = apply_filters( 'woocommerce_dashboard_status_widget_reports', $this->get_default_status_widget_reports() );
+
+		$this->assertNull( $reports['report_data'] );
+		$this->assertNull( $reports['get_sales_sparkline'] );
+	}
+
+	/**
+	 * @testdox Should fill in legacy sales data when another callback adjusts it through the reports filter.
+	 */
+	public function test_status_widget_reports_are_filled_for_other_callbacks(): void {
+		remove_all_filters( 'woocommerce_dashboard_status_widget_reports' );
+		\WC_Admin_Reports::register_hook_handlers();
+		add_filter(
+			'woocommerce_dashboard_status_widget_reports',
+			function ( $reports ) {
+				$reports['report_data']->net_sales = 42;
+				return $reports;
+			},
+			20
+		);
+
+		$reports = apply_filters( 'woocommerce_dashboard_status_widget_reports', $this->get_default_status_widget_reports() );
+
+		$this->assertSame( 42, $reports['report_data']->net_sales );
+		$this->assertIsCallable( $reports['get_sales_sparkline'] );
+	}
+
+	/**
+	 * @testdox Should pass a legacy report to the after status widget action only when something listens to it.
+	 */
+	public function test_status_widget_report_instance_is_only_created_for_listeners(): void {
+		remove_all_actions( 'woocommerce_after_dashboard_status_widget' );
+		remove_all_filters( 'woocommerce_after_dashboard_status_widget_parameter' );
+		\WC_Admin_Reports::register_hook_handlers();
+
+		$this->assertNull( apply_filters( 'woocommerce_after_dashboard_status_widget_parameter', null ), 'No report should be created without listeners.' );
+
+		add_action( 'woocommerce_after_dashboard_status_widget', '__return_null' );
+		$reports = apply_filters( 'woocommerce_after_dashboard_status_widget_parameter', null );
+
+		$this->assertIsObject( $reports, 'Listeners should still receive a legacy report.' );
+		$this->assertSame( 'WC_Admin_Report', get_class( $reports ) );
+	}
+
+	/**
+	 * @testdox Should pass a legacy report to another callback on the after status widget parameter filter.
+	 */
+	public function test_status_widget_report_instance_is_created_for_other_parameter_callbacks(): void {
+		remove_all_actions( 'woocommerce_after_dashboard_status_widget' );
+		remove_all_filters( 'woocommerce_after_dashboard_status_widget_parameter' );
+		\WC_Admin_Reports::register_hook_handlers();
+		$received = null;
+		add_filter(
+			'woocommerce_after_dashboard_status_widget_parameter',
+			function ( $reports ) use ( &$received ) {
+				$received = $reports;
+				return $reports;
+			},
+			20
+		);
+
+		apply_filters( 'woocommerce_after_dashboard_status_widget_parameter', null );
+
+		$this->assertIsObject( $received, 'Another callback on the filter should receive a legacy report.' );
+		$this->assertSame( 'WC_Admin_Report', get_class( $received ) );
+	}
+
+	/**
+	 * The status widget data the dashboard passes to the reports filter when Analytics is disabled.
+	 *
+	 * @return array
+	 */
+	private function get_default_status_widget_reports(): array {
+		return array(
+			'net_sales_link'      => 'admin.php?page=wc-reports&tab=orders&range=month',
+			'top_seller_link'     => 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=',
+			'lowstock_link'       => 'admin.php?page=wc-reports&tab=stock&report=low_in_stock',
+			'outofstock_link'     => 'admin.php?page=wc-reports&tab=stock&report=out_of_stock',
+			'report_data'         => null,
+			'get_sales_sparkline' => null,
+		);
+	}
 }