This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Feature: add categories system - #102

Merged
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system
Oct 13, 2025
Merged

Feature: add categories system#102
galatanovidiu merged 53 commits into
WordPress:trunkfrom
galatanovidiu:feature/add-categories-system

Conversation

@galatanovidiu

@galatanovidiugalatanovidiu commented Oct 6, 2025

Copy link
Copy Markdown
Contributor

Summary

Implements a comprehensive category system for organizing abilities. Each ability must now belong to exactly one category, improving discoverability and enabling filtering of abilities by their purpose.


⚠️ Breaking Changes

Required category Parameter

All abilities must now specify a category when registering.

Before:

wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
// ... other args
));

After:

// First, register a categoryadd_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
wp_register_ability_category( 'data-retrieval', array(
'label' => 'Data Retrieval',
'description' => 'Abilities that retrieve data',
));
}
// Then register ability with categoryadd_action( 'abilities_api_init', 'my_plugin_register_ability' );
functionmy_plugin_register_ability() {
wp_register_ability( 'my-plugin/get-data', array(
'label' => 'Get Data',
'description' => 'Retrieves data',
'category' => 'data-retrieval', // REQUIRED// ... other args
));
}

What's Changed

New Classes

WP_Ability_Category

Encapsulates category properties (slug, label, description).

Location: includes/abilities-api/class-wp-ability-category.php

WP_Abilities_Category_Registry

Singleton registry managing all registered categories.

Location: includes/abilities-api/class-wp-abilities-category-registry.php

Features:

  • Validates category slugs (lowercase alphanumeric + dashes only)
  • Requires label and description for all categories
  • Prevents duplicate category registration
  • Fires abilities_api_category_registry_init hook on initialization
  • Applies register_ability_category_args filter before registration

Core Changes to WP_Ability

File: includes/abilities-api/class-wp-ability.php

  1. Added $category property (required string)
  2. Added get_category() method
  3. Category validation in constructor:
    • Must be non-empty string
    • Must match slug format: ^[a-z0-9]+(-[a-z0-9]+)*$
  4. Category is now part of ability's required properties

Changes to WP_Abilities_Registry

File: includes/abilities-api/class-wp-abilities-registry.php

  1. Added get_abilities_by_category( string $category ) method
  2. Category validation during ability registration:
    • Checks if category exists before registering ability
    • Returns null and triggers _doing_it_wrong() if category not found
  3. Ensures category registry initializes before ability registry

New API Functions

File: includes/abilities-api.php

Category Management

// Register a categorywp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category
// Unregister a categorywp_unregister_ability_category( string $slug ): ?WP_Ability_Category
// Get a specific categorywp_get_ability_category( string $slug ): ?WP_Ability_Category
// Get all categorieswp_get_ability_categories(): array

Ability Filtering

// Get abilities by categorywp_get_abilities_by_category( string $category ): WP_Ability[]

New Hooks

Action: abilities_api_category_registry_init

Fires when the category registry is initialized. This is the required hook for registering categories.

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories( $registry ) {
wp_register_ability_category( 'my-category', array(
'label' => 'My Category',
'description' => 'Description of my category',
));
}

Parameters:

  • $registry (WP_Abilities_Category_Registry) - The category registry instance

Filter: register_ability_category_args

Allows modification of category arguments before validation.

add_filter( 'register_ability_category_args', 'my_modify_category_args', 10, 2 );
functionmy_modify_category_args( array$args, string$slug ): array {
if ( 'my-category' === $slug ) {
$args['label'] = 'Modified Label';
}
return$args;
}

Parameters:

  • $args (array) - Category arguments (label, description)
  • $slug (string) - Category slug being registered

REST API Updates

File: includes/rest-api/endpoints/class-wp-rest-abilities-list-controller.php

New Features

  1. Category field in responses:

    {
    "name": "my-plugin/get-data",
    "label": "Get Data",
    "description": "Retrieves data",
    "category": "data-retrieval",
    "input_schema": {},
    "output_schema": {},
    "meta": {}
    }
  2. Category filtering parameter:

    GET /wp/v2/abilities?category=data-retrieval
  3. Schema updates:

    • category added to ability schema as required field
    • category marked as readonly
    • Available in all contexts (view, edit, embed)

Test Coverage

New Test File: tests/unit/abilities-api/wpAbilityCategory.php (550 lines)

Updated Test Files:

  • wpAbilitiesRegistry.php - Added category setup/teardown
  • wpAbility.php - Added category property to tests
  • wpRegisterAbility.php - Added category validation tests
  • wpRestAbilitiesListController.php - Added category filtering tests (125+ lines)
  • wpRestAbilitiesRunController.php - Updated for category support (56+ lines)

