Improvements in the handling of feature compatibility for plugins (#48169)

- In the plugins list only features that are currently enabled
  will be considered for the list of features that are incompatible
  with a given plugin.

- A per-feature flag named "plugins_are_incompatible_by_default"
  is used. Defaulting to false, when it's true for a given feature
  it means that plugins not declaring compatibility with a the
  feature are to be considered incompatible with the feature.
  The flag is set for the HPOS feature only.

- A new 'woocommerce_plugins_are_incompatible_with_feature_by_default'
  filter is added, which allows to alter the flag for a given feature.

- The "plugins incompatible with feature" page will display all the
  incompatible/uncertain plugins, even if the feature is disabled.
  The "plugins incompatible with ALL features" page still shows
  only plugins that are incompatible/uncertain with enabled features.

- Add a --ignore-plugin-compatibility switch to the "wc hpos enable"
  command, to allow enabling even if there are incompatible plugins.

- Add a new "wc hpos compatibility-info" command, which lists 
  the plugins that are compatible/incompatible/uncertain with HPOS.
This commit is contained in:
Néstor Soriano 2024-06-26 08:45:43 +02:00 committed by GitHub
parent 9bdd6cc45b
commit cacb10065e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 323 additions and 66 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Improvements in the handling of feature compatibility for plugins

View File

@ -7,6 +7,7 @@ use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use Automattic\WooCommerce\Internal\DataStores\Orders\LegacyDataHandler;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Utilities\PluginUtil;
use WP_CLI;
/**
@ -76,6 +77,7 @@ class CLIRunner {
WP_CLI::add_command( 'wc hpos status', array( $this, 'status' ) );
WP_CLI::add_command( 'wc hpos diff', array( $this, 'diff' ) );
WP_CLI::add_command( 'wc hpos backfill', array( $this, 'backfill' ) );
WP_CLI::add_command( 'wc hpos compatibility-info', array( $this, 'compatibility_info' ) );
WP_CLI::add_command( 'wc hpos compatibility-mode enable', array( $this, 'enable_compat_mode' ) );
WP_CLI::add_command( 'wc hpos compatibility-mode disable', array( $this, 'disable_compat_mode' ) );
@ -736,6 +738,9 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
* default: false
* ---
*
* [--ignore-plugin-compatibility]
* : Enable even if there are active plugins that are incompatible with HPOS.
*
* ### EXAMPLES
*
* # Enable HPOS on new shops.
@ -750,8 +755,9 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
$assoc_args = wp_parse_args(
$assoc_args,
array(
'for-new-shop' => false,
'with-sync' => false,
'for-new-shop' => false,
'with-sync' => false,
'ignore-plugin-compatibility' => false,
)
);
@ -763,12 +769,18 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
WP_CLI::error( __( '[Failed] This is not a new shop, but --for-new-shop flag was passed.', 'woocommerce' ) );
}
$container = wc_get_container();
/** Feature controller instance @var FeaturesController $feature_controller */
$feature_controller = wc_get_container()->get( FeaturesController::class );
$plugin_info = $feature_controller->get_compatible_plugins_for_feature( 'custom_order_tables', true );
if ( count( array_merge( $plugin_info['uncertain'], $plugin_info['incompatible'] ) ) > 0 ) {
WP_CLI::warning( __( '[Failed] Some installed plugins are incompatible. Please review the plugins by going to WooCommerce > Settings > Advanced > Features and see the "Order data storage" section.', 'woocommerce' ) );
$enable_hpos = false;
$feature_controller = $container->get( FeaturesController::class );
if ( ! $assoc_args['ignore-plugin-compatibility'] ) {
$compatibility_info = $feature_controller->get_compatible_plugins_for_feature( 'custom_order_tables', true );
/** Plugin util instance @var PluginUtil $plugin_util */
$plugin_util = $container->get( PluginUtil::class );
$incompatibles = $plugin_util->get_items_considered_incompatible( 'custom_order_tables', $compatibility_info );
if ( count( $incompatibles ) > 0 ) {
WP_CLI::warning( __( '[Failed] Some installed plugins are incompatible. Please review the plugins by going to WooCommerce > Settings > Advanced > Features and see the "Order data storage" section.', 'woocommerce' ) );
$enable_hpos = false;
}
}
/** DataSynchronizer instance @var DataSynchronizer $data_synchronizer */
@ -1213,6 +1225,104 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
);
}
/**
* Show the list of WooCommerce-aware plugins known to be compatible, incompatible or without compatibility declaration for HPOS. Note that inactive plugins will always be listed in the "uncertain" list.
*
* [--include-inactive]
* : Include inactive plugins in the list.
*
* [--display-filenames]
* : Print plugin file names instead of plugin names.
*
* @since 9.1.0
*
* @param array $args Positional arguments passed to the command.
* @param array $assoc_args Associative arguments (options) passed to the command.
*/
public function compatibility_info( array $args = array(), array $assoc_args = array() ): void {
$container = wc_get_container();
$feature_controller = $container->get( FeaturesController::class );
$plugin_info = $feature_controller->get_compatible_plugins_for_feature( 'custom_order_tables', ! ( (bool) ( $assoc_args['include-inactive'] ?? null ) ) );
$display_filenames = (bool) ( $assoc_args['display-filenames'] ?? null );
$compatibles = $this->get_printable_plugin_names( $plugin_info['compatible'], $display_filenames );
$compatibles_count = count( $compatibles );
$this->log(
sprintf(
// translators: $1$d = plugins count, %2$s = colon (if list follows) or empty.
_n( "\n%%C%1\$d%%n compatible plugin found%2\$s", "\n%%C%1\$d%%n compatible plugins found%2\$s", $compatibles_count, 'woocommerce' ),
$compatibles_count,
$compatibles_count > 0 ? ":\n" : ''
)
);
$this->print_plugin_names( $compatibles );
$incompatibles = $this->get_printable_plugin_names( $plugin_info['incompatible'], $display_filenames );
$incompatibles_count = count( $incompatibles );
$this->log(
sprintf(
// translators: $1$d = plugins count, %2$s = colon (if list follows) or empty.
_n( "\n%%C%1\$d%%n incompatible plugin found%2\$s", "\n%%C%1\$d%%n incompatible plugins found%2\$s", $incompatibles_count, 'woocommerce' ),
$incompatibles_count,
$incompatibles_count > 0 ? ":\n" : ''
)
);
$this->print_plugin_names( $incompatibles );
$uncertain = $this->get_printable_plugin_names( $plugin_info['uncertain'], $display_filenames );
$uncertain_count = count( $uncertain );
$this->log(
sprintf(
// translators: $1$d = plugins count, %2$s = colon (if list follows) or empty.
_n( "\n%%C%1\$d%%n uncertain plugin found%2\$s", "\n%%C%1\$d%%n uncertain plugins found%2\$s", $uncertain_count, 'woocommerce' ),
$uncertain_count,
$uncertain_count > 0 ? ":\n" : ''
)
);
$this->print_plugin_names( $uncertain );
}
/**
* Get the printable names for a set of plugins given their file names.
*
* @param array $plugins The plugin file names.
* @param bool $display_filenames True to simply return the sorted list of plugin file names.
* @return array A sorted array of plugin names or file names.
*/
private function get_printable_plugin_names( array $plugins, bool $display_filenames ): array {
if ( $display_filenames ) {
sort( $plugins );
return $plugins;
}
$plugin_names = array_map(
fn( $plugin_file ) => get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_file, false )['Name'] ?? $plugin_file,
$plugins
);
sort( $plugin_names );
return $plugin_names;
}
/**
* Print a list of plugin names.
*
* @param array $plugins The names to print.
*/
private function print_plugin_names( array $plugins ): void {
foreach ( $plugins as $plugin_file ) {
$this->log( ' ' . $plugin_file );
}
}
/**
* Show a log message using the WP_CLI text colorization feature.
*
* @param string $text Text to show.
*/
private function log( string $text ) {
WP_CLI::log( WP_CLI::colorize( $text ) );
}
/**
* Enables compatibility mode, which keeps the HPOS and posts datastore in sync.
*

View File

@ -501,12 +501,13 @@ class CustomOrdersTableController {
*/
private function add_feature_definition( $features_controller ) {
$definition = array(
'option_key' => self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
'is_experimental' => false,
'enabled_by_default' => false,
'order' => 50,
'setting' => $this->get_hpos_setting_for_feature(),
'additional_settings' => array(
'option_key' => self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
'is_experimental' => false,
'enabled_by_default' => false,
'order' => 50,
'setting' => $this->get_hpos_setting_for_feature(),
'plugins_are_incompatible_by_default' => true,
'additional_settings' => array(
$this->get_hpos_setting_for_sync(),
),
);
@ -544,11 +545,11 @@ class CustomOrdersTableController {
};
$get_disabled = function () {
$plugin_compatibility = $this->features_controller->get_compatible_plugins_for_feature( 'custom_order_tables', true );
$sync_complete = 0 === $this->data_synchronizer->get_current_orders_pending_sync_count();
$disabled = array();
// Changing something here? might also want to look at `enable|disable` functions in CLIRunner.
$incompatible_plugins = array_merge( $plugin_compatibility['uncertain'], $plugin_compatibility['incompatible'] );
$compatibility_info = $this->features_controller->get_compatible_plugins_for_feature( 'custom_order_tables', true );
$sync_complete = 0 === $this->data_synchronizer->get_current_orders_pending_sync_count();
$disabled = array();
// Changing something here? You might also want to look at `enable|disable` functions in Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner.
$incompatible_plugins = $this->plugin_util->get_items_considered_incompatible( 'custom_order_tables', $compatibility_info );
$incompatible_plugins = array_diff( $incompatible_plugins, $this->plugin_util->get_plugins_excluded_from_compatibility_ui() );
if ( count( $incompatible_plugins ) > 0 ) {
$disabled = array( 'yes' );

View File

@ -26,6 +26,8 @@ class FeaturesController {
public const FEATURE_ENABLED_CHANGED_ACTION = 'woocommerce_feature_enabled_changed';
public const PLUGINS_COMPATIBLE_BY_DEFAULT_OPTION = 'woocommerce_plugins_are_compatible_with_features_by_default';
/**
* The existing feature definitions.
*
@ -142,12 +144,13 @@ class FeaturesController {
*/
public function add_feature_definition( $slug, $name, array $args = array() ) {
$defaults = array(
'disable_ui' => false,
'enabled_by_default' => false,
'is_experimental' => true,
'is_legacy' => false,
'name' => $name,
'order' => 10,
'disable_ui' => false,
'enabled_by_default' => false,
'is_experimental' => true,
'is_legacy' => false,
'plugins_are_incompatible_by_default' => false,
'name' => $name,
'order' => 10,
);
$args = wp_parse_args( $args, $defaults );
@ -322,6 +325,34 @@ class FeaturesController {
return $features;
}
/**
* Check if plugins that don't declare compatibility nor incompatibility with a given feature
* are to be considered incompatible with that feature.
*
* @param string $feature_id Feature id to check.
* @return bool True if plugins that don't declare compatibility nor incompatibility with the feature will be considered incompatible with the feature.
* @throws \InvalidArgumentException The feature doesn't exist.
*/
public function get_plugins_are_incompatible_by_default( string $feature_id ): bool {
$feature_definition = $this->get_feature_definitions()[ $feature_id ] ?? null;
if ( is_null( $feature_definition ) ) {
throw new \InvalidArgumentException( esc_html( "The WooCommerce feature '$feature_id' doesn't exist" ) );
}
$incompatible_by_default = $feature_definition['plugins_are_incompatible_by_default'] ?? false;
/**
* Filter to determine if plugins that don't declare compatibility nor incompatibility with a given feature
* are to be considered incompatible with that feature.
*
* @param bool $incompatible_by_default Default value, true if plugins are to be considered incompatible by default with the feature.
* @param string $feature_id The feature to check.
*
* @since 9.2.0
*/
return (bool) apply_filters( 'woocommerce_plugins_are_incompatible_with_feature_by_default', $incompatible_by_default, $feature_id );
}
/**
* Check if a given feature is currently enabled.
*
@ -476,7 +507,7 @@ class FeaturesController {
*
* @param string $feature_id Feature id.
* @param bool $active_only True to return only active plugins.
* @return array An array having a 'compatible' and an 'incompatible' key, each holding an array of plugin names.
* @return array An array having a 'compatible', an 'incompatible' and an 'uncertain' key, each holding an array of plugin names.
*/
public function get_compatible_plugins_for_feature( string $feature_id, bool $active_only = false ): array {
$this->verify_did_woocommerce_init( __FUNCTION__ );
@ -884,7 +915,7 @@ class FeaturesController {
*
* @param array $plugin_list The original list of plugins.
*/
private function filter_plugins_list( $plugin_list ): array {
public function filter_plugins_list( $plugin_list ): array {
if ( ! $this->verify_did_woocommerce_init() ) {
return $plugin_list;
}
@ -910,10 +941,11 @@ class FeaturesController {
*
* @return array List of plugins incompatible with the given feature.
*/
private function get_incompatible_plugins( $feature_id, $plugin_list ) {
$incompatibles = array();
$plugin_list = array_diff_key( $plugin_list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
public function get_incompatible_plugins( $feature_id, $plugin_list ) {
$incompatibles = array();
$plugin_list = array_diff_key( $plugin_list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
$feature_ids = 'all' === $feature_id ? array_keys( $this->get_feature_definitions() ) : array( $feature_id );
$only_enabled_features = 'all' === $feature_id;
// phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
foreach ( array_keys( $plugin_list ) as $plugin_name ) {
@ -921,16 +953,17 @@ class FeaturesController {
continue;
}
$compatibility = $this->get_compatible_features_for_plugin( $plugin_name );
$incompatible_with = array_filter(
array_merge( $compatibility['incompatible'], $compatibility['uncertain'] ),
function ( $feature_id ) {
return ! $this->is_legacy_feature( $feature_id );
$compatibility_info = $this->get_compatible_features_for_plugin( $plugin_name );
foreach ( $feature_ids as $feature_id ) {
$features_considered_incompatible = array_filter(
$this->plugin_util->get_items_considered_incompatible( $feature_id, $compatibility_info ),
$only_enabled_features ?
fn( $feature_id ) => $this->feature_is_enabled( $feature_id ) && ! $this->is_legacy_feature( $feature_id ) :
fn( $feature_id ) => ! $this->is_legacy_feature( $feature_id )
);
if ( in_array( $feature_id, $features_considered_incompatible, true ) ) {
$incompatibles[] = $plugin_name;
}
);
if ( ( 'all' === $feature_id && ! empty( $incompatible_with ) ) || in_array( $feature_id, $incompatible_with, true ) ) {
$incompatibles[] = $plugin_name;
}
}
@ -954,7 +987,7 @@ class FeaturesController {
/**
* Shows a warning when there are any incompatibility between active plugins and enabled features.
* The warning is shown in on any admin screen except the plugins screen itself, since
* there's already a "You are viewing
* there's already a "You are viewing plugins that are incompatible" notice.
*/
private function maybe_display_feature_incompatibility_warning(): void {
if ( ! current_user_can( 'activate_plugins' ) ) {
@ -965,18 +998,25 @@ class FeaturesController {
$relevant_plugins = array_diff( $this->plugin_util->get_woocommerce_aware_plugins( true ), $this->plugins_excluded_from_compatibility_ui );
foreach ( $relevant_plugins as $plugin ) {
$compatibility = $this->get_compatible_features_for_plugin( $plugin, true );
$incompatible_with = array_filter(
array_merge( $compatibility['incompatible'], $compatibility['uncertain'] ),
function ( $feature_id ) {
return ! $this->is_legacy_feature( $feature_id );
}
);
$compatibility_info = $this->get_compatible_features_for_plugin( $plugin, true );
if ( $incompatible_with ) {
$incompatibles = array_filter( $compatibility_info['incompatible'], fn( $feature_id ) => ! $this->is_legacy_feature( $feature_id ) );
if ( ! empty( $incompatibles ) ) {
$incompatible_plugins = true;
break;
}
$uncertains = array_filter( $compatibility_info['uncertain'], fn( $feature_id ) => ! $this->is_legacy_feature( $feature_id ) );
foreach ( $uncertains as $feature_id ) {
if ( $this->get_plugins_are_incompatible_by_default( $feature_id ) ) {
$incompatible_plugins = true;
break;
}
}
if ( $incompatible_plugins ) {
break;
}
}
if ( ! $incompatible_plugins ) {

View File

@ -5,6 +5,7 @@
namespace Automattic\WooCommerce\Utilities;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Internal\Utilities\PluginInstaller;
use Automattic\WooCommerce\Proxies\LegacyProxy;
@ -189,13 +190,12 @@ class PluginUtil {
* the Legacy REST API and HPOS are active.
*
* @param string $feature_id Feature id.
* @param array $plugin_feature_info Array of plugin feature info. See FeaturesControllers->get_compatible_plugins_for_feature() for details.
* @param array $plugin_feature_info Array of plugin feature info, as provided by FeaturesController->get_compatible_plugins_for_feature().
*
* @return string Warning string.
*/
public function generate_incompatible_plugin_feature_warning( string $feature_id, array $plugin_feature_info ): string {
$feature_warning = '';
$incompatibles = array_merge( $plugin_feature_info['incompatible'], $plugin_feature_info['uncertain'] );
$incompatibles = $this->get_items_considered_incompatible( $feature_id, $plugin_feature_info );
$incompatibles = array_filter( $incompatibles, 'is_plugin_active' );
$incompatibles = array_values( array_diff( $incompatibles, $this->get_plugins_excluded_from_compatibility_ui() ) );
$incompatible_count = count( $incompatibles );
@ -268,7 +268,8 @@ class PluginUtil {
),
admin_url( 'plugins.php' )
);
$feature_warnings[] = sprintf(
$feature_warnings[] = sprintf(
/* translators: %1$s opening link tag %2$s closing link tag. */
__( '%1$sView and manage%2$s', 'woocommerce' ),
'<a href="' . esc_url( $incompatible_plugins_url ) . '">',
@ -279,6 +280,23 @@ class PluginUtil {
return str_replace( "\n", '<br>', implode( "\n", $feature_warnings ) );
}
/**
* Filter plugin/feature compatibility info, returning the names of the plugins/features that are considered incompatible.
* "Uncertain" information will be included or not depending on the value of the value of the 'plugins_are_incompatible_by_default'
* flag in the feature definition (default is true).
*
* @param string $feature_id Feature id.
* @param array $compatibility_info Array containing "compatible', 'incompatible' and 'uncertain' keys.
* @return array Items in 'incompatible' and 'uncertain' if plugins are incompatible by default with the feature; only items in 'incompatible' otherwise.
*/
public function get_items_considered_incompatible( string $feature_id, array $compatibility_info ): array {
$incompatible_by_default = wc_get_container()->get( FeaturesController::class )->get_plugins_are_incompatible_by_default( $feature_id );
return $incompatible_by_default ?
array_merge( $compatibility_info['incompatible'], $compatibility_info['uncertain'] ) :
$compatibility_info['incompatible'];
}
/**
* Get the names of the plugins that are excluded from the feature compatibility UI.
* These plugins won't be considered as incompatible with any existing feature for the purposes

View File

@ -799,7 +799,7 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
*/
public function test_no_warning_when_all_plugin_are_hpos_compatible() {
$this->simulate_inside_before_woocommerce_init_hook();
// phpcs:disable Squiz.Commenting
// phpcs:disable Squiz.Commenting, Generic.CodeAnalysis.UnusedFunctionParameter.Found
$fake_plugin_util = new class() extends PluginUtil {
private $active_plugins;
@ -826,7 +826,7 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
},
)
);
// phpcs:enable
// phpcs:enable Squiz.Commenting, Generic.CodeAnalysis.UnusedFunctionParameter.Found
$local_sut = new FeaturesController();
@ -884,10 +884,15 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
/**
* @testDox If there is an incompatible plugin, it is returned by get_incompatible_plugins.
*
* @testWith [true]
* [false]
*
* @param bool $hpos_is_enabled True to test with HPOS enabled, false to test with HPOS disabled.
*/
public function test_show_warning_when_a_plugin_is_not_hpos_compatible() {
public function test_show_warning_when_a_plugin_is_not_hpos_compatible_if_hpos_is_enabled( bool $hpos_is_enabled ) {
$this->simulate_inside_before_woocommerce_init_hook();
// phpcs:disable Squiz.Commenting
// phpcs:disable Squiz.Commenting, Generic.CodeAnalysis.UnusedFunctionParameter.Found
$fake_plugin_util = new class() extends PluginUtil {
private $active_plugins;
@ -906,7 +911,6 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
return array();
}
};
// phpcs:enable
$this->register_legacy_proxy_function_mocks(
array(
@ -915,8 +919,10 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
},
)
);
// phpcs:enable Squiz.Commenting, Generic.CodeAnalysis.UnusedFunctionParameter.Found
$local_sut = new FeaturesController();
$local_sut->change_feature_enable( 'custom_order_tables', $hpos_is_enabled );
add_action(
'woocommerce_register_feature_definitions',
@ -926,8 +932,10 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
$features = array(
'custom_order_tables' => array(
'name' => __( 'High-Performance order storage', 'woocommerce' ),
'is_experimental' => true,
'is_experimental' => false,
'enabled_by_default' => false,
'option_key' => CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
'plugins_are_incompatible_by_default' => true,
),
'cart_checkout_blocks' => array(
'name' => __( 'Cart & Checkout Blocks', 'woocommerce' ),
@ -966,6 +974,8 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
$incompatible_plugins = function () use ( $plugins ) {
return $this->get_incompatible_plugins( 'all', array_flip( $plugins ) );
};
$this->assertEquals( array( 'incompatible_plugin' ), array_keys( $incompatible_plugins->call( $local_sut ) ) );
$expected = $hpos_is_enabled ? array( 'incompatible_plugin' ) : array();
$this->assertEquals( $expected, array_keys( $incompatible_plugins->call( $local_sut ) ) );
}
}

View File

@ -2,6 +2,8 @@
namespace Automattic\WooCommerce\Tests\Utilities;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Utilities\PluginUtil;
use Automattic\WooCommerce\Utilities\StringUtil;
@ -123,23 +125,23 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
$this->reset_legacy_proxy_mocks();
$this->register_legacy_proxy_function_mocks(
array(
'get_plugins' => function() {
'get_plugins' => function () {
return array(
'woocommerce/woocommerce.php' => array( 'WC tested up to' => '1.0' ),
'jetpack/jetpack.php' => array( 'foo' => 'bar' ),
'jetpack/jetpack.php' => array( 'foo' => 'bar' ),
'classic-editor/classic-editor.php' => array( 'foo' => 'bar' ),
);
},
)
);
// Unix style
// Unix style.
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( 'woocommerce/woocommerce.php' ) );
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( '6.9.2/woocommerce.php' ) );
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( '/srv/htdocs/woocommerce/latest/woocommerce.php' ) );
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( '../../../../wordpress/plugins/woocommerce/latest/woocommerce.php' ) );
// Windows style
// Windows style.
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( 'woocommerce\\woocommerce.php' ) );
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( '6.9.2\\woocommerce.php' ) );
$this->assertEquals( 'woocommerce/woocommerce.php', $this->sut->get_wp_plugin_id( 'D:\\WordPress\\plugins\\woocommerce\\6.9.2\\woocommerce.php' ) );
@ -155,7 +157,7 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
private function mock_plugin_functions() {
$this->register_legacy_proxy_function_mocks(
array(
'get_plugins' => function() {
'get_plugins' => function () {
return array(
'woo_aware_1' => array( 'WC tested up to' => '1.0' ),
'woo_aware_2' => array( 'WC tested up to' => '2.0' ),
@ -164,10 +166,10 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
'not_woo_aware_2' => array( 'foo' => 'bar' ),
);
},
'is_plugin_active' => function( $plugin_name ) {
'is_plugin_active' => function ( $plugin_name ) {
return 'woo_aware_3' !== $plugin_name;
},
'get_plugin_data' => function( $plugin_name ) {
'get_plugin_data' => function ( $plugin_name ) {
return StringUtil::ends_with( $plugin_name, 'woo_aware_1' ) ?
array(
'WC tested up to' => '1.0',
@ -178,4 +180,76 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
)
);
}
/**
* @testdox Test the `get_items_considered_incompatible` method.
*/
public function test_get_items_considered_incompatible() {
$this->reset_container_resolutions();
add_action(
'woocommerce_register_feature_definitions',
function ( $features_controller ) {
$features = array(
'test_feature_1' => array(
'name' => 'Test feature 1',
'plugins_are_incompatible_by_default' => true,
),
'test_feature_2' => array(
'name' => 'Test feature 2',
'plugins_are_incompatible_by_default' => false,
),
'test_feature_3' => array(
'name' => 'Test feature 2',
),
);
foreach ( $features as $slug => $definition ) {
$features_controller->add_feature_definition( $slug, $definition['name'], $definition );
}
},
20
);
$plugin_compatibility_info = array(
'compatible' => array(
'compatible_1.php',
'compatible_2.php',
),
'incompatible' => array(
'incompatible_1.php',
'incompatible_2.php',
),
'uncertain' => array(
'uncertain_1.php',
'uncertain_2.php',
),
);
$expected = array(
'incompatible_1.php',
'incompatible_2.php',
'uncertain_1.php',
'uncertain_2.php',
);
$actual = $this->sut->get_items_considered_incompatible( 'test_feature_1', $plugin_compatibility_info );
sort( $actual );
sort( $expected );
$this->assertEquals( $expected, $actual );
$expected = array(
'incompatible_1.php',
'incompatible_2.php',
);
$actual = $this->sut->get_items_considered_incompatible( 'test_feature_2', $plugin_compatibility_info );
sort( $actual );
$this->assertEquals( $expected, $actual );
$actual = $this->sut->get_items_considered_incompatible( 'test_feature_3', $plugin_compatibility_info );
sort( $actual );
$this->assertEquals( $expected, $actual );
}
}