From ab5a6c36469b4b543facdfd015c50d1686755c2f Mon Sep 17 00:00:00 2001 From: David Evbodaghe Date: Thu, 6 Mar 2025 12:07:37 +0000 Subject: [PATCH 1/3] feat: deprecate legacy Facebook product fields for new products --- includes/Admin.php | 80 +++++++++++++++++------- tests/Unit/AdminTest.php | 132 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 tests/Unit/AdminTest.php diff --git a/includes/Admin.php b/includes/Admin.php index 896f21be3..be63ba8fa 100644 --- a/includes/Admin.php +++ b/includes/Admin.php @@ -1236,6 +1236,7 @@ public function add_product_settings_tab_content() { $is_visible = ( $visibility = get_post_meta( $post->ID, Products::VISIBILITY_META_KEY, true ) ) ? wc_string_to_bool( $visibility ) : true; $product = wc_get_product( $post ); + $fb_product_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, true ); $rich_text_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_RICH_TEXT_DESCRIPTION, true ); $price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true ); $image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true ); @@ -1256,6 +1257,35 @@ public function add_product_settings_tab_content() {
ID && ($fb_product_description || $image || $price)) { + ?> +
+

+ ', + '' + ); + ?> +

+ +
+ + 'wc_facebook_sync_mode', @@ -1271,27 +1301,30 @@ public function add_product_settings_tab_content() { ) ); - echo '
'; - echo ''; - wp_editor( - $rich_text_description, - \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, - array( - 'id' => 'wc_facebook_sync_mode', - 'textarea_name' => \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, - 'textarea_rows' => 10, - 'media_buttons' => true, - 'teeny' => true, - 'quicktags' => false, - 'tinymce' => array( - 'toolbar1' => 'bold,italic,bullist,spellchecker,fullscreen', - ), - ) - ); - echo '
'; - + // Check if this is an existing product with a Facebook description + if ($post->ID && $fb_product_description) { + echo '
'; + echo ''; + wp_editor( + $rich_text_description, + \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, + array( + 'id' => 'wc_facebook_sync_mode', + 'textarea_name' => \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, + 'textarea_rows' => 10, + 'media_buttons' => true, + 'teeny' => true, + 'quicktags' => false, + 'tinymce' => array( + 'toolbar1' => 'bold,italic,bullist,spellchecker,fullscreen', + ), + ) + ); + echo '
'; + } + if ($post->ID && $image) { woocommerce_wp_radio( array( 'id' => 'fb_product_image_source', @@ -1318,9 +1351,11 @@ public function add_product_settings_tab_content() { 'description' => __( 'Please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ), ) ); + } $this->render_facebook_product_video_field( $video_urls ); + if ($post->ID && $price) { woocommerce_wp_text_input( array( 'id' => \WC_Facebook_Product::FB_PRODUCT_PRICE, @@ -1336,7 +1371,8 @@ public function add_product_settings_tab_content() { 'value' => $price, 'class' => 'enable-if-sync-enabled', ) - ); + ); + } woocommerce_wp_text_input( array( diff --git a/tests/Unit/AdminTest.php b/tests/Unit/AdminTest.php new file mode 100644 index 000000000..0e488515a --- /dev/null +++ b/tests/Unit/AdminTest.php @@ -0,0 +1,132 @@ +admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + // Create a test product + $this->product = new \WC_Product_Simple(); + $this->product->save(); + + // Set up the global post + $GLOBALS['post'] = get_post($this->product->get_id()); + } + + public function tearDown(): void { + parent::tearDown(); + + // Clean up WordPress admin environment + set_current_screen('front'); + + // Clean up + if ($this->product) { + $this->product->delete(true); + } + } + + /** + * Test that deprecation notice is not shown for new products + */ + public function test_deprecation_notice_not_shown_for_new_products() { + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without notice'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringNotContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringNotContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has Facebook description + */ + public function test_deprecation_notice_shown_with_fb_description() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has custom image URL + */ + public function test_deprecation_notice_shown_with_custom_image() { + $this->product->update_meta_data('fb_product_image', 'https://example.com/image.jpg'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has custom price + */ + public function test_deprecation_notice_shown_with_custom_price() { + $this->product->update_meta_data('fb_product_price', '99.99'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that notice dismiss button exists and has correct structure + */ + public function test_deprecation_notice_has_dismiss_button() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('button type="button" class="notice-dismiss"', $content); + $this->assertStringContainsString('Dismiss this notice', $content); + } +} \ No newline at end of file From 407d3efde7df2e6076247dae7d426c784fa99b25 Mon Sep 17 00:00:00 2001 From: David Evbodaghe Date: Thu, 6 Mar 2025 12:22:24 +0000 Subject: [PATCH 2/3] feat: deprecate legacy Facebook product fields for new products --- includes/Admin.php | 371 +++++++++++++------------- includes/Jobs/GenerateProductFeed.php | 4 +- 2 files changed, 184 insertions(+), 191 deletions(-) diff --git a/includes/Admin.php b/includes/Admin.php index be63ba8fa..5a5dd2fdc 100644 --- a/includes/Admin.php +++ b/includes/Admin.php @@ -181,7 +181,7 @@ public function enqueue_scripts() { 'i18n' => array( 'top_level_dropdown_placeholder' => __( 'Search main categories...', 'facebook-for-woocommerce' ), 'second_level_empty_dropdown_placeholder' => __( 'Choose a main category first', 'facebook-for-woocommerce' ), - 'general_dropdown_placeholder' => __( 'Choose a category', 'facebook-for-woocommerce' ), + 'general_dropdown_placeholder' => __( 'Choose a category', 'facebook-for-woocommerce' ), ), ) ); @@ -215,7 +215,7 @@ public function enqueue_scripts() { 'facebook_for_woocommerce_product_sets', array( - 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(), + 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(), 'excluded_category_warning_message' => __( 'You have selected one or more categories currently excluded from the Facebook sync. Products belonging to the excluded categories will not be added to your Facebook Product Set.', 'facebook-for-woocommerce' ), ) ); @@ -238,22 +238,22 @@ public function enqueue_scripts() { 'facebook-for-woocommerce-products-admin', 'facebook_for_woocommerce_products_admin', [ - 'ajax_url' => admin_url( 'admin-ajax.php' ), - 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, - 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, - 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY, - 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT, - 'is_product_published' => $this->is_current_product_published(), - 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(), - 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ), - 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ), - 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ), - 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(), - 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(), + 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, + 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, + 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY, + 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT, + 'is_product_published' => $this->is_current_product_published(), + 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(), + 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ), + 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ), + 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ), + 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(), + 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(), 'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(), 'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(), - 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC, - 'i18n' => [ + 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC, + 'i18n' => [ 'missing_google_product_category_message' => __( 'Please enter a Google product category and at least one sub-category to sell this product on Instagram.', 'facebook-for-woocommerce' ), ], ] @@ -265,7 +265,6 @@ public function enqueue_scripts() { wp_enqueue_script( 'wc-enhanced-select' ); } }//end if - } /** @@ -557,7 +556,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { /** @var \WC_Product[] $found_products */ foreach ( $found_products as $product ) { if ( ! Products::is_sync_enabled_for_product( $product ) - || ! Products::is_product_visible( $product ) ) { + || ! Products::is_product_visible( $product ) ) { $exclude_products[] = $product->get_id(); } } @@ -587,7 +586,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { /** @var \WC_Product[] $found_products */ foreach ( $found_products as $product ) { if ( ! Products::is_sync_enabled_for_product( $product ) - || Products::is_product_visible( $product ) ) { + || Products::is_product_visible( $product ) ) { $exclude_products[] = $product->get_id(); } } @@ -618,15 +617,15 @@ public function filter_products_by_sync_enabled( $query_vars ) { $variable_product = wc_get_product( $variation_post->post_parent ); // we need this check because we only want products with ALL variations hidden if ( $variable_product instanceof \WC_Product && Products::is_sync_enabled_for_product( $variable_product ) - && ! Products::is_product_visible( $variable_product ) ) { + && ! Products::is_product_visible( $variable_product ) ) { $include_products[] = $variable_product->get_id(); } } } else { // self::SYNC_MODE_SYNC_DISABLED // products to be included in the QUERY, not in the sync - $include_products = []; - $found_ids = []; + $include_products = []; + $found_ids = []; $integration = facebook_for_woocommerce()->get_integration(); $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : []; $excluded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : []; @@ -648,7 +647,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { ), ) ); - $include_products = array_unique( array_merge( $include_products, $excluded_products ) ); + $include_products = array_unique( array_merge( $include_products, $excluded_products ) ); // since we record enabled status and visibility on child variations, // we need to include variable products with excluded children $excluded_variations = get_posts( @@ -903,15 +902,13 @@ public function handle_products_sync_bulk_actions( $redirect ) { if ( 'facebook_include' === $action ) { if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) { $enabling_sync_virtual_products[ $product->get_id() ] = $product; - } else { - if ( $product->is_type( 'variable' ) ) { + } elseif ( $product->is_type( 'variable' ) ) { // collect the virtual variations - foreach ( $product->get_children() as $variation_id ) { - $variation = wc_get_product( $variation_id ); - if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) { - $enabling_sync_virtual_products[ $product->get_id() ] = $product; - $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation; - } + foreach ( $product->get_children() as $variation_id ) { + $variation = wc_get_product( $variation_id ); + if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) { + $enabling_sync_virtual_products[ $product->get_id() ] = $product; + $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation; } } }//end if @@ -1122,7 +1119,7 @@ public function filter_virtual_products_affected_enabling_sync( $query_vars ) { public function add_handled_virtual_products_variations_notice() { if ( 'yes' === get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) && - 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'no' ) ) { + 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'no' ) ) { facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice( sprintf( @@ -1164,61 +1161,61 @@ public function add_product_settings_tab( $tabs ) { return $tabs; } - /** - * Outputs the form field for Facebook Product Videos with a description tip. - * - * @param array $video_urls Array of video URLs. - */ - private function render_facebook_product_video_field( $video_urls ) { - $attachment_ids = []; - - // Output the form field for Facebook Product Videos with a description tip - ?> -

- - - -

-
- -

- - - -

- -
- - \WC_Facebook_Product::FB_PRODUCT_VIDEO, - 'name' => \WC_Facebook_Product::FB_PRODUCT_VIDEO, - 'value' => esc_attr( implode( ',', $attachment_ids ) ), // Store attachment IDs - ] - ); - } + /** + * Outputs the form field for Facebook Product Videos with a description tip. + * + * @param array $video_urls Array of video URLs. + */ + private function render_facebook_product_video_field( $video_urls ) { + $attachment_ids = []; + + // Output the form field for Facebook Product Videos with a description tip + ?> +

+ + + +

+
+ +

+ + + +

+ +
+ + \WC_Facebook_Product::FB_PRODUCT_VIDEO, + 'name' => \WC_Facebook_Product::FB_PRODUCT_VIDEO, + 'value' => esc_attr( implode( ',', $attachment_ids ) ), // Store attachment IDs + ] + ); + } /** * Adds content to the new Facebook tab on the Product edit page. @@ -1230,20 +1227,19 @@ private function render_facebook_product_video_field( $video_urls ) { public function add_product_settings_tab_content() { global $post; - // all products have sync enabled unless explicitly disabled $sync_enabled = 'no' !== get_post_meta( $post->ID, Products::SYNC_ENABLED_META_KEY, true ); $is_visible = ( $visibility = get_post_meta( $post->ID, Products::VISIBILITY_META_KEY, true ) ) ? wc_string_to_bool( $visibility ) : true; - $product = wc_get_product( $post ); + $product = wc_get_product( $post ); $fb_product_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, true ); $rich_text_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_RICH_TEXT_DESCRIPTION, true ); - $price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true ); - $image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true ); - $image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true ); - $video_urls = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_VIDEO, true ); - $fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true ); - $fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true ); + $price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true ); + $image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true ); + $image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true ); + $video_urls = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_VIDEO, true ); + $fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true ); + $fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true ); if ( $sync_enabled ) { $sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE; @@ -1258,21 +1254,21 @@ public function add_product_settings_tab_content() { ID && ($fb_product_description || $image || $price)) { + if ( $post->ID && ( $fb_product_description || $image || $price ) ) { ?>

- ', '' - ); + ); ?>

array( 'simple', 'variation' ), 'include' => $items, @@ -99,7 +99,7 @@ protected function process_items( array $items, array $args ) { 'limit' => $this->get_batch_size(), ) ); - $feed_handler = new \WC_Facebook_Product_Feed(); + $feed_handler = new \WC_Facebook_Product_Feed(); $temp_feed_file = fopen( $feed_handler->get_temp_file_path(), 'a' ); $feed_handler->write_products_feed_to_temp_file( $products, $temp_feed_file ); if ( is_resource( $temp_feed_file ) ) { From 52810fd4d71e33c7fef776a80bf670c2c7c66081 Mon Sep 17 00:00:00 2001 From: David Evbodaghe Date: Thu, 6 Mar 2025 18:05:44 +0000 Subject: [PATCH 3/3] Fix FB Variants Products Data Tab --- includes/Admin.php | 36 ++++++++++ tests/Unit/AdminTest.php | 140 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/includes/Admin.php b/includes/Admin.php index 5a5dd2fdc..90412112f 100644 --- a/includes/Admin.php +++ b/includes/Admin.php @@ -1458,6 +1458,36 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos $sync_mode = self::SYNC_MODE_SYNC_DISABLED; } + // Only show deprecation notice if any of the deprecated fields exist + if ($variation->get_id() && ($description || $image_url || $price)) { + ?> +
+

+ ', + '' + ); + ?> +

+ +
+ + "variable_facebook_sync_mode$index", @@ -1476,6 +1506,7 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos ) ); + if ($variation->get_id() && $description) { woocommerce_wp_textarea_input( array( 'id' => sprintf( 'variable_%s%s', \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $index ), @@ -1490,7 +1521,9 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'wrapper_class' => 'form-row form-row-full', ) ); + } + if ($variation->get_id() && $image_url) { woocommerce_wp_radio( array( 'id' => "variable_fb_product_image_source$index", @@ -1521,7 +1554,9 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'description' => __( 'Please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ), ) ); + } + if ($variation->get_id() && $price) { woocommerce_wp_text_input( array( 'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_PRODUCT_PRICE, $index ), @@ -1538,6 +1573,7 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'wrapper_class' => 'form-row form-full', ) ); + } woocommerce_wp_text_input( array( diff --git a/tests/Unit/AdminTest.php b/tests/Unit/AdminTest.php index 0e488515a..bb50822ce 100644 --- a/tests/Unit/AdminTest.php +++ b/tests/Unit/AdminTest.php @@ -129,4 +129,144 @@ public function test_deprecation_notice_has_dismiss_button() { $this->assertStringContainsString('button type="button" class="notice-dismiss"', $content); $this->assertStringContainsString('Dismiss this notice', $content); } + + /** + * Test that deprecation notice has grey styling + */ + public function test_deprecation_notice_has_grey_styling() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-info', $content); + $this->assertStringContainsString('background-color: #f8f9fa', $content); + $this->assertStringContainsString('border-left-color: #72777c', $content); + } + + /** + * Test variation deprecation notice styling and visibility + */ + public function test_variation_deprecation_notice() { + // Create a variable product + $product = new \WC_Product_Variable(); + $product->save(); + + // Create a variation + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->save(); + + // Create a new mock for first test + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without notice'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringNotContainsString('notice notice-info', $content); + + // Add deprecated field and test again with a new mock + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->save(); + + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringContainsString('notice notice-info', $content); + $this->assertStringContainsString('background-color: #f8f9fa', $content); + $this->assertStringContainsString('border-left-color: #72777c', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } + + /** + * Test variation notice dismiss button + */ + public function test_variation_notice_dismiss_button() { + $product = new \WC_Product_Variable(); + $product->save(); + + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('button type="button" class="notice-dismiss"', $content); + $this->assertStringContainsString('Dismiss this notice', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } + + /** + * Test conditional rendering of variation fields + */ + public function test_variation_fields_conditional_render() { + $product = new \WC_Product_Variable(); + $product->save(); + + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->save(); + + // Create a new mock for first test + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without fields'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringNotContainsString('Facebook Description', $content); + $this->assertStringNotContainsString('Custom Image URL', $content); + $this->assertStringNotContainsString('Facebook Price', $content); + + // Test with deprecated fields using a new mock + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->update_meta_data('fb_product_image', 'https://example.com/image.jpg'); + $variation->update_meta_data('fb_product_price', '99.99'); + $variation->save(); + + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Facebook Description Custom Image URL Facebook Price'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringContainsString('Facebook Description', $content); + $this->assertStringContainsString('Custom Image URL', $content); + $this->assertStringContainsString('Facebook Price', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } } \ No newline at end of file