Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new query args to Pattern Directory Controller #3861

Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public function get_items_permissions_check( $request ) {
*
* @since 5.8.0
* @since 6.0.0 Added 'slug' to request.
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
*
* @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.
Expand All @@ -93,31 +94,23 @@ public function get_items( $request ) {
*/
require ABSPATH . WPINC . '/version.php';

$query_args = array(
'locale' => get_user_locale(),
'wp-version' => $wp_version,
$valid_query_args = array(
'offset' => true,
'order' => true,
'orderby' => true,
'page' => true,
'per_page' => true,
'search' => true,
'slug' => true,
);
$query_args = array_intersect_key( $request->get_params(), $valid_query_args );

$category_id = $request['category'];
$keyword_id = $request['keyword'];
$search_term = $request['search'];
$slug = $request['slug'];
$query_args['locale'] = get_user_locale();
$query_args['wp-version'] = $wp_version; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- it's defined in `version.php` above.
$query_args['pattern-categories'] = isset( $request['category'] ) ? $request['category'] : false;
$query_args['pattern-keywords'] = isset( $request['keyword'] ) ? $request['keyword'] : false;

if ( $category_id ) {
$query_args['pattern-categories'] = $category_id;
}

if ( $keyword_id ) {
$query_args['pattern-keywords'] = $keyword_id;
}

if ( $search_term ) {
$query_args['search'] = $search_term;
}

if ( $slug ) {
$query_args['slug'] = $slug;
}
$query_args = array_filter( $query_args );

$transient_key = $this->get_transient_key( $query_args );

Expand Down Expand Up @@ -303,16 +296,14 @@ public function get_item_schema() {
* Retrieves the search parameters for the block pattern's collection.
*
* @since 5.8.0
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
*
* @return array Collection parameters.
*/
public function get_collection_params() {
$query_params = parent::get_collection_params();

// Pagination is not supported.
unset( $query_params['page'] );
unset( $query_params['per_page'] );

$query_params['per_page']['default'] = 100;
$query_params['search']['minLength'] = 1;
$query_params['context']['default'] = 'view';

Expand All @@ -333,6 +324,37 @@ public function get_collection_params() {
'type' => 'array',
);

$query_params['offset'] = array(
'description' => __( 'Offset the result set by a specific number of items.' ),
'type' => 'integer',
);

$query_params['order'] = array(
'description' => __( 'Order sort attribute ascending or descending.' ),
'type' => 'string',
'default' => 'desc',
'enum' => array( 'asc', 'desc' ),
);

$query_params['orderby'] = array(
'description' => __( 'Sort collection by post attribute.' ),
'type' => 'string',
'default' => 'date',
'enum' => array(
'author',
'date',
'id',
'include',
'modified',
'parent',
'relevance',
'slug',
'include_slugs',
'title',
'favorite_count',
),
);

/**
* Filter collection parameters for the block pattern directory controller.
*
Expand Down
227 changes: 227 additions & 0 deletions tests/phpunit/tests/rest-api/rest-pattern-directory-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ class WP_REST_Pattern_Directory_Controller_Test extends WP_Test_REST_Controller_
*/
private static $controller;

/**
* List of URLs captured.
*
* @since 6.2.0
*
* @var string[]
*/
protected static $http_request_urls;

/**
* Set up class test fixtures.
*
Expand All @@ -44,9 +53,30 @@ public static function wpSetUpBeforeClass( $factory ) {
)
);

self::$http_request_urls = array();

static::$controller = new WP_REST_Pattern_Directory_Controller();
}

/**
* Tear down after class.
*
* @since 6.2.0
*/
public static function wpTearDownAfterClass() {
self::delete_user( self::$contributor_id );
}

/**
* Clear the captured request URLs after each test.
*
* @since 6.2.0
*/
public function tear_down() {
self::$http_request_urls = array();
parent::tear_down();
}

/**
* Asserts that the pattern matches the expected response schema.
*
Expand Down Expand Up @@ -310,6 +340,174 @@ static function( $response ) {
$this->assertSame( 'modified the cache', $patterns[0] );
}

/**
* Tests if the provided query args are passed through to the wp.org API.
*
* @dataProvider data_get_items_query_args
*
* @covers WP_REST_Pattern_Directory_Controller::get_items
*
* @since 6.2.0
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*
* @param string $param Query parameter name (ex, page).
* @param mixed $value Query value to test.
* @param bool $is_error Whether this value should error or not.
* @param mixed $expected Expected value (or expected error code).
*/
public function test_get_items_query_args( $param, $value, $is_error, $expected ) {
wp_set_current_user( self::$contributor_id );
add_filter( 'pre_http_request', array( $this, 'mock_request_to_apiwporg_url' ), 10, 3 );

$request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
if ( $value ) {
$request->set_query_params( array( $param => $value ) );
}

$response = rest_do_request( $request );
$data = $response->get_data();
if ( $is_error ) {
$this->assertSame( $expected, $data['code'], 'Response error code does not match' );
$this->assertStringContainsString( $param, $data['message'], 'Response error message does not match' );
} else {
$this->assertCount( 1, self::$http_request_urls, 'The number of HTTP Request URLs is not 1' );
$this->assertStringContainsString( $param . '=' . $expected, self::$http_request_urls[0], 'The param and/or value do not match' );
}
}

/**
* Data provider.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Data provider.
* Data provider for test_get_items_query_args().

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is unnecessary. The naming convention of test_ and data_ align to know that this data provider is for the matching test.

Copy link
Contributor

@hellofromtonya hellofromtonya Jan 24, 2023

Choose a reason for hiding this comment

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

Whoops forgot to share where the naming convention is identified the handbook 🤦‍♀️

A data provider is a secondary function which is linked to a test using a@dataProviderannotation in the docblock of the test method and is generally named after the test, replacing the test prefix in the method name with data.

https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/#repetitive-tests.

*
* return array[]
*/
public function data_get_items_query_args() {
return array(
'per_page default' => array(
'param' => 'per_page',
'value' => false,
'is_error' => false,
'expected' => 100,
),
'per_page custom-1' => array(
'param' => 'per_page',
'value' => 5,
'is_error' => false,
'expected' => 5,
),
'per_page custom-2' => array(
'param' => 'per_page',
'value' => 50,
'is_error' => false,
'expected' => 50,
),
'per_page invalid-1' => array(
'param' => 'per_page',
'value' => 200,
'is_error' => true,
'expected' => 'rest_invalid_param',
),
'per_page invalid-2' => array(
'param' => 'per_page',
'value' => 'abc',
'is_error' => true,
'expected' => 'rest_invalid_param',
),

'page default' => array(
'param' => 'page',
'value' => false,
'is_error' => false,
'expected' => 1,
),
'page custom' => array(
'param' => 'page',
'value' => 5,
'is_error' => false,
'expected' => 5,
),
'page invalid' => array(
'param' => 'page',
'value' => 'abc',
'is_error' => true,
'expected' => 'rest_invalid_param',
),

'offset custom' => array(
'param' => 'offset',
'value' => 5,
'is_error' => false,
'expected' => 5,
),
'offset invalid-1' => array(
'param' => 'offset',
'value' => 'abc',
'is_error' => true,
'expected' => 'rest_invalid_param',
),

'order default' => array(
'param' => 'order',
'value' => false,
'is_error' => false,
'expected' => 'desc',
),
'order custom' => array(
'param' => 'order',
'value' => 'asc',
'is_error' => false,
'expected' => 'asc',
),
'order invalid-1' => array(
'param' => 'order',
'value' => 10,
'is_error' => true,
'expected' => 'rest_invalid_param',
),
'order invalid-2' => array(
'param' => 'order',
'value' => 'fake',
'is_error' => true,
'expected' => 'rest_invalid_param',
),

'orderby default' => array(
'param' => 'orderby',
'value' => false,
'is_error' => false,
'expected' => 'date',
),
'orderby custom-1' => array(
'param' => 'orderby',
'value' => 'title',
'is_error' => false,
'expected' => 'title',
),
'orderby custom-2' => array(
'param' => 'orderby',
'value' => 'date',
'is_error' => false,
'expected' => 'date',
),
'orderby custom-3' => array(
'param' => 'orderby',
'value' => 'favorite_count',
'is_error' => false,
'expected' => 'favorite_count',
),
'orderby invalid-1' => array(
'param' => 'orderby',
'value' => 10,
'is_error' => true,
'expected' => 'rest_invalid_param',
),
'orderby invalid-2' => array(
'param' => 'orderby',
'value' => 'fake',
'is_error' => true,
'expected' => 'rest_invalid_param',
),
);
}

/**
* @doesNotPerformAssertions
*/
Expand Down Expand Up @@ -573,4 +771,33 @@ static function ( $response, $parsed_args, $url ) use ( $blocked_host ) {
3
);
}

/**
* Mock the request to wp.org URL to capture the URLs.
*
* @since 6.2.0
*
* @return array faux/mocked response.
*/
public function mock_request_to_apiwporg_url( $response, $args, $url ) {
Comment on lines +777 to +784
Copy link
Member

Choose a reason for hiding this comment

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

In the core, some mock functions have the document and some don't. I appreciated @hellofromtonya's and @SergeyBiryukov's input on this. 

Copy link
Contributor

Choose a reason for hiding this comment

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

I know. The handbook and similar test mocks needs some love to align for consistency. I agree!

if ( 'api.wordpress.org' !== wp_parse_url( $url, PHP_URL_HOST ) ) {
return $response;
}

self::$http_request_urls[] = $url;

// Return a response to prevent external API request.
$response = array(
'headers' => array(),
'response' => array(
'code' => 200,
'message' => 'OK',
),
'body' => '[]',
'cookies' => array(),
'filename' => null,
);

return $response;
}
}
Loading