From 7beb6a0e9e03a61e54952ff598c02537d542e444 Mon Sep 17 00:00:00 2001 From: Thor Brink Date: Wed, 15 Jan 2025 13:49:18 +0100 Subject: [PATCH] refactor: move UserGroup parts of MiniOrange integration into separate UserGroup feature (#1250) --- .../RedirectUserToGroupUrlIfIsPreferred.php | 17 +++- library/App.php | 68 +++++++++----- .../Contracts/CreateOrGetTermIdFromString.php | 15 +++ library/Helper/Term/Term.php | 31 +++++-- library/Helper/Term/Term.test.php | 39 ++++++++ .../MiniOrange/SetGroupAsTaxonomy.php | 92 ------------------- .../SetUserGroupFromSsoLoginGroup.php | 76 +++++++++++++++ .../SetUserGroupFromSsoLoginGroup.test.php | 85 +++++++++++++++++ library/UserGroup/Config/UserGroupConfig.php | 38 ++++++++ .../UserGroup/Config/UserGroupConfig.test.php | 35 +++++++ .../Config/UserGroupConfigInterface.php | 20 ++++ .../CreateUserGroupTaxonomy.php | 36 +++++--- .../CreateUserGroupTaxonomy.test.php | 55 +++++++++++ .../DisplayUserGroupTaxonomyInUserProfile.php | 12 ++- .../DisplayUserGroupTaxonomyInUsersList.php | 12 ++- .../DisplayUserGroupTaxonomyLinkInAdminUi.php | 12 ++- .../PopulateUserGroupUrlBlogIdField.php | 15 ++- tests/phpunit/TestUtils/WpMockFactory.php | 11 +++ 18 files changed, 519 insertions(+), 150 deletions(-) create mode 100644 library/Helper/Term/Contracts/CreateOrGetTermIdFromString.php delete mode 100644 library/Integrations/MiniOrange/SetGroupAsTaxonomy.php create mode 100644 library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.php create mode 100644 library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.test.php create mode 100644 library/UserGroup/Config/UserGroupConfig.php create mode 100644 library/UserGroup/Config/UserGroupConfig.test.php create mode 100644 library/UserGroup/Config/UserGroupConfigInterface.php rename library/{Integrations/MiniOrange => UserGroup}/CreateUserGroupTaxonomy.php (62%) create mode 100644 library/UserGroup/CreateUserGroupTaxonomy.test.php rename library/{Integrations/MiniOrange => UserGroup}/DisplayUserGroupTaxonomyInUserProfile.php (92%) rename library/{Integrations/MiniOrange => UserGroup}/DisplayUserGroupTaxonomyInUsersList.php (89%) rename library/{Integrations/MiniOrange => UserGroup}/DisplayUserGroupTaxonomyLinkInAdminUi.php (81%) rename library/{Integrations/MiniOrange => UserGroup}/PopulateUserGroupUrlBlogIdField.php (78%) diff --git a/library/Admin/Login/RedirectUserToGroupUrlIfIsPreferred.php b/library/Admin/Login/RedirectUserToGroupUrlIfIsPreferred.php index 1a0b30833..eeacbe110 100644 --- a/library/Admin/Login/RedirectUserToGroupUrlIfIsPreferred.php +++ b/library/Admin/Login/RedirectUserToGroupUrlIfIsPreferred.php @@ -5,10 +5,17 @@ use Municipio\HooksRegistrar\Hookable; use WpService\WpService; use Municipio\Helper\User\User; +use WpService\Contracts\AddQueryArg; +/** + * Redirect user to group url if user prefers group url + */ class RedirectUserToGroupUrlIfIsPreferred implements Hookable { - public function __construct(private WpService $wpService, private User $userHelper) + /** + * Constructor. + */ + public function __construct(private WpService&AddQueryArg $wpService, private User $userHelper) { } @@ -39,12 +46,14 @@ public function redirectToGroupUrl($redirectTo, $request, $userInHook) if ($user != null) { $perfersGroupUrl = $this->userHelper->getUserPrefersGroupUrl(); $groupUrl = $this->userHelper->getUserGroupUrl(); - + if ($perfersGroupUrl && $groupUrl) { - return add_query_arg(['offerPersistantHomeUrl' => 'true'], $groupUrl); + return $this->wpService->addQueryArg([ + 'loggedin' => 'true', + 'prefersgroup' => 'true' + ], $groupUrl); } } return $redirectTo; } - } diff --git a/library/App.php b/library/App.php index 40764cab7..5fd8d6078 100644 --- a/library/App.php +++ b/library/App.php @@ -358,6 +358,11 @@ public function __construct( */ $this->setupLoginLogout(); + /** + * UserGroup feature + */ + $this->setupUserGroupFeature(); + /** * MiniOrange integration */ @@ -426,6 +431,38 @@ private function setupLoginLogout(): void $redirectUserToGroupUrlIfIsPrefered->addHooks(); } + /** + * Set up the user group feature. + */ + private function setupUserGroupFeature(): void + { + $config = new \Municipio\UserGroup\Config\UserGroupConfig(); + + if ($config->isEnabled() === false) { + return; + } + + // Create user group taxonomy + $createUserGroupTaxonomy = new \Municipio\UserGroup\CreateUserGroupTaxonomy($this->wpService, $config); + $createUserGroupTaxonomy->addHooks(); + + // Add user group to users list + $displayUserGroupTaxonomyInUsersList = new \Municipio\UserGroup\DisplayUserGroupTaxonomyInUsersList($this->wpService, $config); + $displayUserGroupTaxonomyInUsersList->addHooks(); + + // Add user group to admin menu + $displayUserGroupTaxonomyLinkInAdminUi = new \Municipio\UserGroup\DisplayUserGroupTaxonomyLinkInAdminUi($this->wpService, $config); + $displayUserGroupTaxonomyLinkInAdminUi->addHooks(); + + // Add user group to user profile & populate + $displayUserGroupTaxonomyInUserProfile = new \Municipio\UserGroup\DisplayUserGroupTaxonomyInUserProfile($this->wpService, $this->acfService, $config); + $displayUserGroupTaxonomyInUserProfile->addHooks(); + + // User group url + $populateUserGroupUrlBlogIdField = new \Municipio\UserGroup\PopulateUserGroupUrlBlogIdField($this->wpService); + $populateUserGroupUrlBlogIdField->addHooks(); + } + /** * Set up the MiniOrange integration. * @@ -435,7 +472,10 @@ private function setupLoginLogout(): void */ private function setUpMiniOrangeIntegration(): void { - $config = new \Municipio\Integrations\MiniOrange\Config\MiniOrangeConfig($this->wpService); + $termHelper = new \Municipio\Helper\Term\Term($this->wpService, $this->acfService); + $userGroupConfig = new \Municipio\UserGroup\Config\UserGroupConfig(); + $config = new \Municipio\Integrations\MiniOrange\Config\MiniOrangeConfig($this->wpService); + if ($config->isEnabled() === false) { return; } @@ -451,29 +491,13 @@ private function setUpMiniOrangeIntegration(): void $attributeMapper = new \Municipio\Integrations\MiniOrange\AttributeMapper($this->wpService, $config, ...$mappingProviders); $attributeMapper->addHooks(); - //Create user group taxonomy - $createUserGroupTaxonomy = new \Municipio\Integrations\MiniOrange\CreateUserGroupTaxonomy($this->wpService, $config); - $createUserGroupTaxonomy->addHooks(); + if ($userGroupConfig->isEnabled() === false) { + return; + } - //Set group as taxonomy - $setGroupAsTaxonomy = new \Municipio\Integrations\MiniOrange\SetGroupAsTaxonomy($this->wpService, $config); + // Set group as taxonomy + $setGroupAsTaxonomy = new \Municipio\Integrations\MiniOrange\SetUserGroupFromSsoLoginGroup($this->wpService, $termHelper, $userGroupConfig); $setGroupAsTaxonomy->addHooks(); - - //Add user group to users list - $displayUserGroupTaxonomyInUsersList = new \Municipio\Integrations\MiniOrange\DisplayUserGroupTaxonomyInUsersList($this->wpService, $config); - $displayUserGroupTaxonomyInUsersList->addHooks(); - - //Add user group to admin menu - $displayUserGroupTaxonomyLinkInAdminUi = new \Municipio\Integrations\MiniOrange\DisplayUserGroupTaxonomyLinkInAdminUi($this->wpService, $config); - $displayUserGroupTaxonomyLinkInAdminUi->addHooks(); - - //Add user group to user profile & populate - $displayUserGroupTaxonomyInUserProfile = new \Municipio\Integrations\MiniOrange\DisplayUserGroupTaxonomyInUserProfile($this->wpService, $this->acfService, $config); - $displayUserGroupTaxonomyInUserProfile->addHooks(); - - //User group url - $populateUserGroupUrlBlogIdField = new \Municipio\Integrations\MiniOrange\PopulateUserGroupUrlBlogIdField($this->wpService); - $populateUserGroupUrlBlogIdField->addHooks(); } /** diff --git a/library/Helper/Term/Contracts/CreateOrGetTermIdFromString.php b/library/Helper/Term/Contracts/CreateOrGetTermIdFromString.php new file mode 100644 index 000000000..f74cc16df --- /dev/null +++ b/library/Helper/Term/Contracts/CreateOrGetTermIdFromString.php @@ -0,0 +1,15 @@ +wpService->getTermBy('name', $termString, $taxonomy, 'OBJECT'); + + if (!$term) { + $result = $this->wpService->wpInsertTerm($termString, $taxonomy); + if ($this->wpService->isWpError($result)) { + return null; + } + $termId = $result['term_id']; + } else { + $termId = $term->term_id; + } + + return $termId ?? null; + } } diff --git a/library/Helper/Term/Term.test.php b/library/Helper/Term/Term.test.php index bdc625981..0a0aff9b3 100644 --- a/library/Helper/Term/Term.test.php +++ b/library/Helper/Term/Term.test.php @@ -189,4 +189,43 @@ public function testGetTermIconReturnsTheSvgIconOfTheTermIfItHasOne() $this->assertEquals('description', $icon['description']); $this->assertEquals('description', $icon['alt']); } + + /** + * @testdox createOrGetTermIdFromString() returns termId if term already exists + */ + public function testCreateOrGetTermIdFromStringReturnsTermIdIfTermAlreadyExists() + { + $wpService = new FakeWpService(['getTermBy' => WpMockFactory::createWpTerm(['term_id' => 123])]); + $termHelper = new Term($wpService, new FakeAcfService()); + + $this->assertEquals(123, $termHelper->createOrGetTermIdFromString('term', 'category')); + } + + /** + * @testdox createOrGetTermIdFromString() returns null if term could no be created + */ + public function testCreateOrGetTermIdFromStringReturnsNullIfTermCouldNotBeCreated() + { + $wpService = new FakeWpService([ + 'getTermBy' => false, + 'wpInsertTerm' => WpMockFactory::createWpError(), + 'isWpError' => true]); + $termHelper = new Term($wpService, new FakeAcfService()); + + $this->assertNull($termHelper->createOrGetTermIdFromString('term', 'category')); + } + + /** + * @testdox createOrGetTermIdFromString() returns termId if term was created + */ + public function testCreateOrGetTermIdFromStringReturnsTermIdIfTermWasCreated() + { + $wpService = new FakeWpService([ + 'getTermBy' => false, + 'wpInsertTerm' => ['term_id' => 123], + 'isWpError' => false]); + $termHelper = new Term($wpService, new FakeAcfService()); + + $this->assertEquals(123, $termHelper->createOrGetTermIdFromString('term', 'category')); + } } diff --git a/library/Integrations/MiniOrange/SetGroupAsTaxonomy.php b/library/Integrations/MiniOrange/SetGroupAsTaxonomy.php deleted file mode 100644 index 296f81ad6..000000000 --- a/library/Integrations/MiniOrange/SetGroupAsTaxonomy.php +++ /dev/null @@ -1,92 +0,0 @@ -wpService->addAction('mo_saml_user_group_name', array($this, 'setGroupAsTaxonomy'), 10, 2); - } - - /** - * Set the group as a taxonomy - * - * @param int $userId - * @param string|array $groupName - * @return void - */ - public function setGroupAsTaxonomy(int $userId, string|array $groupName): void - { - $taxonomy = $this->config->getUserGroupTaxonomy(); - $groupName = $this->getGroupNameFromMixed($groupName); - - if (!$taxonomy) { - return; - } - - if (!$groupName || is_numeric($groupName)) { - return; - } - - if (!$userId) { - return; - } - - if ($termId = $this->createOrGetTermFromString($groupName, $taxonomy)) { - $this->wpService->wpSetObjectTerms($userId, $termId, $taxonomy, false); - } - } - - /** - * Set the group as a taxonomy - * - * @param string|array $groupName - * @return string|null $groupName - */ - private function getGroupNameFromMixed(mixed $groupName): ?string - { - if (empty($groupName)) { - return null; - } - if (is_array($groupName)) { - $groupName = $groupName[0] ?? null; - } - return $groupName; - } - - /** - * Create a taxonomy from a string - * - * @param string|array $groupName - * @param string $taxonomy - * @return int|null - */ - private function createOrGetTermFromString(string $groupName, $taxonomy): ?int - { - $term = $this->wpService->getTermBy('name', $groupName, $taxonomy); - if (!$term) { - $result = $this->wpService->wpInsertTerm($groupName, $taxonomy); - if ($this->wpService->isWpError($result)) { - return null; - } - $termId = $result['term_id']; - } else { - $termId = $term->term_id; - } - return $termId ?? null; - } -} diff --git a/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.php b/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.php new file mode 100644 index 000000000..60cdbc0b7 --- /dev/null +++ b/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.php @@ -0,0 +1,76 @@ +wpService->addAction('mo_saml_user_group_name', array($this, 'setUserGroupFromSsoLoginGroup'), 10, 2); + } + + /** + * Set the group as a taxonomy + * + * @param int $userId + * @param string|array $groupName + * @return void + */ + public function setUserGroupFromSsoLoginGroup(int $userId, string|array $groupName): void + { + $taxonomy = $this->config->getUserGroupTaxonomy(); + $groupName = $this->getGroupNameFromMixed($groupName); + + if (!$groupName || is_numeric($groupName)) { + return; + } + + if (!$userId) { + return; + } + + if ($termId = $this->termHelper->createOrGetTermIdFromString($groupName, $taxonomy)) { + $this->wpService->wpSetObjectTerms($userId, $termId, $taxonomy, false); + } + } + + /** + * Set the group as a taxonomy + * + * @param string|array $groupName + * @return string|null $groupName + */ + private function getGroupNameFromMixed(mixed $groupName): ?string + { + if (empty($groupName)) { + return null; + } + if (is_array($groupName)) { + $groupName = $groupName[0] ?? null; + } + return $groupName; + } +} diff --git a/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.test.php b/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.test.php new file mode 100644 index 000000000..745359f75 --- /dev/null +++ b/library/Integrations/MiniOrange/SetUserGroupFromSsoLoginGroup.test.php @@ -0,0 +1,85 @@ +createMock(CreateOrGetTermIdFromString::class); + $config = $this->createMock(UserGroupConfigInterface::class); + $setGroupAsTaxonomy = new SetUserGroupFromSsoLoginGroup(new FakeWpService(), $termHelper, $config); + + $this->assertInstanceOf(SetUserGroupFromSsoLoginGroup::class, $setGroupAsTaxonomy); + } + + /** + * @testdox setUserGroupFromSsoLoginGroup() does not connect user to term if groupName is numeric + */ + public function testSetUserGroupFromSsoLoginGroupDoesNotConnectUserToTermIfGroupNameIsNumeric() + { + $wpService = new FakeWpService(); + $termHelper = $this->createMock(CreateOrGetTermIdFromString::class); + $config = $this->createMock(UserGroupConfigInterface::class); + + $setGroupAsTaxonomy = new SetUserGroupFromSsoLoginGroup($wpService, $termHelper, $config); + $setGroupAsTaxonomy->setUserGroupFromSsoLoginGroup(1, 1); + + $this->assertArrayNotHasKey('wpSetObjectTerms', $wpService->methodCalls); + } + + /** + * @testdox setUserGroupFromSsoLoginGroup() does not connect user to term if groupName is empty + */ + public function testSetUserGroupFromSsoLoginGroupDoesNotConnectUserToTermIfGroupNameIsEmpty() + { + $wpService = new FakeWpService(); + $termHelper = $this->createMock(CreateOrGetTermIdFromString::class); + $config = $this->createMock(UserGroupConfigInterface::class); + + $setGroupAsTaxonomy = new SetUserGroupFromSsoLoginGroup($wpService, $termHelper, $config); + $setGroupAsTaxonomy->setUserGroupFromSsoLoginGroup(1, ''); + + $this->assertArrayNotHasKey('wpSetObjectTerms', $wpService->methodCalls); + } + + /** + * @testdox setUserGroupFromSsoLoginGroup() does not connect user to term if userId is 0 + */ + public function testSetUserGroupFromSsoLoginGroupDoesNotConnectUserToTermIfUserIdIsEmpty() + { + $wpService = new FakeWpService(); + $termHelper = $this->createMock(CreateOrGetTermIdFromString::class); + $config = $this->createMock(UserGroupConfigInterface::class); + + $setGroupAsTaxonomy = new SetUserGroupFromSsoLoginGroup($wpService, $termHelper, $config); + $setGroupAsTaxonomy->setUserGroupFromSsoLoginGroup(0, 'group'); + + $this->assertArrayNotHasKey('wpSetObjectTerms', $wpService->methodCalls); + } + + /** + * @testdox setUserGroupFromSsoLoginGroup() connects user to term + */ + public function testSetUserGroupFromSsoLoginGroupConnectsUserToTerm() + { + $wpService = new FakeWpService(['wpSetObjectTerms' => []]); + $termHelper = $this->createMock(CreateOrGetTermIdFromString::class); + $config = $this->createMock(UserGroupConfigInterface::class); + + $termHelper->method('createOrGetTermIdFromString')->willReturn(1); + + $setGroupAsTaxonomy = new SetUserGroupFromSsoLoginGroup($wpService, $termHelper, $config); + $setGroupAsTaxonomy->setUserGroupFromSsoLoginGroup(1, 'group'); + + $this->assertArrayHasKey('wpSetObjectTerms', $wpService->methodCalls); + } +} diff --git a/library/UserGroup/Config/UserGroupConfig.php b/library/UserGroup/Config/UserGroupConfig.php new file mode 100644 index 000000000..124284828 --- /dev/null +++ b/library/UserGroup/Config/UserGroupConfig.php @@ -0,0 +1,38 @@ +assertInstanceOf(UserGroupConfig::class, $userGroupConfig); + } + + /** + * @testdox isEnabled returns true + */ + public function testIsEnabledReturnsTrue() + { + $userGroupConfig = new UserGroupConfig(); + $this->assertTrue($userGroupConfig->isEnabled()); + } + + /** + * @testdox getUserGroupTaxonomy() returns expected taxonomy name + */ + public function testGetUserGroupTaxonomyReturnsExpectedTaxonomyName() + { + $userGroupConfig = new UserGroupConfig(); + $this->assertEquals('user_group', $userGroupConfig->getUserGroupTaxonomy()); + } +} diff --git a/library/UserGroup/Config/UserGroupConfigInterface.php b/library/UserGroup/Config/UserGroupConfigInterface.php new file mode 100644 index 000000000..cbeb6d63d --- /dev/null +++ b/library/UserGroup/Config/UserGroupConfigInterface.php @@ -0,0 +1,20 @@ +wpService->addAction('init', array($this, 'registerUserGroupTaxonomy'), 5); } - /** - * Register the user group taxonomy - * - * @return void - */ + /** + * Register the user group taxonomy + * + * @return void + */ public function registerUserGroupTaxonomy(): void { $taxonomy = $this->config->getUserGroupTaxonomy(); @@ -35,7 +43,7 @@ public function registerUserGroupTaxonomy(): void array( 'label' => $this->wpService->__('User Groups', 'municipio'), 'hierarchical' => false, - 'public' => false, + 'public' => false, 'show_ui' => true, 'show_in_rest' => false, 'capabilities' => array( diff --git a/library/UserGroup/CreateUserGroupTaxonomy.test.php b/library/UserGroup/CreateUserGroupTaxonomy.test.php new file mode 100644 index 000000000..caaf862fb --- /dev/null +++ b/library/UserGroup/CreateUserGroupTaxonomy.test.php @@ -0,0 +1,55 @@ +createMock(UserGroupConfigInterface::class); + $createUserGroupTaxonomy = new CreateUserGroupTaxonomy($wpService, $config); + + $this->assertInstanceOf(CreateUserGroupTaxonomy::class, $createUserGroupTaxonomy); + } + + /** + * @testdox addHooks method runs registerUserGroupTaxonomy on init hook with expected priority + */ + public function testAddHooksMethodRunsRegisterUserGroupTaxonomyOnInitHook() + { + $wpService = new FakeWpService(['addAction' => true]); + $config = $this->createMock(UserGroupConfigInterface::class); + $createUserGroupTaxonomy = new CreateUserGroupTaxonomy($wpService, $config); + + $createUserGroupTaxonomy->addHooks(); + + $this->assertEquals('init', $wpService->methodCalls['addAction'][0][0]); + $this->assertEquals([$createUserGroupTaxonomy, 'registerUserGroupTaxonomy'], $wpService->methodCalls['addAction'][0][1]); + $this->assertEquals(5, $wpService->methodCalls['addAction'][0][2]); + } + + /** + * @testdox registerUserGroupTaxonomy() registers a taxonomy with name from config + */ + public function testRegisterUserGroupTaxonomyRegistersTaxonomy() + { + $wpService = new FakeWpService(['registerTaxonomy' => true, '__' => fn($string) => $string, 'registerTaxonomy' => WpMockFactory::createWpTaxonomy()]); + $config = $this->createMock(UserGroupConfigInterface::class); + $config->method('getUserGroupTaxonomy')->willReturn('test_taxonomy'); + $createUserGroupTaxonomy = new CreateUserGroupTaxonomy($wpService, $config); + + $createUserGroupTaxonomy->registerUserGroupTaxonomy(); + + $this->assertEquals('test_taxonomy', $wpService->methodCalls['registerTaxonomy'][0][0]); + } +} diff --git a/library/Integrations/MiniOrange/DisplayUserGroupTaxonomyInUserProfile.php b/library/UserGroup/DisplayUserGroupTaxonomyInUserProfile.php similarity index 92% rename from library/Integrations/MiniOrange/DisplayUserGroupTaxonomyInUserProfile.php rename to library/UserGroup/DisplayUserGroupTaxonomyInUserProfile.php index e25f2a556..433551398 100644 --- a/library/Integrations/MiniOrange/DisplayUserGroupTaxonomyInUserProfile.php +++ b/library/UserGroup/DisplayUserGroupTaxonomyInUserProfile.php @@ -1,15 +1,21 @@ taxonomy = $this->config->getUserGroupTaxonomy(); } diff --git a/library/Integrations/MiniOrange/DisplayUserGroupTaxonomyLinkInAdminUi.php b/library/UserGroup/DisplayUserGroupTaxonomyLinkInAdminUi.php similarity index 81% rename from library/Integrations/MiniOrange/DisplayUserGroupTaxonomyLinkInAdminUi.php rename to library/UserGroup/DisplayUserGroupTaxonomyLinkInAdminUi.php index bf4371c90..29363893d 100644 --- a/library/Integrations/MiniOrange/DisplayUserGroupTaxonomyLinkInAdminUi.php +++ b/library/UserGroup/DisplayUserGroupTaxonomyLinkInAdminUi.php @@ -1,14 +1,20 @@ wpService->addFilter('acf/load_field/key=field_677e6b8534123', array($this, 'populateUserGroupUrlBlogIdField')); @@ -23,8 +32,8 @@ public function addHooks(): void */ public function populateUserGroupUrlBlogIdField($field) { - if(!$this->wpService->isMultisite()) { - return $field; + if (!$this->wpService->isMultisite()) { + return $field; } $field['choices'] = []; $blogs = $this->wpService->getSites(['number' => 500]) ?? []; diff --git a/tests/phpunit/TestUtils/WpMockFactory.php b/tests/phpunit/TestUtils/WpMockFactory.php index f0918430f..6d3976ad9 100644 --- a/tests/phpunit/TestUtils/WpMockFactory.php +++ b/tests/phpunit/TestUtils/WpMockFactory.php @@ -9,6 +9,7 @@ use WP_Error; use WP_Post; use WP_Term; +use WP_Taxonomy; use wpdb; /** @@ -56,6 +57,16 @@ public static function createWpCommentQuery(array $args = []): MockObject|WP_Com return self::buildMockWithArgs('WP_Comment_Query', $args); } + /** + * Create a mock WP_Taxonomy object. + * + * @param array $args + */ + public static function createWpTaxonomy(array $args = []): MockObject|WP_Taxonomy + { + return self::buildMockWithArgs('WP_Taxonomy', $args); + } + /** * Create a mock wpdb object. *