diff --git a/inc/class-shortcode-ui.php b/inc/class-shortcode-ui.php index 77671a3f..bfba6f6d 100644 --- a/inc/class-shortcode-ui.php +++ b/inc/class-shortcode-ui.php @@ -67,7 +67,7 @@ private function __construct() { private function setup_actions() { add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); add_action( 'wp_enqueue_editor', array( $this, 'action_wp_enqueue_editor' ) ); - add_action( 'wp_ajax_bulk_do_shortcode', array( $this, 'handle_ajax_bulk_do_shortcode' ) ); + add_action( 'rest_api_init', array( $this, 'action_rest_api_init' ) ); add_filter( 'wp_editor_settings', array( $this, 'filter_wp_editor_settings' ), 10, 2 ); } @@ -247,9 +247,13 @@ public function enqueue() { 'insert_content_label' => __( 'Insert Content', 'shortcode-ui' ), ), 'nonces' => array( - 'preview' => wp_create_nonce( 'shortcode-ui-preview' ), + 'wp_rest' => wp_create_nonce( 'wp_rest' ), 'thumbnailImage' => wp_create_nonce( 'shortcode-ui-get-thumbnail-image' ), ), + 'urls' => array( + 'preview' => rest_url( '/shortcode-ui/v1/preview' ), + 'bulkPreview' => rest_url( '/shortcode-ui/v1/preview/bulk' ), + ), ) ); // add templates to the footer, instead of where we're at now @@ -338,10 +342,6 @@ private function render_shortcode_for_preview( $shortcode, $post_id = null ) { define( 'SHORTCODE_UI_DOING_PREVIEW', true ); } - if ( ! current_user_can( 'edit_post', $post_id ) ) { - return esc_html__( "Something's rotten in the state of Denmark", 'shortcode-ui' ); - } - if ( ! empty( $post_id ) ) { // @codingStandardsIgnoreStart global $post; @@ -369,37 +369,148 @@ private function render_shortcode_for_preview( $shortcode, $post_id = null ) { } /** - * Get a bunch of shortcodes to render in MCE preview. + * Register rest api endpoints. */ - public function handle_ajax_bulk_do_shortcode() { + public function action_rest_api_init() { + + register_rest_route( 'shortcode-ui/v1', 'preview', array( + 'methods' => 'GET', + 'callback' => array( $this, 'rest_preview_callback' ), + 'permission_callback' => array( $this, 'rest_preview_permission_callback' ), + 'args' => array( + 'shortcode' => array( + 'sanitize_callback' => array( $this, 'rest_sanitize_shortcode' ), + 'required' => true, + ), + 'post_id' => array( + 'sanitize_callback' => array( $this, 'rest_sanitize_post_id' ), + 'required' => true, + ), + ), + ) ); - if ( is_array( $_POST['queries'] ) ) { + register_rest_route( 'shortcode-ui/v1', 'preview/bulk', array( + 'methods' => 'GET', + 'callback' => array( $this, 'rest_preview_bulk_callback' ), + 'permission_callback' => array( $this, 'rest_preview_permission_callback' ), + 'args' => array( + 'queries' => array( + 'sanitize_callback' => array( $this, 'rest_sanitize_queries' ), + 'validate_callback' => array( $this, 'rest_validate_queries' ), + ), + 'post_id' => array( + 'sanitize_callback' => array( $this, 'rest_sanitize_post_id' ), + ), + ), + ) ); - $responses = array(); + } - foreach ( $_POST['queries'] as $posted_query ) { + /** + * Permission check for getting a shortcode preview. + * + * @param WP_REST_Request $request + * @return boolean + */ + public function rest_preview_permission_callback( WP_REST_Request $request ) { - // Don't sanitize shortcodes — can contain HTML kses doesn't allow (e.g. sourcecode shortcode) - if ( ! empty( $posted_query['shortcode'] ) ) { - $shortcode = stripslashes( $posted_query['shortcode'] ); - } else { - $shortcode = null; - } - if ( isset( $posted_query['post_id'] ) ) { - $post_id = intval( $posted_query['post_id'] ); - } else { - $post_id = null; - } + $post_id = $request->get_param( 'post_id' ); - $responses[ $posted_query['counter'] ] = array( - 'query' => $posted_query, - 'response' => $this->render_shortcode_for_preview( $shortcode, $post_id ), - ); - } + if ( empty( $post_id ) ) { + return new WP_Error( 'rest_no_post_id', __( 'No Post ID.' ) ); + } + + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return new WP_Error( 'rest_no_edit_post_cap', __( 'You do not have permission to edit this Post.' ) ); + } - wp_send_json_success( $responses ); - exit; + return true; + } + + /** + * Sanitize collection of shortcode queries. + * + * Used for bulk requests. + * + * @param array $queries Queries + * @return string Queries + */ + public function rest_sanitize_queries( $queries ) { + $clean_queries = array(); + foreach ( $queries as $query ) { + $clean_queries[] = array( + 'counter' => absint( $query['counter'] ), + 'shortcode' => $this->rest_sanitize_shortcode( $query['shortcode'] ), + ); } + return $clean_queries; + } + + /** + * Validate collection of shortcodes. + * + * Used for bulk requests. + * + * @param array $queries Queries + * @return boolean + */ + public function rest_validate_queries( $shortcodes ) { + return is_array( $shortcodes ); + } + + /** + * Sanitize rest request shortcode arg. + * + * @param string $shortcode Shortcode + * @return string Shortcode + */ + public function rest_sanitize_shortcode( $shortcode ) { + return stripslashes( $shortcode ); + } + + /** + * Sanitize Post ID. + * + * @param mixed $shortcode Post Id + * @return int Post Id + */ + public function rest_sanitize_post_id( $post_id ) { + return absint( $post_id ); + } + + /** + * Get a preview for a single shortcode to render in MCE preview. + */ + public function rest_preview_callback( WP_REST_Request $request ) { + + $shortcode = $request->get_param( 'shortcode' ); + $post_id = $request->get_param( 'post_id' ); + + return array( + 'shortcode' => $shortcode, + 'post_id' => $post_id, + 'preview' => $this->render_shortcode_for_preview( $shortcode, $post_id ), + ); + } + + /** + * Get a bunch of shortcodes previews to render in MCE preview. + */ + public function rest_preview_bulk_callback( WP_REST_Request $request ) { + + $previews = array(); + $post_id = $request->get_param( 'post_id' ); + + foreach ( $request->get_param( 'queries' ) as $query ) { + $previews[] = array( + 'shortcode' => $query['shortcode'], + 'post_id' => $post_id, + 'counter' => $query['counter'], + 'preview' => $this->render_shortcode_for_preview( $query['shortcode'], $post_id ), + ); + } + + return array_filter( $previews ); } diff --git a/js-tests/build/specs.js b/js-tests/build/specs.js index 6970bbb3..bf17daf2 100644 --- a/js-tests/build/specs.js +++ b/js-tests/build/specs.js @@ -382,65 +382,6 @@ describe( "MCE View Constructor", function() { } ); - describe( "Fetch preview HTML", function() { - - beforeEach(function() { - jasmine.Ajax.install(); - }); - - afterEach(function() { - jasmine.Ajax.uninstall(); - }); - - var constructor = jQuery.extend( true, { - render: function( force ) {}, - }, MceViewConstructor ); - - // Mock shortcode model data. - constructor.shortcodeModel = jQuery.extend( true, {}, sui.shortcodes.first() ); - - it( 'Fetches data success', function(){ - - spyOn( wp.ajax, "post" ).and.callThrough(); - spyOn( constructor, "render" ); - - constructor.fetch(); - - expect( constructor.fetching ).toEqual( true ); - expect( constructor.content ).toEqual( undefined ); - expect( wp.ajax.post ).toHaveBeenCalled(); - expect( constructor.render ).not.toHaveBeenCalled(); - - jasmine.Ajax.requests.mostRecent().respondWith( { - 'status': 200, - 'responseText': '{"success":true,"data":"test preview response body"}' - } ); - - expect( constructor.fetching ).toEqual( undefined ); - expect( constructor.content ).toEqual( 'test preview response body' ); - expect( constructor.render ).toHaveBeenCalled(); - - }); - - it( 'Handles errors when fetching data', function() { - - spyOn( constructor, "render" ); - - constructor.fetch(); - - jasmine.Ajax.requests.mostRecent().respondWith( { - 'status': 500, - 'responseText': '{"success":false}' - }); - - expect( constructor.fetching ).toEqual( undefined ); - expect( constructor.content ).toContain( 'shortcake-error' ); - expect( constructor.render ).toHaveBeenCalled(); - - } ); - - } ); - it( 'parses simple shortcode', function() { var shortcode = MceViewConstructor.parseShortcodeString( '[test_shortcode attr="test value"]'); expect( shortcode instanceof Shortcode ).toEqual( true ); @@ -761,15 +702,13 @@ var Fetcher = (function() { * } * @return {Deferred} */ - this.queueToFetch = function( query ) { + this.queueToFetch = function( shortcode ) { var fetchPromise = new $.Deferred(); - query.counter = ++fetcher.counter; - fetcher.queries.push({ promise: fetchPromise, - query: query, - counter: query.counter + shortcode: shortcode, + counter: ++fetcher.counter }); if ( ! fetcher.timeout ) { @@ -795,22 +734,30 @@ var Fetcher = (function() { return; } - var request = $.post( ajaxurl + '?action=bulk_do_shortcode', { - queries: _.pluck( fetcher.queries, 'query' ) + var request = $.get( shortcodeUIData.urls.bulkPreview, { + _wpnonce: shortcodeUIData.nonces.wp_rest, + post_id: $( '#post_ID' ).val(), + queries: _.map( fetcher.queries, function( query ) { + return { shortcode: query.shortcode, counter: query.counter }; + } ) } ); - request.done( function( response ) { - _.each( response.data, function( result, index ) { + request.done( function( responses ) { + + _.each( responses, function( result ) { + var matchedQuery = _.findWhere( fetcher.queries, { - counter: parseInt( index ), + counter: result.counter, }); if ( matchedQuery ) { fetcher.queries = _.without( fetcher.queries, matchedQuery ); - matchedQuery.promise.resolve( result ); + matchedQuery.promise.resolve( result.preview ); } + } ); + } ); }; @@ -857,8 +804,8 @@ var shortcodeViewConstructor = { this.shortcodeModel = this.getShortcodeModel( this.shortcode ); this.fetching = this.delayedFetch(); - this.fetching.done( function( queryResponse ) { - var response = queryResponse.response; + this.fetching.done( function( response ) { + if ( '' === response ) { var span = $('').addClass('shortcake-notice shortcake-empty').text( self.shortcodeModel.formatShortcode() ); var wrapper = $('
').html( span ); @@ -944,43 +891,7 @@ var shortcodeViewConstructor = { * @return {Promise} */ delayedFetch: function() { - return fetcher.queueToFetch({ - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }); - }, - - /** - * Fetch a preview of a single shortcode. - * - * Async. Sets this.content and calls this.render. - * - * @return undefined - */ - fetch: function() { - var self = this; - - if ( ! this.fetching ) { - this.fetching = true; - - wp.ajax.post( 'do_shortcode', { - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }).done( function( response ) { - if ( '' === response ) { - self.content = '' + self.shortcodeModel.formatShortcode() + ''; - } else { - self.content = response; - } - }).fail( function() { - self.content = '' + shortcodeUIData.strings.mce_view_error + ''; - } ).always( function() { - delete self.fetching; - self.render( null, true ); - } ); - } + return fetcher.queueToFetch( this.shortcodeModel.formatShortcode() ); }, /** diff --git a/js-tests/src/utils/mceViewConstructorSpec.js b/js-tests/src/utils/mceViewConstructorSpec.js index 3f28e61c..54863fa2 100644 --- a/js-tests/src/utils/mceViewConstructorSpec.js +++ b/js-tests/src/utils/mceViewConstructorSpec.js @@ -73,65 +73,6 @@ describe( "MCE View Constructor", function() { } ); - describe( "Fetch preview HTML", function() { - - beforeEach(function() { - jasmine.Ajax.install(); - }); - - afterEach(function() { - jasmine.Ajax.uninstall(); - }); - - var constructor = jQuery.extend( true, { - render: function( force ) {}, - }, MceViewConstructor ); - - // Mock shortcode model data. - constructor.shortcodeModel = jQuery.extend( true, {}, sui.shortcodes.first() ); - - it( 'Fetches data success', function(){ - - spyOn( wp.ajax, "post" ).and.callThrough(); - spyOn( constructor, "render" ); - - constructor.fetch(); - - expect( constructor.fetching ).toEqual( true ); - expect( constructor.content ).toEqual( undefined ); - expect( wp.ajax.post ).toHaveBeenCalled(); - expect( constructor.render ).not.toHaveBeenCalled(); - - jasmine.Ajax.requests.mostRecent().respondWith( { - 'status': 200, - 'responseText': '{"success":true,"data":"test preview response body"}' - } ); - - expect( constructor.fetching ).toEqual( undefined ); - expect( constructor.content ).toEqual( 'test preview response body' ); - expect( constructor.render ).toHaveBeenCalled(); - - }); - - it( 'Handles errors when fetching data', function() { - - spyOn( constructor, "render" ); - - constructor.fetch(); - - jasmine.Ajax.requests.mostRecent().respondWith( { - 'status': 500, - 'responseText': '{"success":false}' - }); - - expect( constructor.fetching ).toEqual( undefined ); - expect( constructor.content ).toContain( 'shortcake-error' ); - expect( constructor.render ).toHaveBeenCalled(); - - } ); - - } ); - it( 'parses simple shortcode', function() { var shortcode = MceViewConstructor.parseShortcodeString( '[test_shortcode attr="test value"]'); expect( shortcode instanceof Shortcode ).toEqual( true ); diff --git a/js/build/shortcode-ui.js b/js/build/shortcode-ui.js index df00c642..a5b03624 100644 --- a/js/build/shortcode-ui.js +++ b/js/build/shortcode-ui.js @@ -335,15 +335,13 @@ var Fetcher = (function() { * } * @return {Deferred} */ - this.queueToFetch = function( query ) { + this.queueToFetch = function( shortcode ) { var fetchPromise = new $.Deferred(); - query.counter = ++fetcher.counter; - fetcher.queries.push({ promise: fetchPromise, - query: query, - counter: query.counter + shortcode: shortcode, + counter: ++fetcher.counter }); if ( ! fetcher.timeout ) { @@ -369,22 +367,30 @@ var Fetcher = (function() { return; } - var request = $.post( ajaxurl + '?action=bulk_do_shortcode', { - queries: _.pluck( fetcher.queries, 'query' ) + var request = $.get( shortcodeUIData.urls.bulkPreview, { + _wpnonce: shortcodeUIData.nonces.wp_rest, + post_id: $( '#post_ID' ).val(), + queries: _.map( fetcher.queries, function( query ) { + return { shortcode: query.shortcode, counter: query.counter }; + } ) } ); - request.done( function( response ) { - _.each( response.data, function( result, index ) { + request.done( function( responses ) { + + _.each( responses, function( result ) { + var matchedQuery = _.findWhere( fetcher.queries, { - counter: parseInt( index ), + counter: result.counter, }); if ( matchedQuery ) { fetcher.queries = _.without( fetcher.queries, matchedQuery ); - matchedQuery.promise.resolve( result ); + matchedQuery.promise.resolve( result.preview ); } + } ); + } ); }; @@ -431,8 +437,8 @@ var shortcodeViewConstructor = { this.shortcodeModel = this.getShortcodeModel( this.shortcode ); this.fetching = this.delayedFetch(); - this.fetching.done( function( queryResponse ) { - var response = queryResponse.response; + this.fetching.done( function( response ) { + if ( '' === response ) { var span = $('').addClass('shortcake-notice shortcake-empty').text( self.shortcodeModel.formatShortcode() ); var wrapper = $('').html( span ); @@ -518,43 +524,7 @@ var shortcodeViewConstructor = { * @return {Promise} */ delayedFetch: function() { - return fetcher.queueToFetch({ - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }); - }, - - /** - * Fetch a preview of a single shortcode. - * - * Async. Sets this.content and calls this.render. - * - * @return undefined - */ - fetch: function() { - var self = this; - - if ( ! this.fetching ) { - this.fetching = true; - - wp.ajax.post( 'do_shortcode', { - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }).done( function( response ) { - if ( '' === response ) { - self.content = '' + self.shortcodeModel.formatShortcode() + ''; - } else { - self.content = response; - } - }).fail( function() { - self.content = '' + shortcodeUIData.strings.mce_view_error + ''; - } ).always( function() { - delete self.fetching; - self.render( null, true ); - } ); - } + return fetcher.queueToFetch( this.shortcodeModel.formatShortcode() ); }, /** diff --git a/js/src/utils/fetcher.js b/js/src/utils/fetcher.js index a406c7a2..62d2c6d5 100644 --- a/js/src/utils/fetcher.js +++ b/js/src/utils/fetcher.js @@ -47,15 +47,13 @@ var Fetcher = (function() { * } * @return {Deferred} */ - this.queueToFetch = function( query ) { + this.queueToFetch = function( shortcode ) { var fetchPromise = new $.Deferred(); - query.counter = ++fetcher.counter; - fetcher.queries.push({ promise: fetchPromise, - query: query, - counter: query.counter + shortcode: shortcode, + counter: ++fetcher.counter }); if ( ! fetcher.timeout ) { @@ -81,22 +79,30 @@ var Fetcher = (function() { return; } - var request = $.post( ajaxurl + '?action=bulk_do_shortcode', { - queries: _.pluck( fetcher.queries, 'query' ) + var request = $.get( shortcodeUIData.urls.bulkPreview, { + _wpnonce: shortcodeUIData.nonces.wp_rest, + post_id: $( '#post_ID' ).val(), + queries: _.map( fetcher.queries, function( query ) { + return { shortcode: query.shortcode, counter: query.counter }; + } ) } ); - request.done( function( response ) { - _.each( response.data, function( result, index ) { + request.done( function( responses ) { + + _.each( responses, function( result ) { + var matchedQuery = _.findWhere( fetcher.queries, { - counter: parseInt( index ), + counter: result.counter, }); if ( matchedQuery ) { fetcher.queries = _.without( fetcher.queries, matchedQuery ); - matchedQuery.promise.resolve( result ); + matchedQuery.promise.resolve( result.preview ); } + } ); + } ); }; diff --git a/js/src/utils/shortcode-view-constructor.js b/js/src/utils/shortcode-view-constructor.js index c67eee75..898c8c4c 100644 --- a/js/src/utils/shortcode-view-constructor.js +++ b/js/src/utils/shortcode-view-constructor.js @@ -28,8 +28,8 @@ var shortcodeViewConstructor = { this.shortcodeModel = this.getShortcodeModel( this.shortcode ); this.fetching = this.delayedFetch(); - this.fetching.done( function( queryResponse ) { - var response = queryResponse.response; + this.fetching.done( function( response ) { + if ( '' === response ) { var span = $('').addClass('shortcake-notice shortcake-empty').text( self.shortcodeModel.formatShortcode() ); var wrapper = $('').html( span ); @@ -115,43 +115,7 @@ var shortcodeViewConstructor = { * @return {Promise} */ delayedFetch: function() { - return fetcher.queueToFetch({ - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }); - }, - - /** - * Fetch a preview of a single shortcode. - * - * Async. Sets this.content and calls this.render. - * - * @return undefined - */ - fetch: function() { - var self = this; - - if ( ! this.fetching ) { - this.fetching = true; - - wp.ajax.post( 'do_shortcode', { - post_id: $( '#post_ID' ).val(), - shortcode: this.shortcodeModel.formatShortcode(), - nonce: shortcodeUIData.nonces.preview, - }).done( function( response ) { - if ( '' === response ) { - self.content = '' + self.shortcodeModel.formatShortcode() + ''; - } else { - self.content = response; - } - }).fail( function() { - self.content = '' + shortcodeUIData.strings.mce_view_error + ''; - } ).always( function() { - delete self.fetching; - self.render( null, true ); - } ); - } + return fetcher.queueToFetch( this.shortcodeModel.formatShortcode() ); }, /** diff --git a/php-tests/test-shortcode-ui.php b/php-tests/test-shortcode-ui.php index cf896c8f..749147c1 100644 --- a/php-tests/test-shortcode-ui.php +++ b/php-tests/test-shortcode-ui.php @@ -2,6 +2,14 @@ class Test_Shortcode_UI extends WP_UnitTestCase { + public function setUp() { + add_shortcode( 'test', array( $this, 'return_foo' ) ); + } + + public function return_foo() { + return 'foo'; + } + public function test_plugin_loaded() { $this->assertTrue( function_exists( 'shortcode_ui_register_for_shortcode' ) ); } @@ -62,4 +70,113 @@ public function test_register_shortcode_malicious_html() { $this->assertEquals( 'banana()', $shortcodes['foo']['attrs'][0]['description'] ); } + public function action_rest_api_init() { + + } + + public function test_rest_preview_permission_callback() { + + $instance = Shortcode_UI::get_instance(); + $author_id = self::factory()->user->create( array( 'role' => 'author' ) ); + $author_id_2 = self::factory()->user->create( array( 'role' => 'author' ) ); + $post_id = self::factory()->post->create( array( 'post_author' => $author_id ) ); + + wp_set_current_user( $author_id ); + + $rest_request = new WP_REST_Request( 'GET', 'shortcode-ui/v1/preview' ); + + $permission = $instance->rest_preview_permission_callback( $rest_request ); + + // Test that you cannot generate a shortcode preview without passing a post ID + $this->assertInstanceOf( 'WP_Error', $permission ); + + $rest_request->set_param( 'post_id', $post_id ); + $permission = $instance->rest_preview_permission_callback( $rest_request ); + + // Assert that the user who created a post, + // is able to preview a shortcode for that post. + $this->assertTrue( $permission ); + + wp_set_current_user( $author_id_2 ); + + // Assert that a user who did NOT create the post + // is NOT able to preview a shortcode for that post. + $permission = $instance->rest_preview_permission_callback( $rest_request ); + $this->assertInstanceOf( 'WP_Error', $permission ); + + } + + public function test_rest_sanitize_queries() { + + $queries = Shortcode_UI::get_instance()->rest_sanitize_queries( array( + array( + 'counter' => 'foo', + 'shortcode' => '\foo', + ), + ) ); + + $this->assertEquals( count( $queries ), 1 ); + $this->assertInternalType( 'array', $queries ); + $this->assertInternalType( 'array', $queries[0] ); + $this->assertEquals( $queries[0]['counter'], 0 ); + $this->assertEquals( $queries[0]['shortcode'], 'foo' ); + + } + + public function test_rest_validate_queries() { + $this->assertTrue( Shortcode_UI::get_instance()->rest_validate_queries( array() ) ); + $this->assertFalse( Shortcode_UI::get_instance()->rest_validate_queries( 'foo' ) ); + } + + public function test_rest_sanitize_post_id() { + $this->assertInternalType( 'int', Shortcode_UI::get_instance()->rest_sanitize_post_id( 'foo' ) ); + } + + public function test_rest_preview_callback() { + + $author_id = self::factory()->user->create( array( 'role' => 'author' ) ); + $post_id = self::factory()->post->create( array( 'post_author' => $author_id ) ); + + wp_set_current_user( $author_id ); + + $rest_request = new WP_REST_Request( 'GET', 'shortcode-ui/v1/preview' ); + $rest_request->set_param( 'post_id', $post_id ); + $rest_request->set_param( 'shortcode', '[test]' ); + + $response = Shortcode_UI::get_instance()->rest_preview_callback( $rest_request ); + + $this->assertEquals( $response['shortcode'], '[test]' ); + $this->assertEquals( $response['post_id'], $post_id ); + $this->assertEquals( $response['preview'], 'foo' ); + + } + + public function test_rest_preview_bulk_callback() { + + $author_id = self::factory()->user->create( array( 'role' => 'author' ) ); + $post_id = self::factory()->post->create( array( 'post_author' => $author_id ) ); + + wp_set_current_user( $author_id ); + + $queries = array( + array( + 'counter' => 1, + 'shortcode' => '[test]', + ), + ); + + $rest_request = new WP_REST_Request( 'GET', 'shortcode-ui/v1/preview/bulk' ); + $rest_request->set_param( 'post_id', $post_id ); + $rest_request->set_param( 'queries', $queries ); + + $response = Shortcode_UI::get_instance()->rest_preview_bulk_callback( $rest_request ); + + $this->assertInternalType( 'array', $response ); + $this->assertEquals( $response[0]['shortcode'], '[test]' ); + $this->assertEquals( $response[0]['post_id'], $post_id ); + $this->assertEquals( $response[0]['counter'], 1 ); + $this->assertEquals( $response[0]['preview'], 'foo' ); + + } + }