diff --git a/packages/autoloader/src/class-plugins-handler.php b/packages/autoloader/src/class-plugins-handler.php index 4fee69bf34b1b..6e6511c11714a 100644 --- a/packages/autoloader/src/class-plugins-handler.php +++ b/packages/autoloader/src/class-plugins-handler.php @@ -31,7 +31,7 @@ public function get_all_active_plugins( $skip_single_file_plugins = true ) { * * @return Array The active sitewide plugins. */ - public function get_multisite_plugins() { + protected function get_multisite_plugins() { return is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) : array(); diff --git a/packages/autoloader/tests/php/bootstrap.php b/packages/autoloader/tests/php/bootstrap.php index 95d335f44809f..383171ce78950 100644 --- a/packages/autoloader/tests/php/bootstrap.php +++ b/packages/autoloader/tests/php/bootstrap.php @@ -18,6 +18,62 @@ function trailingslashit( $string ) { } } +if ( ! function_exists( 'wp_unslash' ) ) { + /** + * A drop-in for a WordPress core function. + * + * @param string|string[] $value String or array of strings to unslash. + * @return string|string[] Unslashed $value + */ + function wp_unslash( $value ) { + return stripslashes_deep( $value ); + } + + /** + * A drop-in for a WordPress core function. + * + * @param mixed $value The value to be stripped. + * @return mixed Stripped value. + */ + function stripslashes_deep( $value ) { + return map_deep( $value, 'stripslashes_from_strings_only' ); + } + + /** + * A drop-in for a WordPress core function. + * + * @param mixed $value The array, object, or scalar. + * @param callable $callback The function to map onto $value. + * @return mixed The value with the callback applied to all non-arrays and non-objects inside it. + */ + function map_deep( $value, $callback ) { + if ( is_array( $value ) ) { + foreach ( $value as $index => $item ) { + $value[ $index ] = map_deep( $item, $callback ); + } + } elseif ( is_object( $value ) ) { + $object_vars = get_object_vars( $value ); + foreach ( $object_vars as $property_name => $property_value ) { + $value->$property_name = map_deep( $property_value, $callback ); + } + } else { + $value = call_user_func( $callback, $value ); + } + + return $value; + } + + /** + * A drop-in for a WordPress core function. + * + * @param mixed $value The array or string to be stripped. + * @return mixed $value The stripped value. + */ + function stripslashes_from_strings_only( $value ) { + return is_string( $value ) ? stripslashes( $value ) : $value; + } +} + require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../src/functions.php'; require_once __DIR__ . '/../../src/class-plugins-handler.php'; diff --git a/packages/autoloader/tests/php/test_plugins_handler.php b/packages/autoloader/tests/php/test_plugins_handler.php new file mode 100644 index 0000000000000..2b9e596f1e50b --- /dev/null +++ b/packages/autoloader/tests/php/test_plugins_handler.php @@ -0,0 +1,328 @@ + 'details', + 'multi2/multi2.php' => 'details', + ); + + /** + * This method is called before each test. + */ + public function setUp() { + $this->plugins_handler = $this->getMockBuilder( 'Plugins_Handler' ) + ->setMethods( + array( + 'get_current_plugin', + 'get_multisite_plugins', + 'get_active_plugins', + ) + ) + ->getMock(); + } + + /** + * Set up mock Plugins_Handler methods. + * + * @param Array $active_plugins The names of the active plugins. + * @param Boolean $use_multisite Whether the env is multisite. + */ + private function set_up_mocks( + $active_plugins = self::DEFAULT_ACTIVE_PLUGINS, + $use_multisite = false ) { + + $this->plugins_handler + ->method( 'get_active_plugins' ) + ->willReturn( (array) $active_plugins ); + + if ( $use_multisite ) { + $this->plugins_handler + ->method( 'get_multisite_plugins' ) + ->willReturn( array_keys( self::DEFAULT_MULTISITE_PLUGINS ) ); + } else { + $this->plugins_handler + ->method( 'get_multisite_plugins' ) + ->willReturn( array() ); + } + } + + /** + * Tests is_directory_plugin() with a single-file plugin. + * + * @covers Plugins_Handler::is_directory_plugin + */ + public function test_is_directory_plugin_single_file() { + $this->assertFalse( $this->plugins_handler->is_directory_plugin( 'test.php' ) ); + } + + /** + * Tests is_directory_plugin() with a single-file plugin that begins with '/'. + * + * @covers Plugins_Handler::is_directory_plugin + */ + public function test_is_directory_plugin_single_file_with_slash() { + $this->assertFalse( $this->plugins_handler->is_directory_plugin( '/test.php' ) ); + } + + /** + * Tests is_directory_plugin() with a plugin with a directory. + * + * @covers Plugins_Handler::is_directory_plugin + */ + public function test_is_directory_plugin_dir() { + $this->assertTrue( $this->plugins_handler->is_directory_plugin( 'test/test.php' ) ); + } + + /** + * Tests should_autoloader_reset() with an already active plugin. + * + * @covers Plugins_Handler::should_autoloader_reset + */ + public function test_should_autoloader_reset_known_plugin() { + global $jetpack_autoloader_activating_plugins; + + // 'test1/test1.php' is in self::DEFAULT_ACTIVE_PLUGINS. + $this->plugins_handler + ->method( 'get_current_plugin' ) + ->willReturn( 'test1/test1.php' ); + + $this->set_up_mocks(); + + $this->assertFalse( $this->plugins_handler->should_autoloader_reset() ); + $this->assertEmpty( $jetpack_autoloader_activating_plugins ); + } + + /** + * Tests should_autoloader_reset() with an activating, unknown plugin. + * + * @covers Plugins_Handler::should_autoloader_reset + */ + public function test_should_autoloader_reset_unknown_plugin() { + global $jetpack_autoloader_activating_plugins; + + $current_plugin = 'unknown/unknown.php'; + + // 'unknown/unknown.php' is not in self::DEFAULT_ACTIVE_PLUGINS. + $this->plugins_handler + ->method( 'get_current_plugin' ) + ->willReturn( $current_plugin ); + + $this->set_up_mocks(); + + $this->assertTrue( $this->plugins_handler->should_autoloader_reset() ); + $this->assertCount( 1, $jetpack_autoloader_activating_plugins ); + $this->assertEquals( $current_plugin, $jetpack_autoloader_activating_plugins[0] ); + } + + /** + * Tests get_all_active_plugins() with activating plugins (via request and + * non-request methods) and a list of active plugins. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_everything() { + global $jetpack_autoloader_activating_plugins; + + // Activating plugin. + $activating_plugin = 'activating/activating.php'; + $jetpack_autoloader_activating_plugins = array( $activating_plugin ); + + // Plugin activating via a request. + $request_plugin = 'request/request.php'; + $_REQUEST['action'] = 'activate'; + $_REQUEST['plugin'] = $request_plugin; + $_REQUEST['_wpnonce'] = '123abc'; + + // Use default active plugins. + $this->set_up_mocks(); + + $expected_output = array_merge( + array( $activating_plugin ), + array( $request_plugin ), + self::DEFAULT_ACTIVE_PLUGINS + ); + + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins() with multiple plugins activating (via request and + * non-request methods) and a list of active plugins. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_multiple_activating() { + global $jetpack_autoloader_activating_plugins; + + // Activating plugins. + $activating_plugins = array( 'activating1/activating1.php', 'activating2/activating2.php' ); + + $jetpack_autoloader_activating_plugins = $activating_plugins; + + // Plugins activating via a request. + $request_plugins = array( + 'request1/request1.php', + 'request2/request2.php', + 'request3/request3.php', + ); + + $_REQUEST['action'] = 'activate-selected'; + $_REQUEST['checked'] = $request_plugins; + $_REQUEST['_wpnonce'] = '123abc'; + + // Use default active plugins. + $this->set_up_mocks(); + + $expected_output = array_merge( + $activating_plugins, + $request_plugins, + self::DEFAULT_ACTIVE_PLUGINS + ); + + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins() with no nonce included in the request. Since + * a nonce isn't included, the plugin will not be activated. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_no_nonce() { + global $jetpack_autoloader_activating_plugins; + + // Activating plugin. + $activating_plugin = 'activating/activating.php'; + $jetpack_autoloader_activating_plugins = array( $activating_plugin ); + + // Plugin activating via a request without a nonce. + $request_plugin = 'request/request.php'; + $_REQUEST['action'] = 'activate'; + $_REQUEST['plugin'] = $request_plugin; + + // Use default active plugins. + $this->set_up_mocks(); + + // The plugin activating via a request should not be in the output. + $expected_output = array_merge( + array( $activating_plugin ), + self::DEFAULT_ACTIVE_PLUGINS + ); + + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins() with no activating plugins. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_no_activating() { + // Plugin deactivating via a request. + $request_plugin = 'request/request.php'; + $_REQUEST['action'] = 'deactivate'; + $_REQUEST['plugin'] = $request_plugin; + $_REQUEST['_wpnonce'] = '123abc'; + + // Use default active plugins. + $this->set_up_mocks(); + + $expected_output = self::DEFAULT_ACTIVE_PLUGINS; + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + + /** + * Tests get_all_active_plugins() with no activating plugins and a single + * active plugin. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_single_active() { + $active_plugin = array( 'test/test.php' ); + $this->set_up_mocks( $active_plugin ); + + $expected_output = $active_plugin; + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins() with an active single-file plugin and single-file + * plugins skipped. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_single_file_plugin_skip() { + $active_dir_plugin = array( 'test/test.php' ); + $active_plugins = array_merge( $active_dir_plugin, array( 'single_file.php' ) ); + $this->set_up_mocks( $active_plugins ); + + $expected_output = $active_dir_plugin; + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins with a single-file plugin and single-file + * plugins not skipped. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_single_file_plugin_dont_skip() { + $active_plugins = array( 'test/test.php', 'single_file.php' ); + $this->set_up_mocks( $active_plugins ); + + $expected_output = $active_plugins; + $actual_output = $this->plugins_handler->get_all_active_plugins( false ); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } + + /** + * Tests get_all_active_plugins with activating plugins (via request and + * non-request methods) and a list of active plugins. + * + * @covers Plugins_Handler::get_all_active_plugins + */ + public function test_get_all_active_plugins_multisite() { + global $jetpack_autoloader_activating_plugins; + + // Activating plugin. + $activating_plugin = 'activating/activating.php'; + $jetpack_autoloader_activating_plugins = array( $activating_plugin ); + + // Plugin activating via a request. + $request_plugin = 'request/request.php'; + $_REQUEST['action'] = 'activate'; + $_REQUEST['plugin'] = $request_plugin; + $_REQUEST['_wpnonce'] = '123abc'; + + $this->set_up_mocks( self::DEFAULT_ACTIVE_PLUGINS, true ); + + $expected_output = array_merge( + array( $activating_plugin ), + array( $request_plugin ), + self::DEFAULT_ACTIVE_PLUGINS, + array_keys( self::DEFAULT_MULTISITE_PLUGINS ) + ); + + $actual_output = $this->plugins_handler->get_all_active_plugins(); + $this->assertEquals( sort( $expected_output ), sort( $actual_output ) ); + } +}