diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index a5055170150fd..d23931a589eed 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -485,6 +485,8 @@ function create_initial_rest_routes() { $font_collections_controller->register_routes(); // Abilities. + $abilities_categories_controller = new WP_REST_Abilities_V1_Categories_Controller(); + $abilities_categories_controller->register_routes(); $abilities_run_controller = new WP_REST_Abilities_V1_Run_Controller(); $abilities_run_controller->register_routes(); $abilities_list_controller = new WP_REST_Abilities_V1_List_Controller(); diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php new file mode 100644 index 0000000000000..11246c41319cb --- /dev/null +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php @@ -0,0 +1,293 @@ +namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[a-z0-9]+(?:-[a-z0-9]+)*)', + array( + 'args' => array( + 'slug' => array( + 'description' => __( 'Unique identifier for the ability category.' ), + 'type' => 'string', + 'pattern' => '^[a-z0-9]+(?:-[a-z0-9]+)*$', + ), + ), + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Retrieves all ability categories. + * + * @since 6.9.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response Response object on success. + */ + public function get_items( $request ) { + $categories = wp_get_ability_categories(); + + $page = $request['page']; + $per_page = $request['per_page']; + $offset = ( $page - 1 ) * $per_page; + + $total_categories = count( $categories ); + $max_pages = ceil( $total_categories / $per_page ); + + if ( $request->get_method() === 'HEAD' ) { + $response = new WP_REST_Response( array() ); + } else { + $categories = array_slice( $categories, $offset, $per_page ); + + $data = array(); + foreach ( $categories as $category ) { + $item = $this->prepare_item_for_response( $category, $request ); + $data[] = $this->prepare_response_for_collection( $item ); + } + + $response = rest_ensure_response( $data ); + } + + $response->header( 'X-WP-Total', (string) $total_categories ); + $response->header( 'X-WP-TotalPages', (string) $max_pages ); + + $query_params = $request->get_query_params(); + $base = add_query_arg( + urlencode_deep( $query_params ), + rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) + ); + + if ( $page > 1 ) { + $prev_page = $page - 1; + $prev_link = add_query_arg( 'page', $prev_page, $base ); + $response->link_header( 'prev', $prev_link ); + } + + if ( $page < $max_pages ) { + $next_page = $page + 1; + $next_link = add_query_arg( 'page', $next_page, $base ); + $response->link_header( 'next', $next_link ); + } + + return $response; + } + + /** + * Retrieves a specific ability category. + * + * @since 6.9.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_item( $request ) { + $category = wp_get_ability_category( $request['slug'] ); + if ( ! $category ) { + return new WP_Error( + 'rest_ability_category_not_found', + __( 'Ability category not found.' ), + array( 'status' => 404 ) + ); + } + + $data = $this->prepare_item_for_response( $category, $request ); + return rest_ensure_response( $data ); + } + + /** + * Checks if a given request has access to read ability categories. + * + * @since 6.9.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return bool True if the request has read access. + */ + public function get_items_permissions_check( $request ) { + return current_user_can( 'read' ); + } + + /** + * Checks if a given request has access to read an ability category. + * + * @since 6.9.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return bool True if the request has read access. + */ + public function get_item_permissions_check( $request ) { + return current_user_can( 'read' ); + } + + /** + * Prepares an ability category for response. + * + * @since 6.9.0 + * + * @param WP_Ability_Category $category The ability category object. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response Response object. + */ + public function prepare_item_for_response( $category, $request ) { + $data = array( + 'slug' => $category->get_slug(), + 'label' => $category->get_label(), + 'description' => $category->get_description(), + 'meta' => $category->get_meta(), + ); + + $context = $request['context'] ?? 'view'; + $data = $this->add_additional_fields_to_object( $data, $request ); + $data = $this->filter_response_by_context( $data, $context ); + + $response = rest_ensure_response( $data ); + + $fields = $this->get_fields_for_response( $request ); + if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $category->get_slug() ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), + ), + 'abilities' => array( + 'href' => rest_url( sprintf( '%s/abilities?category=%s', $this->namespace, $category->get_slug() ) ), + ), + ); + + $response->add_links( $links ); + } + + return $response; + } + + /** + * Retrieves the ability category's schema, conforming to JSON Schema. + * + * @since 6.9.0 + * + * @return array Item schema data. + */ + public function get_item_schema(): array { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'ability-category', + 'type' => 'object', + 'properties' => array( + 'slug' => array( + 'description' => __( 'Unique identifier for the ability category.' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), + 'label' => array( + 'description' => __( 'Display label for the category.' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), + 'description' => array( + 'description' => __( 'Description of the category.' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'meta' => array( + 'description' => __( 'Meta information about the category.' ), + 'type' => 'object', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } + + /** + * Retrieves the query params for collections. + * + * @since 6.9.0 + * + * @return array Collection parameters. + */ + public function get_collection_params(): array { + return array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'page' => array( + 'description' => __( 'Current page of the collection.' ), + 'type' => 'integer', + 'default' => 1, + 'minimum' => 1, + ), + 'per_page' => array( + 'description' => __( 'Maximum number of items to be returned in result set.' ), + 'type' => 'integer', + 'default' => 50, + 'minimum' => 1, + 'maximum' => 100, + ), + ); + } +} diff --git a/src/wp-settings.php b/src/wp-settings.php index b404ea5c146b2..256a0f6791427 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -336,6 +336,7 @@ require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-font-families-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-font-faces-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-font-collections-controller.php'; +require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php'; require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-abilities-v1-run-controller.php'; require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php'; diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php index 43c19accb41f4..2946949d9d84d 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-setup.php +++ b/tests/phpunit/tests/rest-api/rest-schema-setup.php @@ -204,6 +204,8 @@ public function test_expected_routes_in_schema() { '/wp/v2/font-families/(?P[\d]+)/font-faces/(?P[\d]+)', '/wp/v2/font-families/(?P[\d]+)', '/wp-abilities/v1', + '/wp-abilities/v1/categories', + '/wp-abilities/v1/categories/(?P[a-z0-9]+(?:-[a-z0-9]+)*)', '/wp-abilities/v1/abilities/(?P[a-zA-Z0-9\-\/]+?)/run', '/wp-abilities/v1/abilities/(?P[a-zA-Z0-9\-\/]+)', '/wp-abilities/v1/abilities', diff --git a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php new file mode 100644 index 0000000000000..a6b7f233ceebf --- /dev/null +++ b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php @@ -0,0 +1,525 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + + self::$subscriber_user_id = self::factory()->user->create( + array( + 'role' => 'subscriber', + ) + ); + } + + /** + * Set up before each test. + */ + public function set_up(): void { + parent::set_up(); + + global $wp_rest_server; + $wp_rest_server = new WP_REST_Server(); + $this->server = $wp_rest_server; + + do_action( 'rest_api_init' ); + + // Initialize the API and register test ability categories. + do_action( 'wp_abilities_api_categories_init' ); + $this->register_test_ability_categories(); + + wp_set_current_user( self::$admin_user_id ); + } + + /** + * Tear down after each test. + */ + public function tear_down(): void { + + // Clean up test ability categories. + foreach ( wp_get_ability_categories() as $ability_category ) { + if ( ! str_starts_with( $ability_category->get_slug(), 'test-' ) ) { + continue; + } + + wp_unregister_ability_category( $ability_category->get_slug() ); + } + + global $wp_rest_server; + $wp_rest_server = null; + + parent::tear_down(); + } + + /** + * Register test ability categories for testing. + */ + public function register_test_ability_categories(): void { + wp_register_ability_category( + 'test-data-retrieval', + array( + 'label' => 'Data Retrieval', + 'description' => 'Abilities that retrieve and return data from the WordPress site.', + ) + ); + + wp_register_ability_category( + 'test-data-modification', + array( + 'label' => 'Data Modification', + 'description' => 'Abilities that modify data on the WordPress site.', + ) + ); + + wp_register_ability_category( + 'test-communication', + array( + 'label' => 'Communication', + 'description' => 'Abilities that send messages or notifications.', + 'meta' => array( + 'priority' => 'high', + ), + ) + ); + + // Register multiple ability categories for pagination testing + for ( $i = 1; $i <= 60; $i++ ) { + wp_register_ability_category( + "test-category-{$i}", + array( + 'label' => "Test Category {$i}", + 'description' => "Test category number {$i}", + ) + ); + } + } + + /** + * Test listing all ability categories. + * + * @ticket 64098 + */ + public function test_get_items(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertNotEmpty( $data ); + + $this->assertCount( 50, $data, 'First page should return exactly 50 items (default per_page)' ); + + $category_slugs = wp_list_pluck( $data, 'slug' ); + $this->assertContains( 'test-data-retrieval', $category_slugs ); + $this->assertContains( 'test-data-modification', $category_slugs ); + $this->assertContains( 'test-communication', $category_slugs ); + } + + /** + * Test getting a specific ability category. + * + * @ticket 64098 + */ + public function test_get_item(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertEquals( 'test-data-retrieval', $data['slug'] ); + $this->assertEquals( 'Data Retrieval', $data['label'] ); + $this->assertEquals( 'Abilities that retrieve and return data from the WordPress site.', $data['description'] ); + $this->assertArrayHasKey( 'meta', $data ); + } + + /** + * Test getting an ability category with meta. + * + * @ticket 64098 + */ + public function test_get_item_with_meta(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-communication' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertEquals( 'test-communication', $data['slug'] ); + $this->assertArrayHasKey( 'meta', $data ); + $this->assertIsArray( $data['meta'] ); + $this->assertEquals( 'high', $data['meta']['priority'] ); + } + + /** + * Test getting a specific ability category with only selected fields. + * + * @ticket 64098 + */ + public function test_get_item_with_selected_fields(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $request->set_param( '_fields', 'slug,label' ); + $response = $this->server->dispatch( $request ); + add_filter( 'rest_post_dispatch', 'rest_filter_response_fields', 10, 3 ); + $response = apply_filters( 'rest_post_dispatch', $response, $this->server, $request ); + remove_filter( 'rest_post_dispatch', 'rest_filter_response_fields', 10 ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertCount( 2, $data, 'Response should only contain the requested fields.' ); + $this->assertEquals( 'test-data-retrieval', $data['slug'] ); + $this->assertEquals( 'Data Retrieval', $data['label'] ); + } + + /** + * Test getting a non-existent ability category returns 404. + * + * @ticket 64098 + * + * @expectedIncorrectUsage WP_Ability_Categories_Registry::get_registered + */ + public function test_get_item_not_found(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/non-existent' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 404, $response->get_status() ); + + $data = $response->get_data(); + $this->assertEquals( 'rest_ability_category_not_found', $data['code'] ); + } + + /** + * Test permission check for listing ability categories. + * + * @ticket 64098 + */ + public function test_get_items_permission_denied(): void { + wp_set_current_user( 0 ); + + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Test permission check for single ability category. + * + * @ticket 64098 + */ + public function test_get_item_permission_denied(): void { + wp_set_current_user( 0 ); + + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Test pagination headers. + * + * @ticket 64098 + */ + public function test_pagination_headers(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $request->set_param( 'per_page', 10 ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $headers = $response->get_headers(); + $this->assertArrayHasKey( 'X-WP-Total', $headers ); + $this->assertArrayHasKey( 'X-WP-TotalPages', $headers ); + + $total_categories = count( wp_get_ability_categories() ); + $this->assertEquals( $total_categories, (int) $headers['X-WP-Total'] ); + $this->assertEquals( ceil( $total_categories / 10 ), (int) $headers['X-WP-TotalPages'] ); + } + + /** + * Test HEAD method returns empty body with proper headers. + * + * @ticket 64098 + */ + public function test_head_request(): void { + $request = new WP_REST_Request( 'HEAD', '/wp-abilities/v1/categories' ); + $response = $this->server->dispatch( $request ); + + $data = $response->get_data(); + $this->assertEmpty( $data ); + + $headers = $response->get_headers(); + $this->assertArrayHasKey( 'X-WP-Total', $headers ); + $this->assertArrayHasKey( 'X-WP-TotalPages', $headers ); + } + + /** + * Test pagination links. + * + * @ticket 64098 + */ + public function test_pagination_links(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $request->set_param( 'per_page', 10 ); + $request->set_param( 'page', 1 ); + $response = $this->server->dispatch( $request ); + + $headers = $response->get_headers(); + $link_header = $headers['Link'] ?? ''; + + $this->assertStringContainsString( 'rel="next"', $link_header ); + $this->assertStringNotContainsString( 'rel="prev"', $link_header ); + + $request->set_param( 'page', 3 ); + $response = $this->server->dispatch( $request ); + + $headers = $response->get_headers(); + $link_header = $headers['Link'] ?? ''; + + $this->assertStringContainsString( 'rel="next"', $link_header ); + $this->assertStringContainsString( 'rel="prev"', $link_header ); + + $total_categories = count( wp_get_ability_categories() ); + $last_page = ceil( $total_categories / 10 ); + $request->set_param( 'page', $last_page ); + $response = $this->server->dispatch( $request ); + + $headers = $response->get_headers(); + $link_header = $headers['Link'] ?? ''; + + $this->assertStringNotContainsString( 'rel="next"', $link_header ); + $this->assertStringContainsString( 'rel="prev"', $link_header ); + } + + /** + * Test collection parameters. + * + * @ticket 64098 + */ + public function test_collection_params(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $request->set_param( 'per_page', 5 ); + $response = $this->server->dispatch( $request ); + + $data = $response->get_data(); + $this->assertCount( 5, $data ); + $request->set_param( 'page', 2 ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $data = $response->get_data(); + $this->assertCount( 5, $data ); + + $page1_request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $page1_request->set_param( 'per_page', 5 ); + $page1_request->set_param( 'page', 1 ); + $page1_response = $this->server->dispatch( $page1_request ); + $page1_slugs = wp_list_pluck( $page1_response->get_data(), 'slug' ); + $page2_slugs = wp_list_pluck( $data, 'slug' ); + + $this->assertNotEquals( $page1_slugs, $page2_slugs ); + } + + /** + * Test response links for individual ability categories. + * + * @ticket 64098 + */ + public function test_ability_category_response_links(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $response = $this->server->dispatch( $request ); + + $links = $response->get_links(); + $this->assertArrayHasKey( 'self', $links ); + $this->assertArrayHasKey( 'collection', $links ); + $this->assertArrayHasKey( 'abilities', $links ); + + $self_link = $links['self'][0]['href']; + $this->assertStringContainsString( '/wp-abilities/v1/categories/test-data-retrieval', $self_link ); + + $collection_link = $links['collection'][0]['href']; + $this->assertStringContainsString( '/wp-abilities/v1/categories', $collection_link ); + + $abilities_link = $links['abilities'][0]['href']; + $this->assertStringContainsString( '/wp-abilities/v1/abilities?category=test-data-retrieval', $abilities_link ); + } + + /** + * Test context parameter. + * + * @ticket 64098 + */ + public function test_context_parameter(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $request->set_param( 'context', 'view' ); + $response = $this->server->dispatch( $request ); + + $data = $response->get_data(); + $this->assertArrayHasKey( 'description', $data ); + + $request->set_param( 'context', 'embed' ); + $response = $this->server->dispatch( $request ); + + $data = $response->get_data(); + $this->assertArrayHasKey( 'slug', $data ); + $this->assertArrayHasKey( 'label', $data ); + } + + /** + * Test schema retrieval. + * + * @ticket 64098 + */ + public function test_get_schema(): void { + $request = new WP_REST_Request( 'OPTIONS', '/wp-abilities/v1/categories' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertArrayHasKey( 'schema', $data ); + $schema = $data['schema']; + + $this->assertEquals( 'ability-category', $schema['title'] ); + $this->assertEquals( 'object', $schema['type'] ); + $this->assertArrayHasKey( 'properties', $schema ); + + $properties = $schema['properties']; + + $this->assertCount( 4, $properties, 'Schema should have exactly 4 properties.' ); + + $this->assertArrayHasKey( 'slug', $properties ); + $this->assertArrayHasKey( 'label', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); + + $slug_property = $properties['slug']; + $this->assertEquals( 'string', $slug_property['type'] ); + $this->assertTrue( $slug_property['readonly'] ); + } + + /** + * Test ability category slug with valid format. + * + * @ticket 64098 + */ + public function test_ability_category_slug_with_valid_format(): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/test-data-retrieval' ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + } + + /** + * Data provider for invalid ability category slugs. + * + * @return array + */ + public function data_invalid_ability_category_slugs_provider(): array { + return array( + 'Uppercase' => array( 'Data-Retrieval' ), + '@ symbol' => array( 'data@retrieval' ), + 'space' => array( 'data retrieval' ), + 'dot' => array( 'data.retrieval' ), + 'underscore' => array( 'data_retrieval' ), + 'URL encoded space' => array( 'data%20retrieval' ), + ); + } + + /** + * Test ability category slugs with invalid format. + * + * @ticket 64098 + * + * @dataProvider data_invalid_ability_category_slugs_provider + * + * @param string $slug Invalid ability category slug to test. + */ + public function test_ability_category_slug_with_invalid_format( string $slug ): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories/' . $slug ); + $response = $this->server->dispatch( $request ); + + $this->assertContains( $response->get_status(), array( 400, 404 ) ); + } + + /** + * Data provider for invalid pagination parameters. + * + * @return array}> + */ + public function data_invalid_pagination_params_provider(): array { + return array( + 'Zero page' => array( array( 'page' => 0 ) ), + 'Negative page' => array( array( 'page' => -1 ) ), + 'Non-numeric page' => array( array( 'page' => 'abc' ) ), + 'Zero per page' => array( array( 'per_page' => 0 ) ), + 'Negative per page' => array( array( 'per_page' => -10 ) ), + 'Exceeds maximum' => array( array( 'per_page' => 1000 ) ), + 'Non-numeric per page' => array( array( 'per_page' => 'all' ) ), + ); + } + + /** + * Test pagination parameters with invalid values. + * + * @ticket 64098 + * + * @dataProvider data_invalid_pagination_params_provider + * + * @param array $params Invalid pagination parameters. + */ + public function test_invalid_pagination_parameters( array $params ): void { + $request = new WP_REST_Request( 'GET', '/wp-abilities/v1/categories' ); + $request->set_query_params( $params ); + + $response = $this->server->dispatch( $request ); + + $this->assertContains( $response->get_status(), array( 200, 400 ) ); + + if ( $response->get_status() !== 200 ) { + return; + } + + $data = $response->get_data(); + $this->assertIsArray( $data ); + } +} diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index eae97fa99c651..329ecf84f3030 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -13513,6 +13513,75 @@ mockedApiResponse.Schema = { ] } }, + "/wp-abilities/v1/categories": { + "namespace": "wp-abilities/v1", + "methods": [ + "GET" + ], + "endpoints": [ + { + "methods": [ + "GET" + ], + "args": { + "context": { + "description": "Scope under which the request is made; determines fields present in response.", + "type": "string", + "enum": [ + "view", + "embed", + "edit" + ], + "default": "view", + "required": false + }, + "page": { + "description": "Current page of the collection.", + "type": "integer", + "default": 1, + "minimum": 1, + "required": false + }, + "per_page": { + "description": "Maximum number of items to be returned in result set.", + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 100, + "required": false + } + } + } + ], + "_links": { + "self": [ + { + "href": "http://example.org/index.php?rest_route=/wp-abilities/v1/categories" + } + ] + } + }, + "/wp-abilities/v1/categories/(?P[a-z0-9]+(?:-[a-z0-9]+)*)": { + "namespace": "wp-abilities/v1", + "methods": [ + "GET" + ], + "endpoints": [ + { + "methods": [ + "GET" + ], + "args": { + "slug": { + "description": "Unique identifier for the ability category.", + "type": "string", + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "required": false + } + } + } + ] + }, "/wp-abilities/v1/abilities/(?P[a-zA-Z0-9\\-\\/]+?)/run": { "namespace": "wp-abilities/v1", "methods": [