WordPress.org

Make WordPress Core


Ignore:
Timestamp:
02/24/2017 10:33:05 PM (16 months ago)
Author:
SergeyBiryukov
Message:

REST API: Add QUnit tests for wp-api.js and PHPUnit fixture generation.

Add QUnit tests: verify that wp-api loads correctly, verify that the expected base models and collections exist and can be instantiated, verify that collections contain the correct models, verify that expected helper functions are in place for each collection.

The QUnit tests rely on two fixture files: tests/qunit/fixtures/wp-api-generated.js contains the data response from each core endpoint and is generated by running the PHPUnit restapi-jsclient group. tests/qunit/fixtures/wp-api.js maps the generated data to endpoint routes, and overrides Backbone.ajax to mock the responses for the tests.

Add PHPUnit tests in tests/phpunit/tests/rest-api/rest-schema-setup.php. First, verify that the API returns the expected routes via server->get_routes(). Then, the test_build_wp_api_client_fixtures test goes thru each endpoint and requests it from the API, tests that it returns data, and builds up the data for the mocked QUnit tests, saving the final results to tests/qunit/fixtures/wp-api-generated.js.

Add a new grunt task restapi-jsclient which runs the phpunit side data generation and the qunit tests together.

Props jnylen0, welcher, adamsilverstein, netweb, ocean90, rachelbaker.
Merges [40058], [40061], [40065], [40066], [40077], and [40104] to the 4.7 branch.
Fixes #39264.

