From ac776c335f5ce9f197ab0c68459e8f8fe5853eb6 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Tue, 25 Jun 2024 22:26:39 -0700 Subject: [PATCH 1/7] Add convenience functions for `:: expectActionRemoved()` `::expectFilterRemoved()` --- php/WP_Mock.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 49ef1f3..cb3cfbb 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -546,4 +546,50 @@ public static function getDeprecatedMethodListener(): DeprecatedMethodListener { return static::$deprecatedMethodListener; } + + /** + * Adds an expectation that an action should be removed. + * + * @param string $action the action hook name + * @param string|callable-string|callable|Type $callback the callable to be removed + * @param ?int $priority the priority it should be registered at + * + * @return void + * @throws InvalidArgumentException + */ + public static function expectActionRemoved(string $action, $callback, ?int $priority = null) : void + { + self::userFunction( + 'remove_action', + array( + 'args' => is_int($priority) + ? array($action, $callback, $priority) + : array($action, $callback), + 'times' => 1, + ) + ); + } + + /** + * Adds an expectation that a filter should be removed. + * + * @param string $action the filter name + * @param string|callable-string|callable|Type $callback the callable to be removed + * @param ?int $priority the registered priority + * + * @return void + * @throws InvalidArgumentException + */ + public static function expectFilterRemoved(string $action, $callback, ?int $priority = null) : void + { + self::userFunction( + 'remove_filter', + array( + 'args' => is_int($priority) + ? array($action, $callback, $priority) + : array($action, $callback), + 'times' => 1, + ) + ); + } } From 67017782c351c0fceb9d74c53545cb424e1566a1 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Tue, 25 Jun 2024 22:38:45 -0700 Subject: [PATCH 2/7] Rename `$action` to `$filter` in filter related function --- php/WP_Mock.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index cb3cfbb..c8e7a46 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -573,21 +573,21 @@ public static function expectActionRemoved(string $action, $callback, ?int $prio /** * Adds an expectation that a filter should be removed. * - * @param string $action the filter name + * @param string $filter the filter name * @param string|callable-string|callable|Type $callback the callable to be removed * @param ?int $priority the registered priority * * @return void * @throws InvalidArgumentException */ - public static function expectFilterRemoved(string $action, $callback, ?int $priority = null) : void + public static function expectFilterRemoved(string $filter, $callback, ?int $priority = null) : void { self::userFunction( 'remove_filter', array( 'args' => is_int($priority) - ? array($action, $callback, $priority) - : array($action, $callback), + ? array($filter, $callback, $priority) + : array($filter, $callback), 'times' => 1, ) ); From e0aa1ddc44d8cde0c4fad9680e8a0f72e636bcba Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Wed, 26 Jun 2024 18:50:42 -0700 Subject: [PATCH 3/7] Add expectHookRemoved tests --- php/WP_Mock.php | 8 ++------ tests/Integration/WP_MockTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index c8e7a46..31f32de 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -562,9 +562,7 @@ public static function expectActionRemoved(string $action, $callback, ?int $prio self::userFunction( 'remove_action', array( - 'args' => is_int($priority) - ? array($action, $callback, $priority) - : array($action, $callback), + 'args' => array_filter(func_get_args()), 'times' => 1, ) ); @@ -585,9 +583,7 @@ public static function expectFilterRemoved(string $filter, $callback, ?int $prio self::userFunction( 'remove_filter', array( - 'args' => is_int($priority) - ? array($filter, $callback, $priority) - : array($filter, $callback), + 'args' => array_filter(func_get_args()), 'times' => 1, ) ); diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index 04461fb..ad5f6e1 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -336,4 +336,29 @@ public function testCanExpectHooksNotAdded() : void $this->assertConditionsMet(); } + + /** + * @covers \WP_Mock::expectActionRemoved() + * @covers \WP_Mock::expectFilterRemoved() + * + * @return void + * @throws Exception + */ + public function testCanExpectHooksRemoved() : void + { + WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10); + WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10); + + WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + + remove_action('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10); + remove_filter('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10); + + remove_action('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + remove_filter('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + + $this->assertConditionsMet(); + } + } } From 22898a2cefa40e294a47508489c88284026345b7 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Wed, 26 Jun 2024 19:02:02 -0700 Subject: [PATCH 4/7] Add expectHookNotRemoved functions --- php/WP_Mock.php | 42 +++++++++++++++++++++++++++++++ tests/Integration/WP_MockTest.php | 17 +++++++++++++ 2 files changed, 59 insertions(+) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 31f32de..522de6f 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -568,6 +568,27 @@ public static function expectActionRemoved(string $action, $callback, ?int $prio ); } + /** + * Adds an expectation that an action should not be removed. + * + * @param string $action the action hook name + * @param null|string|callable-string|callable|Type $callback the callable to be removed + * @param ?int $priority optional priority for the registered callback that is being removed + * + * @return void + * @throws InvalidArgumentException + */ + public static function expectActionNotRemoved(string $action, $callback, $priority = null) : void + { + self::userFunction( + 'remove_action', + array( + 'args' => array_filter(func_get_args()), + 'times' => 0, + ) + ); + } + /** * Adds an expectation that a filter should be removed. * @@ -588,4 +609,25 @@ public static function expectFilterRemoved(string $filter, $callback, ?int $prio ) ); } + + /** + * Adds an expectation that a filter should not be removed. + * + * @param string $filter the filter name + * @param null|string|callable-string|callable|Type $callback the callable to be removed + * @param ?int $priority optional priority for the registered callback that is being removed + * + * @return void + * @throws InvalidArgumentException + */ + public static function expectFilterNotRemoved(string $filter, $callback, $priority = null) : void + { + self::userFunction( + 'remove_filter', + array( + 'args' => array_filter(func_get_args()), + 'times' => 0, + ) + ); + } } diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index ad5f6e1..efa3abc 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -360,5 +360,22 @@ public function testCanExpectHooksRemoved() : void $this->assertConditionsMet(); } + + /** + * @covers \WP_Mock::expectActionNotRemoved() + * @covers \WP_Mock::expectFilterNotRemoved() + * + * @return void + * @throws Exception + */ + public function testCanExpectHooksNotRemoved() : void + { + WP_Mock::expectActionNotRemoved('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10); + WP_Mock::expectFilterNotRemoved('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10); + + WP_Mock::expectActionNotRemoved('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + WP_Mock::expectFilterNotRemoved('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction'); + + $this->assertConditionsMet(); } } From 7d01fcb251bd6be87396602422d13a810ec58b4b Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Wed, 26 Jun 2024 19:10:47 -0700 Subject: [PATCH 5/7] Remove strict typing to allow `::type( 'int' )` `\WP_Mock\Functions::type( 'int' )` --- php/WP_Mock.php | 12 ++++++------ tests/Integration/WP_MockTest.php | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 522de6f..5f70ea2 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -552,12 +552,12 @@ public static function getDeprecatedMethodListener(): DeprecatedMethodListener * * @param string $action the action hook name * @param string|callable-string|callable|Type $callback the callable to be removed - * @param ?int $priority the priority it should be registered at + * @param ?int|Type $priority the priority it should be registered at * * @return void * @throws InvalidArgumentException */ - public static function expectActionRemoved(string $action, $callback, ?int $priority = null) : void + public static function expectActionRemoved(string $action, $callback, $priority = null) : void { self::userFunction( 'remove_action', @@ -573,7 +573,7 @@ public static function expectActionRemoved(string $action, $callback, ?int $prio * * @param string $action the action hook name * @param null|string|callable-string|callable|Type $callback the callable to be removed - * @param ?int $priority optional priority for the registered callback that is being removed + * @param ?int|Type $priority optional priority for the registered callback that is being removed * * @return void * @throws InvalidArgumentException @@ -594,12 +594,12 @@ public static function expectActionNotRemoved(string $action, $callback, $priori * * @param string $filter the filter name * @param string|callable-string|callable|Type $callback the callable to be removed - * @param ?int $priority the registered priority + * @param ?int|Type $priority the registered priority * * @return void * @throws InvalidArgumentException */ - public static function expectFilterRemoved(string $filter, $callback, ?int $priority = null) : void + public static function expectFilterRemoved(string $filter, $callback, $priority = null) : void { self::userFunction( 'remove_filter', @@ -615,7 +615,7 @@ public static function expectFilterRemoved(string $filter, $callback, ?int $prio * * @param string $filter the filter name * @param null|string|callable-string|callable|Type $callback the callable to be removed - * @param ?int $priority optional priority for the registered callback that is being removed + * @param ?int|Type $priority optional priority for the registered callback that is being removed * * @return void * @throws InvalidArgumentException diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index efa3abc..a13a512 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -349,12 +349,26 @@ public function testCanExpectHooksRemoved() : void WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10); WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10); + WP_Mock::expectActionRemoved( + 'wpMockTestActionWithCallbackAndAnyPriority', + 'wpMockTestFunction', + \WP_Mock\Functions::type('int') + ); + WP_Mock::expectFilterRemoved( + 'wpMockTestFilterWithCallbackAndAnyPriority', + 'wpMockTestFunction', + \WP_Mock\Functions::type('int') + ); + WP_Mock::expectActionRemoved('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction'); WP_Mock::expectFilterRemoved('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction'); remove_action('wpMockTestActionWithCallbackAndPriority', 'wpMockTestFunction', 10); remove_filter('wpMockTestFilterWithCallbackAndPriority', 'wpMockTestFunction', 10); + remove_action('wpMockTestActionWithCallbackAndAnyPriority', 'wpMockTestFunction', rand(1,100)); + remove_filter('wpMockTestFilterWithCallbackAndAnyPriority', 'wpMockTestFunction', rand(1,100)); + remove_action('wpMockTestActionWithCallbackAndDefaultPriority', 'wpMockTestFunction'); remove_filter('wpMockTestFilterWithCallbackAndDefaultPriority', 'wpMockTestFunction'); From cd4cff50eca56e67491aafbd772f0e7e988bb388 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Wed, 26 Jun 2024 19:14:29 -0700 Subject: [PATCH 6/7] update functions' type annotations --- php/WP_Mock.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 5f70ea2..e73a5f9 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -552,7 +552,7 @@ public static function getDeprecatedMethodListener(): DeprecatedMethodListener * * @param string $action the action hook name * @param string|callable-string|callable|Type $callback the callable to be removed - * @param ?int|Type $priority the priority it should be registered at + * @param int|Type|null $priority the priority it should be registered at * * @return void * @throws InvalidArgumentException @@ -573,7 +573,7 @@ public static function expectActionRemoved(string $action, $callback, $priority * * @param string $action the action hook name * @param null|string|callable-string|callable|Type $callback the callable to be removed - * @param ?int|Type $priority optional priority for the registered callback that is being removed + * @param int|Type|null $priority optional priority for the registered callback that is being removed * * @return void * @throws InvalidArgumentException @@ -594,7 +594,7 @@ public static function expectActionNotRemoved(string $action, $callback, $priori * * @param string $filter the filter name * @param string|callable-string|callable|Type $callback the callable to be removed - * @param ?int|Type $priority the registered priority + * @param int|Type|null $priority the registered priority * * @return void * @throws InvalidArgumentException @@ -615,7 +615,7 @@ public static function expectFilterRemoved(string $filter, $callback, $priority * * @param string $filter the filter name * @param null|string|callable-string|callable|Type $callback the callable to be removed - * @param ?int|Type $priority optional priority for the registered callback that is being removed + * @param int|Type|null $priority optional priority for the registered callback that is being removed * * @return void * @throws InvalidArgumentException From f56c5575258aaa920acd18b87986f4011af1a04e Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 27 Jun 2024 15:51:56 -0700 Subject: [PATCH 7/7] Add doc: Asserting that actions and filters have been removed --- .../mocking-wp-action-and-filter-hooks.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/usage/mocking-wp-action-and-filter-hooks.md b/docs/usage/mocking-wp-action-and-filter-hooks.md index fde8a9b..973ec40 100644 --- a/docs/usage/mocking-wp-action-and-filter-hooks.md +++ b/docs/usage/mocking-wp-action-and-filter-hooks.md @@ -140,4 +140,25 @@ final class MyClassTest extends TestCase $this->assertEquals('Default', (new MyClass())->filterContent()); } } -``` \ No newline at end of file +``` + +## Asserting that actions and filters have been removed + +Similarly, we can test that actions and filters are removed when expected, e.g.removing another plugin's admin notice. This is done using `WP_Mock::expectActionRemoved()` and `WP_Mock::expectFilterRemoved()`. Or conversely, we can confirm that they have _not_ been removed using `WP_Mock::expectActionNotRemoved()` and `WP_Mock::expectFilterNotRemoved()`. The latter functions are useful where a function being tested returns early in some scenarios, and we want to ensure that the hooks are not removed in that case. + +```php +use MyPlugin\MyClass; +use WP_Mock\Tools\TestCase as TestCase; + +final class MyClassTest extends TestCase +{ + public function testRemoveAction() : void + { + $classInstance = new MyClass(); + + WP_Mock::expectActionRemoved('admin_notices', 'invasive_admin_notice'); + + $classInstance->removeInvasiveAdminNotice(); + } +} +```