Documentation Updates

1. docs/1.intro.md

  • Added Category to Core Concepts
  • Updated Registry definition to include category registry
  • Updated example to show category registration

2. docs/3.registering-abilities.md (59 new lines)

  • Added category as Required parameter
  • Added "Registering Categories" section with:
    • wp_register_ability_category() function signature
    • Category slug conventions
    • Example category registration code
    • Other category functions documentation
  • Updated all 4 code examples to include category field

3. docs/5.rest-api.md

  • Added category field to Ability Object schema
  • Added category filter parameter to List Abilities endpoint
  • Updated all JSON examples to include category field

4. docs/6.hooks.md (70 new lines)

  • Added abilities_api_category_registry_init action documentation
  • Added register_ability_category_args filter documentation
  • Updated Quick Links navigation

Migration Guide

For Plugin Developers

Step 1: Register Categories

Create categories before registering abilities:

add_action( 'abilities_api_category_registry_init', 'my_plugin_register_categories' );
functionmy_plugin_register_categories() {
// Group related abilities into logical categorieswp_register_ability_category( 'data-retrieval', array(
'label' => __( 'Data Retrieval', 'my-plugin' ),
'description' => __( 'Abilities that fetch and return data', 'my-plugin' ),
));
wp_register_ability_category( 'data-modification', array(
'label' => __( 'Data Modification', 'my-plugin' ),
'description' => __( 'Abilities that modify or update data', 'my-plugin' ),
));
}

Step 2: Update Ability Registrations

Add the category parameter to all wp_register_ability() calls:

add_action( 'abilities_api_init', 'my_plugin_register_abilities' );
functionmy_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-posts', array(
'label' => __( 'Get Posts', 'my-plugin' ),
'description' => __( 'Retrieves WordPress posts', 'my-plugin' ),
'category' => 'data-retrieval', // ADD THIS// ... rest of args
));
}

Category Slug Naming Conventions

Valid Slugs:

  • data-retrieval
  • user-management
  • ecommerce
  • analytics-123

Invalid Slugs:

  • Data-Retrieval (uppercase)
  • data_retrieval (underscores)
  • data.retrieval (dots)
  • data/retrieval (slashes)
  • -data-retrieval (leading dash)
  • data-retrieval- (trailing dash)

Related Issues

This PR implements the category system from issue #101.

* Add functions to register, unregister, and retrieve ability categories.
* Introduce WP_Ability_Category and WP_Abilities_Category_Registry classes for managing categories.
* Update WP_Ability class to support categories and modify the abilities retrieval process to filter by category.
* Enhance REST API to allow filtering abilities by category and include category information in responses.
* Bump version to 0.3.0 to reflect new features.
* Update the `register` method in `WP_Abilities_Category_Registry` to check for existing slugs before validating format.
* Modify the `WP_Abilities_Registry` class to return abilities as an associative array keyed by ability name.
* Enhance the `WP_Ability_Category` constructor to throw an exception for empty slugs and streamline property assignment.
@galatanovidiugalatanovidiu changed the title Feature/add categories systemFeature: add categories systemOct 6, 2025
@codecov

codecovBot commented Oct 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.09524% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (4dc57b3) to head (e69d69b).
⚠️ Report is 1 commits behind head on trunk.

