WordPress.org

Make WordPress Core


Ignore:
Timestamp:
02/24/17 22:33:05 (3 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.