Commit c91c5aa9a54 for woocommerce

commit c91c5aa9a54b8d94efb79c5108bd59a7c0b19c10
Author: ahmedevsaad <105542236+ahmedevsaad@users.noreply.github.com>
Date:   Tue Sep 15 12:14:36 2026 +0300

    Add unit tests for the Product Categories List block (#68600)

    Render the block through do_blocks() against a fixed category tree and assert
    the markup for each attribute the inspector controls write: isDropdown,
    hasCount, hasImage, isHierarchical and hasEmpty.

    Closes #42662.

diff --git a/plugins/woocommerce/changelog/42662-product-categories-inspector-controls-unit-tests b/plugins/woocommerce/changelog/42662-product-categories-inspector-controls-unit-tests
new file mode 100644
index 00000000000..fcb768fbe32
--- /dev/null
+++ b/plugins/woocommerce/changelog/42662-product-categories-inspector-controls-unit-tests
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add unit test coverage for the Product Categories List block attributes: display style, product count, category images, hierarchy, and empty categories.
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php
new file mode 100644
index 00000000000..e6d379a92b4
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCategoriesTest.php
@@ -0,0 +1,131 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Blocks\BlockTypes;
+
+use Automattic\WooCommerce\Tests\Helpers\ImageAttachmentTrait;
+use WC_Helper_Product;
+use WC_Unit_Test_Case;
+
+/**
+ * Tests for the Product Categories List block.
+ */
+class ProductCategoriesTest extends WC_Unit_Test_Case {
+
+	use ImageAttachmentTrait;
+
+	/**
+	 * Term ID of "Hoodies", a child of "Clothing" with one product.
+	 *
+	 * @var int
+	 */
+	private int $hoodies_id;
+
+	/**
+	 * Build the category tree the tests assert against.
+	 *
+	 * Nothing pre-existing is removed. The only term a fresh install ships with is the
+	 * default "Uncategorized", and it holds no products, so `hide_empty` keeps it out of
+	 * the rendered list and the counts below stay exact.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		$clothing_id      = (int) wp_insert_term( 'Clothing', 'product_cat' )['term_id'];
+		$this->hoodies_id = (int) wp_insert_term( 'Hoodies', 'product_cat', array( 'parent' => $clothing_id ) )['term_id'];
+		$music_id         = (int) wp_insert_term( 'Music', 'product_cat' )['term_id'];
+		wp_insert_term( 'Empty', 'product_cat' );
+
+		wp_set_object_terms( WC_Helper_Product::create_simple_product()->get_id(), array( $this->hoodies_id ), 'product_cat' );
+		wp_set_object_terms( WC_Helper_Product::create_simple_product()->get_id(), array( $music_id ), 'product_cat' );
+	}
+
+	/**
+	 * Remove the image files create_image_attachment() wrote into the uploads directory.
+	 */
+	public function tearDown(): void {
+		$this->remove_added_uploads();
+		parent::tearDown();
+	}
+
+	/**
+	 * Render the Product Categories List block via do_blocks().
+	 *
+	 * @param string $attrs JSON object string for block attributes.
+	 * @return string Rendered markup.
+	 */
+	private function render_product_categories( string $attrs = '' ): string {
+		return do_blocks( "<!-- wp:woocommerce/product-categories {$attrs} /-->" );
+	}
+
+	/**
+	 * @testdox Should render a hierarchical list with product counts and no images by default.
+	 */
+	public function test_default_attributes(): void {
+		$markup = $this->render_product_categories();
+
+		$this->assertStringContainsString( 'wc-block-product-categories is-list', $markup );
+		$this->assertStringContainsString( '>Clothing<', $markup );
+		$this->assertStringContainsString( '>Hoodies<', $markup );
+		$this->assertStringContainsString( '>Music<', $markup );
+		$this->assertStringContainsString( 'wc-block-product-categories-list--depth-1', $markup );
+		$this->assertSame( 3, substr_count( $markup, 'wc-block-product-categories-list-item-count' ) );
+		$this->assertStringNotContainsString( 'wc-block-product-categories-list-item__image', $markup );
+	}
+
+	/**
+	 * @testdox Should render a dropdown instead of a list when isDropdown is true.
+	 */
+	public function test_is_dropdown(): void {
+		$markup = $this->render_product_categories( '{"isDropdown":true}' );
+
+		$this->assertStringContainsString( 'wc-block-product-categories is-dropdown', $markup );
+		$this->assertStringContainsString( 'wc-block-product-categories__dropdown', $markup );
+		$this->assertStringNotContainsString( '<li', $markup );
+		$this->assertSame( 4, substr_count( $markup, '<option' ) ); // The three categories plus the "Select a category" placeholder.
+	}
+
+	/**
+	 * @testdox Should omit the product counts when hasCount is false.
+	 */
+	public function test_has_count_false(): void {
+		$markup = $this->render_product_categories( '{"hasCount":false}' );
+
+		$this->assertStringContainsString( '>Clothing<', $markup );
+		$this->assertStringNotContainsString( 'wc-block-product-categories-list-item-count', $markup );
+	}
+
+	/**
+	 * @testdox Should render a category image when hasImage is true, falling back to a placeholder.
+	 */
+	public function test_has_image_true(): void {
+		update_term_meta( $this->hoodies_id, 'thumbnail_id', $this->create_image_attachment( 100, 100, 'hoodie.jpg' ) );
+
+		$markup = $this->render_product_categories( '{"hasImage":true}' );
+
+		$this->assertStringContainsString( 'wc-block-product-categories-list--has-images', $markup );
+		$this->assertStringContainsString( 'hoodie.jpg', $markup );
+		$this->assertStringContainsString( 'wc-block-product-categories-list-item__image--placeholder', $markup );
+	}
+
+	/**
+	 * @testdox Should render every category at the top level when isHierarchical is false.
+	 */
+	public function test_is_hierarchical_false(): void {
+		$markup = $this->render_product_categories( '{"isHierarchical":false}' );
+
+		$this->assertStringContainsString( '>Clothing<', $markup );
+		$this->assertStringContainsString( '>Hoodies<', $markup );
+		$this->assertStringContainsString( '>Music<', $markup );
+		$this->assertStringNotContainsString( 'wc-block-product-categories-list--depth-1', $markup );
+		$this->assertSame( 1, substr_count( $markup, '<ul' ) );
+	}
+
+	/**
+	 * @testdox Should include categories without products only when hasEmpty is true.
+	 */
+	public function test_has_empty(): void {
+		$this->assertStringNotContainsString( '>Empty<', $this->render_product_categories() );
+		$this->assertStringContainsString( '>Empty<', $this->render_product_categories( '{"hasEmpty":true}' ) );
+	}
+}