Files with missing linesPatch %Lines
...ities-api/class-wp-abilities-category-registry.php91.42%6 Missing ⚠️
...cludes/abilities-api/class-wp-ability-category.php89.36%5 Missing ⚠️
includes/abilities-api/class-wp-ability.php16.66%5 Missing ⚠️
includes/bootstrap.php0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## trunk #102 +/- ##
============================================
+ Coverage 86.26% 86.48% +0.21% - Complexity 110 148 +38 
============================================
Files 16 18 +2 Lines 808 969 +161 Branches 86 85 -1 ============================================
+ Hits 697 838 +141 - Misses 111 131 +20 
FlagCoverage Δ
javascript92.62% <ø> (ø)
unit84.70% <88.09%> (+0.78%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threaddocs/1.intro.md Outdated
Move the action hook validation for ability category registration from the public API function into the registry class itself. This change centralizes the validation logic, ensuring it's consistently applied.
The validation is also made more specific, now only permitting registration during the `abilities_api_category_registry_init` action to enforce a stricter and more predictable initialization order.
Update unit tests to register ability categories using the `abilities_api_category_registry_init` action hook.
Previously, tests registered categories after this hook had already fired, which does not reflect the intended API usage. This change ensures that the test setup accurately simulates how categories should be registered, making the test suite more robust and reliable.
A helper method has also been introduced in the `wpAbilityCategory` test class to streamline this process and reduce code duplication.
The `abilities_api_category_registry_init` action hook is renamed to the more concise and intuitive `abilities_api_categories_init`.
This change improves developer experience by making the hook's purpose clearer and aligning it more closely with standard WordPress naming conventions. All related code, documentation, and tests have been updated to use the new hook name.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few minor notes for the production logic. This looks solid and I'm planning to approve the PR as soon as I review unit tests. Some of my feedback is perfectly suited as follow-up work as it focuses on code quality which might be even easier to review and discuss seperately. @galatanovidiu, can you collect the discussed code quality improvements so we have a good overview of what's left when making the final call? Again, I didn't disovered any blockers so far.

Comment threaddocs/1.intro.md Outdated
Comment threaddocs/7.registering-categories.md Outdated
Comment threaddocs/7.registering-categories.md
Comment threadincludes/abilities-api.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-category-registry.php Outdated
Comment threadincludes/abilities-api/class-wp-abilities-registry.php Outdated
Comment on lines +100 to +117
// Validate category exists if provided (will be validated as required in WP_Ability).
if ( isset( $args['category'] ) ) {
$category_registry = WP_Abilities_Category_Registry::get_instance();
if ( ! $category_registry->is_registered( $args['category'] ) ) {
_doing_it_wrong(
__METHOD__,
sprintf(
/* translators: %1$s: category slug, %2$s: ability name */
esc_html__( 'Category "%1$s" is not registered. Please register the category before assigning it to ability "%2$s".' ),
esc_attr( $args['category'] ),
esc_attr( $name )
),
'n.e.x.t'
);
return null;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it moves most of the validation to the category registry, as the existence of the category name there is the strongest indicator that it has already been validated. No need for additional checks here. There is strong coupling with the category registry, which is fine to have at both levels. @galatanovidiu explained that the basic check for existence still happens inside prepare_properties(), so all checks necessary are covered. I'm fine keeping it here.

@@ -0,0 +1,80 @@
# 7. Registering Categories

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanbossenger, we really need to remove these numbers, as I would put this one next to registering categories as it fits better there. It's another instance where this ordering causes trouble 😅

Let's also make sure to list this new document in README somwhere next to:

-[Registering Abilities](docs/3.registering-abilities.md)

@gziologzioloOct 13, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the ordering aspect to a new issue here.

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Comment threadtests/unit/rest-api/wpRestAbilitiesListController.php Outdated
galatanovidiuand others added 5 commits October 13, 2025 13:37
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
…r clearer context during translation.
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
This parts is coverded in Hooks documentation
Corrects the PHPStan type annotations for wp_register_ability_category()
and WP_Abilities_Category_Registry::register() to accurately reflect the
actual implementation:
- Mark `label` and `description` as required fields (removed optional `?`)
- Add `meta` as an optional property (array<string,mixed>)
- Update docblock to mention `meta` parameter
The label and description fields are validated as required in the
WP_Ability_Category::prepare_properties() method, while meta is
truly optional. The annotations now match the runtime behavior.

@gziologziolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of additional nitpicks regarding the implementation of tests to consider before landing this PR.

Overall, this is looking excellent functionality-wise from my perspective. Let's make sure that other folks who left feedback are happy with the current shape and plan for merging. I would like to start the process of syncing the abilities registry and REST API layer to WordPress core, and this is the last missing piece of the puzzle I expected 🎉

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment on lines +451 to +455
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

// Cleanup.
wp_unregister_ability( 'test/calculator' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I would do the cleanup before assertions to ensure it always happens in case assertions fail for some reason during development.

Suggested change
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
// Cleanup.
wp_unregister_ability( 'test/calculator' );
$this->assertInstanceOf( WP_Ability::class, $result );
$this->assertSame( 'test-math', $result->get_category() );

Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
Comment threadtests/unit/abilities-api/wpAbilityCategory.php Outdated
galatanovidiuand others added 3 commits October 13, 2025 15:14
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
Replace loop-based slug validation tests with PHPUnit data providers
for better test isolation and clearer failure reporting.
- Add valid_slug_provider() for valid slug format tests
- Add invalid_slug_provider() for invalid slug format tests
Simplify the test by replacing the callback function with direct calls to register_category_during_hook
@gziologziolo mentioned this pull request Oct 13, 2025
- Assert the count of properties in the schema to ensure it matches expected values.
- Verify the existence and details of the 'category' property, including its type and readonly status.
- Confirm that 'category' is included in the required fields of the schema.
- Remove redundant test method for category schema validation.

@JasonTheAdamsJasonTheAdams left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @galatanovidiu! And great discussions, everyone! 😄

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@galatanovidiu@gziolo@emdashcodes@JasonTheAdams@justlevine