Make WordPress Core


Ignore:
Timestamp:
07/16/2024 09:42:38 AM (10 months ago)
Author:
Bernhard Reiter
Message:

Interactivity API: Use Script Modules filter for store & config data.

A dedicated API exists for passing data to Script Modules implemented in changeset [58579].
Use this Core API instead of a custom implementation for Interactivity API to pass data to the client.

Developed in https://github.com/WordPress/wordpress-develop/pull/6683.

Props jonsurrell, gziolo, luisherranz, cbravobernal.
Fixes #61512.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php

    r58594 r58729  
    185185
    186186    /**
    187      * Invokes the private `print_client_interactivity` method of
    188      * WP_Interactivity_API class.
    189      *
    190      * @return array|null The content of the JSON object printed on the client-side or null if nothings was printed.
    191      */
    192     private function print_client_interactivity_data() {
    193         $interactivity_data_markup = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    194         preg_match( '/<script type="application\/json" id="wp-interactivity-data">.*?(\{.*\}).*?<\/script>/s', $interactivity_data_markup, $interactivity_data_string );
    195         return isset( $interactivity_data_string[1] ) ? json_decode( $interactivity_data_string[1], true ) : null;
    196     }
    197 
    198     /**
    199      * Tests that the initial state and config are correctly printed on the
    200      * client-side.
    201      *
    202      * @ticket 60356
    203      *
    204      * @covers ::state
    205      * @covers ::config
    206      * @covers ::print_client_interactivity_data
    207      */
    208     public function test_state_and_config_is_correctly_printed() {
    209         $this->interactivity->state( 'myPlugin', array( 'a' => 1 ) );
    210         $this->interactivity->state( 'otherPlugin', array( 'b' => 2 ) );
    211         $this->interactivity->config( 'myPlugin', array( 'a' => 1 ) );
    212         $this->interactivity->config( 'otherPlugin', array( 'b' => 2 ) );
    213 
    214         $result = $this->print_client_interactivity_data();
    215 
    216         $data = array(
    217             'myPlugin'    => array( 'a' => 1 ),
    218             'otherPlugin' => array( 'b' => 2 ),
    219         );
    220 
    221         $this->assertSame(
    222             array(
    223                 'config' => $data,
    224                 'state'  => $data,
    225             ),
    226             $result
    227         );
    228     }
    229 
    230     /**
    231187     * Tests that the wp-interactivity-data script is not printed if both state
    232188     * and config are empty.
    233189     *
    234190     * @ticket 60356
     191     * @ticket 61512
     192     */
     193    public function test_state_and_config_dont_print_when_empty() {
     194        $filter = $this->get_script_data_filter_result();
     195
     196        $this->assertSame( array(), $filter->get_args()[0][0] );
     197    }
     198
     199    /**
     200     * Test that the print_client_interactivity_data is deprecated and produces no output.
     201     *
     202     * @ticket 60356
     203     * @ticket 61512
    235204     *
    236205     * @covers ::print_client_interactivity_data
    237      */
    238     public function test_state_and_config_dont_print_when_empty() {
    239         $result = $this->print_client_interactivity_data();
    240         $this->assertNull( $result );
    241     }
    242 
    243     /**
    244      * Tests that the config is not printed if it's empty.
    245      *
    246      * @ticket 60356
    247      *
    248      * @covers ::state
    249      * @covers ::print_client_interactivity_data
     206     *
     207     * @expectedDeprecated WP_Interactivity_API::print_client_interactivity_data
    250208     */
    251209    public function test_config_not_printed_when_empty() {
    252         $this->interactivity->state( 'myPlugin', array( 'a' => 1 ) );
    253         $result = $this->print_client_interactivity_data();
    254         $this->assertSame( array( 'state' => array( 'myPlugin' => array( 'a' => 1 ) ) ), $result );
    255     }
    256 
    257     /**
    258      * Tests that the state is not printed if it's empty.
    259      *
    260      * @ticket 60356
    261      *
    262      * @covers ::config
    263      * @covers ::print_client_interactivity_data
     210        $this->interactivity->print_client_interactivity_data();
     211        $this->expectOutputString( '' );
     212    }
     213
     214    /**
     215     * Sets up an activity, runs an optional callback, and returns a MockAction for inspection.
     216     *
     217     * @since 6.7.0
     218     *
     219     * @param  ?Closure $callback Optional. Callback to run to set up interactivity state and config.
     220     * @return MockAction
     221     */
     222    private function get_script_data_filter_result( ?Closure $callback = null ): MockAction {
     223        $this->interactivity->add_hooks();
     224        $this->interactivity->register_script_modules();
     225        wp_enqueue_script_module( '@wordpress/interactivity' );
     226        $filter = new MockAction();
     227        add_filter( 'script_module_data_@wordpress/interactivity', array( $filter, 'filter' ) );
     228
     229        if ( $callback ) {
     230            $callback();
     231        }
     232
     233        ob_start();
     234        wp_script_modules()->print_script_module_data();
     235        ob_end_clean();
     236
     237        return $filter;
     238    }
     239
     240    /**
     241     * Tests that the state is not included in client data if it's empty.
     242     *
     243     * @ticket 60356
     244     * @ticket 61512
    264245     */
    265246    public function test_state_not_printed_when_empty() {
    266         $this->interactivity->config( 'myPlugin', array( 'a' => 1 ) );
    267         $result = $this->print_client_interactivity_data();
    268         $this->assertSame( array( 'config' => array( 'myPlugin' => array( 'a' => 1 ) ) ), $result );
     247        $filter = $this->get_script_data_filter_result(
     248            function () {
     249                $this->interactivity->config( 'myPlugin', array( 'a' => 1 ) );
     250            }
     251        );
     252
     253        $this->assertSame( array( 'config' => array( 'myPlugin' => array( 'a' => 1 ) ) ), $filter->get_args()[0][0] );
    269254    }
    270255
     
    273258     *
    274259     * @ticket 60761
    275      *
    276      * @covers ::print_client_interactivity_data
     260     * @ticket 61512
    277261     */
    278262    public function test_state_not_printed_when_empty_array() {
    279         $this->interactivity->state( 'pluginWithEmptyState_prune', array() );
    280         $this->interactivity->state( 'pluginWithState_include', array( 'value' => 'excellent' ) );
    281         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    282         $expected       = <<<'SCRIPT_TAG'
    283 <script type="application/json" id="wp-interactivity-data">
    284 {"state":{"pluginWithState_include":{"value":"excellent"}}}
    285 </script>
    286 
    287 SCRIPT_TAG;
    288 
    289         $this->assertSameIgnoreEOL( $expected, $printed_script );
     263        $filter = $this->get_script_data_filter_result(
     264            function () {
     265                $this->interactivity->state( 'pluginWithEmptyState_prune', array() );
     266                $this->interactivity->state( 'pluginWithState_include', array( 'value' => 'excellent' ) );
     267            }
     268        );
     269
     270        $this->assertSame( array( 'state' => array( 'pluginWithState_include' => array( 'value' => 'excellent' ) ) ), $filter->get_args()[0][0] );
    290271    }
    291272
     
    294275     *
    295276     * @ticket 60761
    296      *
    297      * @covers ::print_client_interactivity_data
     277     * @ticket 61512
    298278     */
    299279    public function test_state_not_printed_when_only_empty_arrays() {
    300         $this->interactivity->state( 'pluginWithEmptyState_prune', array() );
    301         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    302         $this->assertSame( '', $printed_script );
     280        $filter = $this->get_script_data_filter_result(
     281            function () {
     282                $this->interactivity->state( 'pluginWithEmptyState_prune', array() );
     283            }
     284        );
     285
     286        $this->assertSame( array(), $filter->get_args()[0][0] );
    303287    }
    304288
     
    307291     *
    308292     * @ticket 60761
    309      *
    310      * @covers ::print_client_interactivity_data
     293     * @ticket 61512
    311294     */
    312295    public function test_state_printed_correctly_with_nested_empty_array() {
    313         $this->interactivity->state( 'myPlugin', array( 'emptyArray' => array() ) );
    314         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    315         $expected       = <<<'SCRIPT_TAG'
    316 <script type="application/json" id="wp-interactivity-data">
    317 {"state":{"myPlugin":{"emptyArray":[]}}}
    318 </script>
    319 
    320 SCRIPT_TAG;
    321 
    322         $this->assertSameIgnoreEOL( $expected, $printed_script );
     296        $filter = $this->get_script_data_filter_result(
     297            function () {
     298                $this->interactivity->state( 'myPlugin', array( 'emptyArray' => array() ) );
     299            }
     300        );
     301
     302        $this->assertSame( array( 'state' => array( 'myPlugin' => array( 'emptyArray' => array() ) ) ), $filter->get_args()[0][0] );
    323303    }
    324304
     
    327307     *
    328308     * @ticket 60761
    329      *
    330      * @covers ::print_client_interactivity_data
     309     * @ticket 61512
    331310     */
    332311    public function test_config_not_printed_when_empty_array() {
    333         $this->interactivity->config( 'pluginWithEmptyConfig_prune', array() );
    334         $this->interactivity->config( 'pluginWithConfig_include', array( 'value' => 'excellent' ) );
    335         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    336         $expected       = <<<'SCRIPT_TAG'
    337 <script type="application/json" id="wp-interactivity-data">
    338 {"config":{"pluginWithConfig_include":{"value":"excellent"}}}
    339 </script>
    340 
    341 SCRIPT_TAG;
    342 
    343         $this->assertSameIgnoreEOL( $expected, $printed_script );
     312        $filter = $this->get_script_data_filter_result(
     313            function () {
     314                $this->interactivity->config( 'pluginWithEmptyConfig_prune', array() );
     315                $this->interactivity->config( 'pluginWithConfig_include', array( 'value' => 'excellent' ) );
     316            }
     317        );
     318
     319        $this->assertSame( array( 'config' => array( 'pluginWithConfig_include' => array( 'value' => 'excellent' ) ) ), $filter->get_args()[0][0] );
    344320    }
    345321
     
    348324     *
    349325     * @ticket 60761
    350      *
    351      * @covers ::print_client_interactivity_data
     326     * @ticket 61512
    352327     */
    353328    public function test_config_not_printed_when_only_empty_arrays() {
    354         $this->interactivity->config( 'pluginWithEmptyConfig_prune', array() );
    355         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    356         $this->assertSame( '', $printed_script );
     329        $filter = $this->get_script_data_filter_result(
     330            function () {
     331                $this->interactivity->config( 'pluginWithEmptyConfig_prune', array() );
     332            }
     333        );
     334
     335        $this->assertSame( array(), $filter->get_args()[0][0] );
    357336    }
    358337
     
    361340     *
    362341     * @ticket 60761
    363      *
    364      * @covers ::print_client_interactivity_data
     342     * @ticket 61512
    365343     */
    366344    public function test_config_printed_correctly_with_nested_empty_array() {
    367         $this->interactivity->config( 'myPlugin', array( 'emptyArray' => array() ) );
    368         $printed_script = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    369         $expected       = <<<'SCRIPT_TAG'
    370 <script type="application/json" id="wp-interactivity-data">
    371 {"config":{"myPlugin":{"emptyArray":[]}}}
    372 </script>
    373 
    374 SCRIPT_TAG;
    375 
    376         $this->assertSameIgnoreEOL( $expected, $printed_script );
    377     }
    378 
    379     /**
    380      * Tests that special characters in the initial state and configuration are
    381      * properly escaped.
    382      *
    383      * @ticket 60356
    384      * @ticket 61170
    385      *
    386      * @covers ::state
    387      * @covers ::config
    388      * @covers ::print_client_interactivity_data
    389      */
    390     public function test_state_and_config_escape_special_characters() {
    391         $this->interactivity->state(
    392             'myPlugin',
    393             array(
    394                 'ampersand'                              => '&',
    395                 'less-than sign'                         => '<',
    396                 'greater-than sign'                      => '>',
    397                 'solidus'                                => '/',
    398                 'line separator'                         => "\u{2028}",
    399                 'paragraph separator'                    => "\u{2029}",
    400                 'flag of england'                        => "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}",
    401                 'malicious script closer'                => '</script>',
    402                 'entity-encoded malicious script closer' => '&lt;/script&gt;',
    403             )
    404         );
    405         $this->interactivity->config( 'myPlugin', array( 'chars' => '&<>/' ) );
    406 
    407         $interactivity_data_markup = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    408         preg_match( '~<script type="application/json" id="wp-interactivity-data">\s*(\{.*\})\s*</script>~s', $interactivity_data_markup, $interactivity_data_string );
    409 
    410         $expected = <<<"JSON"
    411 {"config":{"myPlugin":{"chars":"&\\u003C\\u003E/"}},"state":{"myPlugin":{"ampersand":"&","less-than sign":"\\u003C","greater-than sign":"\\u003E","solidus":"/","line separator":"\u{2028}","paragraph separator":"\u{2029}","flag of england":"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}","malicious script closer":"\\u003C/script\\u003E","entity-encoded malicious script closer":"&lt;/script&gt;"}}}
    412 JSON;
    413         $this->assertSame( $expected, $interactivity_data_string[1] );
    414     }
    415 
    416     /**
    417      * Tests that special characters in the initial state and configuration are
    418      * properly escaped when the blog_charset is not UTF-8 (unicode compatible).
    419      *
    420      * This this test, unicode and line terminators should be escaped to their
    421      * JSON unicode sequences.
    422      *
    423      * @ticket 61170
    424      *
    425      * @covers ::state
    426      * @covers ::config
    427      * @covers ::print_client_interactivity_data
    428      */
    429     public function test_state_and_config_escape_special_characters_non_utf8() {
    430         add_filter( 'pre_option_blog_charset', array( $this, 'charset_iso_8859_1' ) );
    431         $this->interactivity->state(
    432             'myPlugin',
    433             array(
    434                 'ampersand'                              => '&',
    435                 'less-than sign'                         => '<',
    436                 'greater-than sign'                      => '>',
    437                 'solidus'                                => '/',
    438                 'line separator'                         => "\u{2028}",
    439                 'paragraph separator'                    => "\u{2029}",
    440                 'flag of england'                        => "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}",
    441                 'malicious script closer'                => '</script>',
    442                 'entity-encoded malicious script closer' => '&lt;/script&gt;',
    443             )
    444         );
    445         $this->interactivity->config( 'myPlugin', array( 'chars' => '&<>/' ) );
    446 
    447         $interactivity_data_markup = get_echo( array( $this->interactivity, 'print_client_interactivity_data' ) );
    448         preg_match( '~<script type="application/json" id="wp-interactivity-data">\s*(\{.*\})\s*</script>~s', $interactivity_data_markup, $interactivity_data_string );
    449 
    450         $expected = <<<"JSON"
    451 {"config":{"myPlugin":{"chars":"&\\u003C\\u003E/"}},"state":{"myPlugin":{"ampersand":"&","less-than sign":"\\u003C","greater-than sign":"\\u003E","solidus":"/","line separator":"\\u2028","paragraph separator":"\\u2029","flag of england":"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f","malicious script closer":"\\u003C/script\\u003E","entity-encoded malicious script closer":"&lt;/script&gt;"}}}
    452 JSON;
    453         $this->assertSame( $expected, $interactivity_data_string[1] );
     345        $filter = $this->get_script_data_filter_result(
     346            function () {
     347                $this->interactivity->config( 'myPlugin', array( 'emptyArray' => array() ) );
     348            }
     349        );
     350
     351        $this->assertSame( array( 'config' => array( 'myPlugin' => array( 'emptyArray' => array() ) ) ), $filter->get_args()[0][0] );
    454352    }
    455353
Note: See TracChangeset for help on using the changeset viewer.