Location:
branches/4.7
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • branches/4.7

  • branches/4.7/tests/phpunit/tests/rest-api/rest-schema-setup.php

    r40058 r40116  
    3737        $this->assertTrue( is_array( $routes ), '`get_routes` should return an array.' );
    3838        $this->assertTrue( ! empty( $routes ), 'Routes should not be empty.' );
     39
     40        $routes = array_filter( array_keys( $routes ), array( $this, 'is_builtin_route' ) );
    3941
    4042        $expected_routes = array(
     
    7173        );
    7274
    73         $this->assertEquals( $expected_routes, array_keys( $routes ) );
     75        $this->assertEquals( $expected_routes, $routes );
     76    }
     77
     78    private function is_builtin_route( $route ) {
     79        return (
     80            '/' === $route ||
     81            preg_match( '#^/oembed/1\.0(/.+)?$#', $route ) ||
     82            preg_match( '#^/wp/v2(/.+)?$#', $route )
     83        );
    7484    }
    7585
    7686    public function test_build_wp_api_client_fixtures() {
    77         // Set up for testing the individual endpoints.
    78         // Set a current admin user.
    79         $administrator = $this->factory->user->create( array(
    80             'role' => 'administrator',
    81         ) );
    82         wp_set_current_user( $administrator );
    83 
    84         // Set up data for endpoints.
    85         $post_id  = $this->factory->post->create();
    86         $page_id  = $this->factory->post->create( array( 'post_type' => 'page' ) );
    87         $tag_id   = $this->factory->tag->create( array( 'name' => 'test' ) );
     87        // Set up data for individual endpoint responses.  We need to specify
     88        // lots of different fields on these objects, otherwise the generated
     89        // fixture file will be different between runs of PHPUnit tests, which
     90        // is not desirable.
     91
     92        $administrator_id = $this->factory->user->create( array(
     93            'role'          => 'administrator',
     94            'display_name'  => 'REST API Client Fixture: User',
     95            'user_nicename' => 'restapiclientfixtureuser',
     96            'user_email'    => 'administrator@example.org',
     97        ) );
     98        wp_set_current_user( $administrator_id );
     99
     100        $post_id = $this->factory->post->create( array(
     101            'post_name'      => 'restapi-client-fixture-post',
     102            'post_title'     => 'REST API Client Fixture: Post',
     103            'post_content'   => 'REST API Client Fixture: Post',
     104            'post_excerpt'   => 'REST API Client Fixture: Post',
     105            'post_author'    => 0,
     106        ) );
     107        wp_update_post( array(
     108            'ID'           => $post_id,
     109            'post_content' => 'Updated post content.',
     110        ) );
     111
     112        $page_id = $this->factory->post->create( array(
     113            'post_type'      => 'page',
     114            'post_name'      => 'restapi-client-fixture-page',
     115            'post_title'     => 'REST API Client Fixture: Page',
     116            'post_content'   => 'REST API Client Fixture: Page',
     117            'post_excerpt'   => 'REST API Client Fixture: Page',
     118            'post_date'      => '2017-02-14 00:00:00',
     119            'post_date_gmt'  => '2017-02-14 00:00:00',
     120            'post_author'    => 0,
     121        ) );
     122        wp_update_post( array(
     123            'ID'           => $page_id,
     124            'post_content' => 'Updated page content.',
     125        ) );
     126
     127        $tag_id = $this->factory->tag->create( array(
     128            'name'        => 'REST API Client Fixture: Tag',
     129            'slug'        => 'restapi-client-fixture-tag',
     130            'description' => 'REST API Client Fixture: Tag',
     131        ) );
     132
    88133        $media_id = $this->factory->attachment->create_object( '/tmp/canola.jpg', 0, array(
    89134            'post_mime_type' => 'image/jpeg',
    90135            'post_excerpt'   => 'A sample caption',
    91         ) );
    92         wp_update_post( array( 'post_content' => 'Updated content.', 'ID' => $post_id ) );
    93         wp_update_post( array( 'post_content' => 'Updated content.', 'ID' => $page_id ) );
     136            'post_name'      => 'restapi-client-fixture-attachment',
     137            'post_title'     => 'REST API Client Fixture: Attachment',
     138            'post_date'      => '2017-02-14 00:00:00',
     139            'post_date_gmt'  => '2017-02-14 00:00:00',
     140            'post_author'    => 0,
     141        ) );
     142
    94143        $comment_id = $this->factory->comment->create( array(
    95             'comment_approved' => 1,
    96             'comment_post_ID'  => $post_id,
    97             'user_id'          => 0,
     144            'comment_approved'     => 1,
     145            'comment_post_ID'      => $post_id,
     146            'user_id'              => 0,
     147            'comment_date'         => '2017-02-14 00:00:00',
     148            'comment_date_gmt'     => '2017-02-14 00:00:00',
     149            'comment_author'       => 'Internet of something or other',
     150            'comment_author_email' => 'lights@example.org',
     151            'comment_author_url'   => 'http://lights.example.org/',
    98152        ) );
    99153
     
    197251            ),
    198252            array(
    199                 'route' => '/wp/v2/users/1',
     253                'route' => '/wp/v2/users/' . $administrator_id,
    200254                'name'  => 'UserModel',
    201255            ),
     
    218272        );
    219273
    220         // Set up the mocked response and tell jshint to ignore the single quote json objects
    221         $mocked_responses = "/*jshint -W109 */\n\nvar mockedApiResponse = {};\n\n";
    222         $mocked_responses .= "/**\n";
     274        $mocked_responses = "/**\n";
    223275        $mocked_responses .= " * DO NOT EDIT\n";
    224276        $mocked_responses .= " * Auto-generated by test_build_wp_api_client_fixtures\n";
    225277        $mocked_responses .= " */\n";
     278        $mocked_responses .= "var mockedApiResponse = {};\n";
     279        $mocked_responses .= "/* jshint -W109 */\n";
    226280
    227281        foreach ( $routes_to_generate_data as $route ) {
     
    232286            $this->assertTrue( ! empty( $data ), $route['name'] . ' route should return data.' );
    233287
    234             $mocked_responses .= 'mockedApiResponse.' . $route['name'] . ' = ' . wp_json_encode( $data ) . ";\n\n";
     288            if ( version_compare( PHP_VERSION, '5.4', '>=' ) ) {
     289                $fixture = $this->normalize_fixture( $data, $route['name'] );
     290                $mocked_responses .= "\nmockedApiResponse." . $route['name'] . ' = '
     291                    . json_encode( $fixture, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES )
     292                    . ";\n";
     293            }
    235294        }
    236295
    237         // Save the route object for QUnit tests.
    238         $file = './tests/qunit/fixtures/wp-api-generated.js';
    239         file_put_contents( $file, $mocked_responses );
     296        if ( is_multisite() ) {
     297            echo "Skipping generation of API client fixtures in multisite mode.\n";
     298        } else if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
     299            echo "Skipping generation of API client fixtures due to unsupported JSON_* constants.\n";
     300        } else {
     301            // Save the route object for QUnit tests.
     302            $file = './tests/qunit/fixtures/wp-api-generated.js';
     303            file_put_contents( $file, $mocked_responses );
     304        }
    240305
    241306        // Clean up our test data.
     
    246311        wp_delete_comment( $comment_id );
    247312    }
     313
     314    /**
     315     * This array contains normalized versions of object IDs and other values
     316     * that can change depending on how PHPUnit is executed.  For details on
     317     * how they were generated, see #39264.
     318     */
     319    private static $fixture_replacements = array(
     320        'PostsCollection.0.id' => 3,
     321        'PostsCollection.0.guid.rendered' => 'http://example.org/?p=3',
     322        'PostsCollection.0.link' => 'http://example.org/?p=3',
     323        'PostsCollection.0._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/posts/3',
     324        'PostsCollection.0._links.replies.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fcomments&post=3',
     325        'PostsCollection.0._links.version-history.0.href' => 'http://example.org/?rest_route=/wp/v2/posts/3/revisions',
     326        'PostsCollection.0._links.wp:attachment.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3',
     327        'PostsCollection.0._links.wp:term.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fcategories&post=3',
     328        'PostsCollection.0._links.wp:term.1.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Ftags&post=3',
     329        'PostModel.id' => 3,
     330        'PostModel.guid.rendered' => 'http://example.org/?p=3',
     331        'PostModel.link' => 'http://example.org/?p=3',
     332        'postRevisions.0.author' => '2',
     333        'postRevisions.0.id' => 4,
     334        'postRevisions.0.parent' => 3,
     335        'postRevisions.0.slug' => '3-revision-v1',
     336        'postRevisions.0.guid.rendered' => 'http://example.org/?p=4',
     337        'postRevisions.0._links.parent.0.href' => 'http://example.org/?rest_route=/wp/v2/posts/3',
     338        'PagesCollection.0.id' => 5,
     339        'PagesCollection.0.guid.rendered' => 'http://example.org/?page_id=5',
     340        'PagesCollection.0.link' => 'http://example.org/?page_id=5',
     341        'PagesCollection.0._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/pages/5',
     342        'PagesCollection.0._links.replies.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fcomments&post=5',
     343        'PagesCollection.0._links.version-history.0.href' => 'http://example.org/?rest_route=/wp/v2/pages/5/revisions',
     344        'PagesCollection.0._links.wp:attachment.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5',
     345        'PageModel.id' => 5,
     346        'PageModel.guid.rendered' => 'http://example.org/?page_id=5',
     347        'PageModel.link' => 'http://example.org/?page_id=5',
     348        'pageRevisions.0.author' => '2',
     349        'pageRevisions.0.id' => 6,
     350        'pageRevisions.0.parent' => 5,
     351        'pageRevisions.0.slug' => '5-revision-v1',
     352        'pageRevisions.0.guid.rendered' => 'http://example.org/?p=6',
     353        'pageRevisions.0._links.parent.0.href' => 'http://example.org/?rest_route=/wp/v2/pages/5',
     354        'MediaCollection.0.id' => 7,
     355        'MediaCollection.0.guid.rendered' => 'http://example.org/?attachment_id=7',
     356        'MediaCollection.0.link' => 'http://example.org/?attachment_id=7',
     357        'MediaCollection.0._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/media/7',
     358        'MediaCollection.0._links.replies.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fcomments&post=7',
     359        'MediaModel.id' => 7,
     360        'MediaModel.guid.rendered' => 'http://example.org/?attachment_id=7',
     361        'MediaModel.link' => 'http://example.org/?attachment_id=7',
     362        'TagsCollection.0.id' => 2,
     363        'TagsCollection.0._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/tags/2',
     364        'TagsCollection.0._links.wp:post_type.0.href' => 'http://example.org/?rest_route=%2Fwp%2Fv2%2Fposts&tags=2',
     365        'TagModel.id' => 2,
     366        'UsersCollection.1.id' => 2,
     367        'UsersCollection.1.link' => 'http://example.org/?author=2',
     368        'UsersCollection.1._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/users/2',
     369        'UserModel.id' => 2,
     370        'UserModel.link' => 'http://example.org/?author=2',
     371        'me.id' => 2,
     372        'me.link' => 'http://example.org/?author=2',
     373        'CommentsCollection.0.id' => 2,
     374        'CommentsCollection.0.post' => 3,
     375        'CommentsCollection.0.link' => 'http://example.org/?p=3#comment-2',
     376        'CommentsCollection.0._links.self.0.href' => 'http://example.org/?rest_route=/wp/v2/comments/2',
     377        'CommentsCollection.0._links.up.0.href' => 'http://example.org/?rest_route=/wp/v2/posts/3',
     378    );
     379
     380    private function normalize_fixture( $data, $path ) {
     381        if ( isset( self::$fixture_replacements[ $path ] ) ) {
     382            return self::$fixture_replacements[ $path ];
     383        }
     384
     385        if ( ! is_array( $data ) ) {
     386            return $data;
     387        }
     388
     389        foreach ( $data as $key => $value ) {
     390            if ( is_string( $value ) && (
     391                'date' === $key ||
     392                'date_gmt' === $key ||
     393                'modified' === $key ||
     394                'modified_gmt' === $key
     395            ) ) {
     396                $data[ $key ] = '2017-02-14T00:00:00';
     397            } else {
     398                $data[ $key ] = $this->normalize_fixture( $value, "$path.$key" );
     399            }
     400        }
     401
     402        return $data;
     403    }
    248404}
Note: See TracChangeset for help on using the changeset viewer.