Commit e7ad58a9b18 for woocommerce

commit e7ad58a9b182c21d57acc3a260fd3a86f3c3bb18
Author: Ján Mikláš <neosinner@gmail.com>
Date:   Mon Aug 10 17:35:58 2026 +0200

    Fix admin script 404s when the minified-js feature is enabled on core builds (#67515)

    * Fix admin script 404s when minified-js feature is enabled on core builds

    * Add changelog entry for minified-js asset fallback fix

diff --git a/plugins/woocommerce/changelog/66575-fix-minified-js-feature-404 b/plugins/woocommerce/changelog/66575-fix-minified-js-feature-404
new file mode 100644
index 00000000000..9f4552987cf
--- /dev/null
+++ b/plugins/woocommerce/changelog/66575-fix-minified-js-feature-404
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Serve minified admin scripts only when the minified file actually exists, preventing broken admin pages when the minified-js feature is force-enabled (e.g. via WooCommerce Beta Tester) on builds that do not ship minified JS files.
diff --git a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
index 3ce46b2a440..0ea36ed912a 100644
--- a/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
+++ b/plugins/woocommerce/src/Internal/Admin/WCAdminAssets.php
@@ -89,10 +89,12 @@ class WCAdminAssets {
 	public static function get_url( $file, $ext ) {
 		$suffix = '';

-		// Potentially enqueue minified JavaScript.
+		// Potentially enqueue minified JavaScript, but only if the minified file exists.
+		// Core builds do not ship minified JS files, so this also guards against the
+		// 'minified-js' feature being force-enabled (e.g. via WooCommerce Beta Tester).
 		if ( $ext === 'js' ) {
 			$script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
-			$suffix       = self::should_use_minified_js_file( $script_debug ) ? '.min' : '';
+			$suffix       = self::should_use_minified_js_file( $script_debug ) && is_readable( WC_ADMIN_ABSPATH . self::get_path( $ext ) . $file . '.min.' . $ext ) ? '.min' : '';
 		}

 		return plugins_url( self::get_path( $ext ) . $file . $suffix . '.' . $ext, WC_ADMIN_PLUGIN_FILE );
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-assets.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-assets.php
index 1b1b28056fd..6a1ef2f4a4a 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-assets.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-assets.php
@@ -19,25 +19,25 @@ class WC_Admin_Tests_WCAdminAssets extends WP_UnitTestCase {
 	public function setUp(): void {
 		parent::setUp();

-		add_filter( 'woocommerce_admin_features', array( $this, 'turn_on_unminified_js_feature' ), 20, 1 );
+		add_filter( 'woocommerce_admin_features', array( $this, 'turn_on_minified_js_feature' ), 20, 1 );
 	}

 	/**
 	 * Tear Down
 	 */
 	public function tearDown(): void {
-		remove_filter( 'woocommerce_admin_features', array( $this, 'turn_on_unminified_js_feature' ), 20 );
+		remove_filter( 'woocommerce_admin_features', array( $this, 'turn_on_minified_js_feature' ), 20 );

 		parent::tearDown();
 	}

 	/**
-	 * Filter to enable unminified-js feature.
+	 * Filter to enable the minified-js feature.
 	 *
 	 * @param  array $features Array of active features.
 	 */
-	public static function turn_on_unminified_js_feature( $features ) {
-		return array_merge( $features, array( 'unminified-js' ) );
+	public static function turn_on_minified_js_feature( $features ) {
+		return array_merge( $features, array( 'minified-js' ) );
 	}

 	/**
@@ -50,13 +50,11 @@ class WC_Admin_Tests_WCAdminAssets extends WP_UnitTestCase {
 		$parts           = explode( '/', $result );
 		$final_file_name = array_pop( $parts );

-		// Since this can vary depending on the env the tests are running in, we will make this assertion based upon the SCRIPT_DEBUG value.
-		$expected_value = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? 'flavortown.js' : 'flavortown.min.js';
-
+		// No minified build of this file exists, so the unminified file name should be served regardless of SCRIPT_DEBUG.
 		$this->assertEquals(
-			$expected_value,
+			'flavortown.js',
 			$final_file_name,
-			'the anticipated js file name should use .min when SCRIPT_DEBUG is off, and have no .min when SCRIPT_DEBUG is on.'
+			'the anticipated js file name should not use .min when no minified file exists.'
 		);
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/WCAdminAssetsTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/WCAdminAssetsTest.php
new file mode 100644
index 00000000000..7dc9ccbf7e1
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/WCAdminAssetsTest.php
@@ -0,0 +1,100 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\Admin;
+
+use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
+use WC_Unit_Test_Case;
+
+/**
+ * Tests for the WCAdminAssets class.
+ */
+class WCAdminAssetsTest extends WC_Unit_Test_Case {
+
+	/**
+	 * Path of the temporary minified JS file created by a test, if any.
+	 *
+	 * @var string|null
+	 */
+	private $temp_min_file = null;
+
+	/**
+	 * Set up test fixtures.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		add_filter( 'woocommerce_admin_features', array( $this, 'enable_minified_js_feature' ), 20 );
+	}
+
+	/**
+	 * Tear down test fixtures.
+	 */
+	public function tearDown(): void {
+		remove_filter( 'woocommerce_admin_features', array( $this, 'enable_minified_js_feature' ), 20 );
+
+		if ( $this->temp_min_file && file_exists( $this->temp_min_file ) ) {
+			wp_delete_file( $this->temp_min_file );
+			$this->temp_min_file = null;
+		}
+
+		parent::tearDown();
+	}
+
+	/**
+	 * Filter callback to enable the minified-js feature.
+	 *
+	 * @param array $features Array of active features.
+	 * @return array
+	 */
+	public function enable_minified_js_feature( $features ) {
+		return array_merge( $features, array( 'minified-js' ) );
+	}
+
+	/**
+	 * Skips the test when SCRIPT_DEBUG is on, as the minified file is never served then and the test would pass vacuously.
+	 */
+	private function skip_if_script_debug(): void {
+		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
+			$this->markTestSkipped( 'Minified files are never served when SCRIPT_DEBUG is on.' );
+		}
+	}
+
+	/**
+	 * @testdox get_url should not use the .min suffix when the minified file does not exist, even with the minified-js feature enabled.
+	 */
+	public function test_get_url_falls_back_to_unminified_file_when_minified_file_is_missing(): void {
+		$this->skip_if_script_debug();
+
+		$url = WCAdminAssets::get_url( 'nonexistent-test-script/index', 'js' );
+
+		$this->assertStringEndsWith(
+			'nonexistent-test-script/index.js',
+			$url,
+			'The unminified file name should be served when no minified file exists.'
+		);
+	}
+
+	/**
+	 * @testdox get_url should use the .min suffix when the minified file exists, the minified-js feature is enabled and SCRIPT_DEBUG is off.
+	 */
+	public function test_get_url_uses_minified_file_when_it_exists(): void {
+		$this->skip_if_script_debug();
+
+		$dist_js_path = WC_ADMIN_ABSPATH . WC_ADMIN_DIST_JS_FOLDER;
+		if ( ! wp_is_writable( $dist_js_path ) ) {
+			$this->markTestSkipped( 'The admin dist JS folder is not writable.' );
+		}
+
+		$this->temp_min_file = $dist_js_path . 'wc-admin-assets-test.min.js';
+		file_put_contents( $this->temp_min_file, '/* test */' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
+
+		$url = WCAdminAssets::get_url( 'wc-admin-assets-test', 'js' );
+
+		$this->assertStringEndsWith(
+			'wc-admin-assets-test.min.js',
+			$url,
+			'The minified file name should be served when the minified file exists and SCRIPT_DEBUG is off.'
+		);
+	}
+}