WordPress.org

Make WordPress Core

Ticket #41394: 41394.1.diff

File 41394.1.diff, 26.9 KB (added by westonruter, 4 years ago)

https://github.com/xwp/wordpress-develop/pull/241/files/2d1362d..6e8ef5d

  • src/wp-admin/js/widgets/text-widgets.js

    diff --git src/wp-admin/js/widgets/text-widgets.js src/wp-admin/js/widgets/text-widgets.js
    index 282090d585..692a0642c9 100644
    wp.textWidgets = ( function( $ ) { 
    368368                }
    369369
    370370                // Bypass using TinyMCE when widget is in legacy mode.
    371                 if ( widgetForm.find( '.legacy' ).length > 0 ) {
     371                if ( ! widgetForm.find( '.visual' ).val() ) {
    372372                        return;
    373373                }
    374374
    wp.textWidgets = ( function( $ ) { 
    429429                }
    430430
    431431                // Bypass using TinyMCE when widget is in legacy mode.
    432                 if ( widgetForm.find( '.legacy' ).length > 0 ) {
     432                if ( ! widgetForm.find( '.visual' ).val() ) {
    433433                        return;
    434434                }
    435435
  • src/wp-includes/widgets/class-wp-widget-custom-html.php

    diff --git src/wp-includes/widgets/class-wp-widget-custom-html.php src/wp-includes/widgets/class-wp-widget-custom-html.php
    index 7fcce78f19..82f1ff944c 100644
    class WP_Widget_Custom_HTML extends WP_Widget { 
    6161                /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
    6262                $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
    6363
     64                // Prepare instance data that looks like a normal Text widget.
     65                $simulated_text_widget_instance = $instance;
     66                $simulated_text_widget_instance['text'] = $simulated_text_widget_instance['content'];
     67                unset( $simulated_text_widget_instance['content'] );
     68                $simulated_text_widget_instance['filter'] = false; // Because wpautop is not applied.
     69
    6470                /** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */
    65                 $content = apply_filters( 'widget_text', $instance['content'], $instance, $this );
     71                $content = apply_filters( 'widget_text', $instance['content'], $simulated_text_widget_instance, $this );
    6672
    6773                /**
    6874                 * Filters the content of the Custom HTML widget.
  • src/wp-includes/widgets/class-wp-widget-text.php

    diff --git src/wp-includes/widgets/class-wp-widget-text.php src/wp-includes/widgets/class-wp-widget-text.php
    index 2297d2bee2..0335a3e489 100644
    class WP_Widget_Text extends WP_Widget { 
    7979         */
    8080        public function is_legacy_instance( $instance ) {
    8181
    82                 // If the widget has been updated while in legacy mode, it stays in legacy mode.
    83                 if ( ! empty( $instance['legacy'] ) ) {
    84                         return true;
     82                // Legacy mode when not in visual mode.
     83                if ( isset( $instance['visual'] ) ) {
     84                        return ! $instance['visual'];
    8585                }
    8686
    87                 // If the widget has been added/updated in 4.8 then filter prop is 'content' and it is no longer legacy.
     87                // Or, the widget has been added/updated in 4.8.0 then filter prop is 'content' and it is no longer legacy.
    8888                if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) {
    8989                        return false;
    9090                }
    class WP_Widget_Text extends WP_Widget { 
    193193                $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
    194194
    195195                $text = ! empty( $instance['text'] ) ? $instance['text'] : '';
    196                 $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] );
     196                $is_visual_text_widget = ( ! empty( $instance['visual'] ) && ! empty( $instance['filter'] ) );
     197
     198                // In 4.8.0 only, visual Text widgets get filter=content, without visual prop; upgrade instance props just-in-time.
     199                if ( ! $is_visual_text_widget ) {
     200                        $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] );
     201                }
     202                if ( $is_visual_text_widget ) {
     203                        $instance['filter'] = true;
     204                        $instance['visual'] = true;
     205                }
    197206
    198207                /*
    199208                 * Just-in-time temporarily upgrade Visual Text widget shortcode handling
    class WP_Widget_Text extends WP_Widget { 
    221230                 */
    222231                $text = apply_filters( 'widget_text', $text, $instance, $this );
    223232
    224                 if ( isset( $instance['filter'] ) ) {
    225                         if ( 'content' === $instance['filter'] ) {
    226 
    227                                 /**
    228                                  * Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor.
    229                                  *
    230                                  * By default a subset of the_content filters are applied, including wpautop and wptexturize.
    231                                  *
    232                                  * @since 4.8.0
    233                                  *
    234                                  * @param string         $text     The widget content.
    235                                  * @param array          $instance Array of settings for the current widget.
    236                                  * @param WP_Widget_Text $this     Current Text widget instance.
    237                                  */
    238                                 $text = apply_filters( 'widget_text_content', $text, $instance, $this );
    239 
    240                         } elseif ( $instance['filter'] ) {
    241                                 $text = wpautop( $text ); // Back-compat for instances prior to 4.8.
    242                         }
     233                if ( $is_visual_text_widget ) {
     234
     235                        /**
     236                         * Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor.
     237                         *
     238                         * By default a subset of the_content filters are applied, including wpautop and wptexturize.
     239                         *
     240                         * @since 4.8.0
     241                         *
     242                         * @param string         $text     The widget content.
     243                         * @param array          $instance Array of settings for the current widget.
     244                         * @param WP_Widget_Text $this     Current Text widget instance.
     245                         */
     246                        $text = apply_filters( 'widget_text_content', $text, $instance, $this );
     247
     248                } elseif ( ! empty( $instance['filter'] ) ) {
     249                        $text = wpautop( $text ); // Back-compat for instances prior to 4.8.
    243250                }
    244251
    245252                // Undo temporary upgrade of the plugin-supplied shortcode handling.
    class WP_Widget_Text extends WP_Widget { 
    271278         * @return array Settings to save or bool false to cancel saving.
    272279         */
    273280        public function update( $new_instance, $old_instance ) {
     281                $new_instance = wp_parse_args( $new_instance, array(
     282                        'title' => '',
     283                        'text' => '',
     284                        'filter' => false, // For back-compat.
     285                        'visual' => null, // Must be explicitly defined.
     286                ) );
     287
    274288                $instance = $old_instance;
     289
    275290                $instance['title'] = sanitize_text_field( $new_instance['title'] );
    276291                if ( current_user_can( 'unfiltered_html' ) ) {
    277292                        $instance['text'] = $new_instance['text'];
    class WP_Widget_Text extends WP_Widget { 
    279294                        $instance['text'] = wp_kses_post( $new_instance['text'] );
    280295                }
    281296
    282                 /*
    283                  * If the Text widget is in legacy mode, then a hidden input will indicate this
    284                  * and the new content value for the filter prop will by bypassed. Otherwise,
    285                  * re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply.
    286                  * Prior to 4.8, this is a boolean value used to indicate whether or not wpautop should be
    287                  * applied. By re-using this property, downgrading WordPress from 4.8 to 4.7 will ensure
    288                  * that the content for Text widgets created with TinyMCE will continue to get wpautop.
    289                  */
    290                 if ( isset( $new_instance['legacy'] ) || isset( $old_instance['legacy'] ) || ( isset( $new_instance['filter'] ) && 'content' !== $new_instance['filter'] ) ) {
    291                         $instance['filter'] = ! empty( $new_instance['filter'] );
    292                         $instance['legacy'] = true;
    293                 } else {
    294                         $instance['filter'] = 'content';
    295                         unset( $instance['legacy'] );
     297                $instance['filter'] = ! empty( $new_instance['filter'] );
     298
     299                // Upgrade 4.8.0 format.
     300                if ( isset( $old_instance['filter'] ) && 'content' === $old_instance['filter'] ) {
     301                        $instance['visual'] = true;
     302                }
     303                if ( 'content' === $new_instance['filter'] ) {
     304                        $instance['visual'] = true;
     305                }
     306
     307                if ( isset( $new_instance['visual'] ) ) {
     308                        $instance['visual'] = ! empty( $new_instance['visual'] );
     309                }
     310
     311                // Filter is always true in visual mode.
     312                if ( ! empty( $instance['visual'] ) ) {
     313                        $instance['filter'] = true;
    296314                }
    297315
    298316                return $instance;
    class WP_Widget_Text extends WP_Widget { 
    333351                <?php if ( ! $this->is_legacy_instance( $instance ) ) : ?>
    334352                        <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>">
    335353                        <input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>">
     354                        <input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" class="filter" type="hidden" value="1">
     355                        <input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual" type="hidden" value="1">
    336356                <?php else : ?>
    337                         <input name="<?php echo $this->get_field_name( 'legacy' ); ?>" type="hidden" class="legacy" value="true">
     357                        <input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual" type="hidden" value="">
    338358                        <p>
    339359                                <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
    340360                                <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
  • tests/phpunit/tests/widgets/custom-html-widget.php

    diff --git tests/phpunit/tests/widgets/custom-html-widget.php tests/phpunit/tests/widgets/custom-html-widget.php
    index dcd03d2007..3d39b673cc 100644
    class Test_WP_Widget_Custom_HTML extends WP_UnitTestCase { 
    5959                        'content' => $content,
    6060                );
    6161
     62                // Convert Custom HTML widget instance into Text widget instance data.
     63                $text_widget_instance = array_merge( $instance, array(
     64                        'filter' => false,
     65                        'text' => $instance['content'],
     66                ) );
     67                unset( $text_widget_instance['content'] );
     68
    6269                update_option( 'use_balanceTags', 0 );
    6370                add_filter( 'widget_custom_html_content', array( $this, 'filter_widget_custom_html_content' ), 5, 3 );
    6471                add_filter( 'widget_text', array( $this, 'filter_widget_text' ), 10, 3 );
    class Test_WP_Widget_Custom_HTML extends WP_UnitTestCase { 
    7582                $this->assertNotContains( '<p>', $output );
    7683                $this->assertNotContains( '<br>', $output );
    7784                $this->assertNotContains( '</u>', $output );
    78                 $this->assertEquals( $instance, $this->widget_text_args[1] );
     85                $this->assertEquals( $text_widget_instance, $this->widget_text_args[1] );
    7986                $this->assertEquals( $instance, $this->widget_custom_html_content_args[1] );
    8087                $this->assertSame( $widget, $this->widget_text_args[2] );
    8188                $this->assertSame( $widget, $this->widget_custom_html_content_args[2] );
    82                 remove_filter( 'widget_custom_html_content', array( $this, 'filter_widget_custom_html_content' ), 5, 3 );
     89                remove_filter( 'widget_custom_html_content', array( $this, 'filter_widget_custom_html_content' ), 5 );
    8390                remove_filter( 'widget_text', array( $this, 'filter_widget_text' ), 10 );
    8491
    8592                update_option( 'use_balanceTags', 1 );
  • tests/phpunit/tests/widgets/text-widget.php

    diff --git tests/phpunit/tests/widgets/text-widget.php tests/phpunit/tests/widgets/text-widget.php
    index 764b4eaeac..c65e2e0f5c 100644
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    8282                        'before_widget' => '<section>',
    8383                        'after_widget'  => "</section>\n",
    8484                );
    85                 $instance = array(
    86                         'title'  => 'Foo',
    87                         'text'   => $text,
    88                         'filter' => false,
    89                 );
    9085
    9186                add_filter( 'widget_text_content', array( $this, 'filter_widget_text_content' ), 5, 3 );
    9287                add_filter( 'widget_text', array( $this, 'filter_widget_text' ), 5, 3 );
    9388
    94                 // Test with filter=false.
     89                // Test with filter=false, implicit legacy mode.
     90                $this->widget_text_content_args = null;
    9591                ob_start();
     92                $instance = array(
     93                        'title'  => 'Foo',
     94                        'text'   => $text,
     95                        'filter' => false,
     96                );
    9697                $widget->widget( $args, $instance );
    9798                $output = ob_get_clean();
    9899                $this->assertNotContains( '<p>', $output );
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    102103                $this->assertContains( '[filter:widget_text]', $output );
    103104                $this->assertNotContains( '[filter:widget_text_content]', $output );
    104105
    105                 // Test with filter=true.
    106                 $instance['filter'] = true;
     106                // Test with filter=true, implicit legacy mode.
     107                $this->widget_text_content_args = null;
     108                $instance = array(
     109                        'title'  => 'Foo',
     110                        'text'   => $text,
     111                        'filter' => true,
     112                );
    107113                ob_start();
    108114                $widget->widget( $args, $instance );
    109115                $output = ob_get_clean();
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    117123                $this->assertContains( '[filter:widget_text]', $output );
    118124                $this->assertNotContains( '[filter:widget_text_content]', $output );
    119125
    120                 // Test with filter=content, the upgraded widget.
    121                 $instance['filter'] = 'content';
     126                // Test with filter=content, the upgraded widget, in 4.8.0 only.
     127                $this->widget_text_content_args = null;
     128                $instance = array(
     129                        'title'  => 'Foo',
     130                        'text'   => $text,
     131                        'filter' => 'content',
     132                );
     133                $expected_instance = array_merge( $instance, array(
     134                        'filter' => true,
     135                        'visual' => true,
     136                ) );
     137                ob_start();
     138                $widget->widget( $args, $instance );
     139                $output = ob_get_clean();
     140                $this->assertContains( '<p>', $output );
     141                $this->assertContains( '<br />', $output );
     142                $this->assertCount( 3, $this->widget_text_args );
     143                $this->assertEquals( $expected_instance['text'], $this->widget_text_args[0] );
     144                $this->assertEquals( $expected_instance, $this->widget_text_args[1] );
     145                $this->assertEquals( $widget, $this->widget_text_args[2] );
     146                $this->assertCount( 3, $this->widget_text_content_args );
     147                $this->assertEquals( $expected_instance['text'] . '[filter:widget_text]', $this->widget_text_content_args[0] );
     148                $this->assertEquals( $expected_instance, $this->widget_text_content_args[1] );
     149                $this->assertEquals( $widget, $this->widget_text_content_args[2] );
     150                $this->assertContains( wpautop( $expected_instance['text'] . '[filter:widget_text][filter:widget_text_content]' ), $output );
     151
     152                // Test with filter=true&visual=true, the upgraded widget, in 4.8.1 and above.
     153                $this->widget_text_content_args = null;
     154                $instance = array(
     155                        'title'  => 'Foo',
     156                        'text'   => $text,
     157                        'filter' => true,
     158                        'visual' => true,
     159                );
     160                $expected_instance = $instance;
    122161                ob_start();
    123162                $widget->widget( $args, $instance );
    124163                $output = ob_get_clean();
    125164                $this->assertContains( '<p>', $output );
    126165                $this->assertContains( '<br />', $output );
    127166                $this->assertCount( 3, $this->widget_text_args );
    128                 $this->assertEquals( $instance['text'], $this->widget_text_args[0] );
    129                 $this->assertEquals( $instance, $this->widget_text_args[1] );
     167                $this->assertEquals( $expected_instance['text'], $this->widget_text_args[0] );
     168                $this->assertEquals( $expected_instance, $this->widget_text_args[1] );
    130169                $this->assertEquals( $widget, $this->widget_text_args[2] );
    131170                $this->assertCount( 3, $this->widget_text_content_args );
    132                 $this->assertEquals( $instance['text'] . '[filter:widget_text]', $this->widget_text_content_args[0] );
    133                 $this->assertEquals( $instance, $this->widget_text_content_args[1] );
     171                $this->assertEquals( $expected_instance['text'] . '[filter:widget_text]', $this->widget_text_content_args[0] );
     172                $this->assertEquals( $expected_instance, $this->widget_text_content_args[1] );
    134173                $this->assertEquals( $widget, $this->widget_text_content_args[2] );
    135                 $this->assertContains( wpautop( $instance['text'] . '[filter:widget_text][filter:widget_text_content]' ), $output );
     174                $this->assertContains( wpautop( $expected_instance['text'] . '[filter:widget_text][filter:widget_text_content]' ), $output );
     175
     176                // Test with filter=true&visual=true, the upgraded widget, in 4.8.1 and above.
     177                $this->widget_text_content_args = null;
     178                $instance = array(
     179                        'title'  => 'Foo',
     180                        'text'   => $text,
     181                        'filter' => true,
     182                        'visual' => false,
     183                );
     184                $expected_instance = $instance;
     185                ob_start();
     186                $widget->widget( $args, $instance );
     187                $output = ob_get_clean();
     188                $this->assertContains( '<p>', $output );
     189                $this->assertContains( '<br />', $output );
     190                $this->assertCount( 3, $this->widget_text_args );
     191                $this->assertEquals( $expected_instance['text'], $this->widget_text_args[0] );
     192                $this->assertEquals( $expected_instance, $this->widget_text_args[1] );
     193                $this->assertEquals( $widget, $this->widget_text_args[2] );
     194                $this->assertNull( $this->widget_text_content_args );
     195                $this->assertContains( wpautop( $expected_instance['text'] . '[filter:widget_text]' ), $output );
     196
     197                // Test with filter=false&visual=false, the upgraded widget, in 4.8.1 and above.
     198                $this->widget_text_content_args = null;
     199                $instance = array(
     200                        'title'  => 'Foo',
     201                        'text'   => $text,
     202                        'filter' => false,
     203                        'visual' => false,
     204                );
     205                $expected_instance = $instance;
     206                ob_start();
     207                $widget->widget( $args, $instance );
     208                $output = ob_get_clean();
     209                $this->assertNotContains( '<p>', $output );
     210                $this->assertNotContains( '<br />', $output );
     211                $this->assertCount( 3, $this->widget_text_args );
     212                $this->assertEquals( $expected_instance['text'], $this->widget_text_args[0] );
     213                $this->assertEquals( $expected_instance, $this->widget_text_args[1] );
     214                $this->assertEquals( $widget, $this->widget_text_args[2] );
     215                $this->assertNull( $this->widget_text_content_args );
     216                $this->assertContains( $expected_instance['text'] . '[filter:widget_text]', $output );
    136217        }
    137218
    138219        /**
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    250331                );
    251332
    252333                $instance = array_merge( $base_instance, array(
    253                         'legacy' => true,
     334                        'visual' => false,
    254335                ) );
    255                 $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when legacy prop is present.' );
     336                $this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when visual=false prop is present.' );
     337
     338                $instance = array_merge( $base_instance, array(
     339                        'visual' => true,
     340                ) );
     341                $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when visual=true prop is present.' );
    256342
    257343                $instance = array_merge( $base_instance, array(
    258344                        'filter' => 'content',
    259345                ) );
    260                 $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when filter is explicitly content.' );
     346                $this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when filter is explicitly content (in WP 4.8.0 only).' );
    261347
    262348                $instance = array_merge( $base_instance, array(
    263349                        'text' => '',
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    364450                        'title' => 'Title',
    365451                        'text' => 'Text',
    366452                        'filter' => false,
    367                         'legacy' => true,
     453                        'visual' => false,
    368454                );
    369455                $this->assertTrue( $widget->is_legacy_instance( $instance ) );
    370456                ob_start();
    371457                $widget->form( $instance );
    372458                $form = ob_get_clean();
    373                 $this->assertContains( 'class="legacy"', $form );
     459                $this->assertContains( 'class="visual" type="hidden" value=""', $form );
     460                $this->assertNotContains( 'class="visual" type="hidden" value="1"', $form );
    374461
    375462                $instance = array(
    376463                        'title' => 'Title',
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    381468                ob_start();
    382469                $widget->form( $instance );
    383470                $form = ob_get_clean();
    384                 $this->assertNotContains( 'class="legacy"', $form );
     471                $this->assertContains( 'class="visual" type="hidden" value="1"', $form );
     472                $this->assertNotContains( 'class="visual" type="hidden" value=""', $form );
     473
     474                $instance = array(
     475                        'title' => 'Title',
     476                        'text' => 'Text',
     477                        'filter' => true,
     478                );
     479                $this->assertFalse( $widget->is_legacy_instance( $instance ) );
     480                ob_start();
     481                $widget->form( $instance );
     482                $form = ob_get_clean();
     483                $this->assertContains( 'class="visual" type="hidden" value="1"', $form );
     484                $this->assertNotContains( 'class="visual" type="hidden" value=""', $form );
     485
     486                $instance = array(
     487                        'title' => 'Title',
     488                        'text' => 'Text',
     489                        'filter' => true,
     490                        'visual' => true,
     491                );
     492                $this->assertFalse( $widget->is_legacy_instance( $instance ) );
     493                ob_start();
     494                $widget->form( $instance );
     495                $form = ob_get_clean();
     496                $this->assertContains( 'class="visual" type="hidden" value="1"', $form );
     497                $this->assertNotContains( 'class="visual" type="hidden" value=""', $form );
    385498        }
    386499
    387500        /**
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    394507                $instance = array(
    395508                        'title' => "The\nTitle",
    396509                        'text'  => "The\n\nText",
    397                         'filter' => 'content',
     510                        'filter' => true,
     511                        'visual' => true,
    398512                );
    399513
    400514                wp_set_current_user( $this->factory()->user->create( array(
    401515                        'role' => 'administrator',
    402516                ) ) );
    403517
    404                 // Should return valid instance in legacy mode since filter=false and there are line breaks.
    405518                $expected = array(
    406519                        'title'  => sanitize_text_field( $instance['title'] ),
    407520                        'text'   => $instance['text'],
    408                         'filter' => 'content',
     521                        'filter' => true,
     522                        'visual' => true,
    409523                );
    410524                $result = $widget->update( $instance, array() );
    411525                $this->assertEquals( $expected, $result );
    412526                $this->assertTrue( ! empty( $expected['filter'] ), 'Expected filter prop to be truthy, to handle case where 4.8 is downgraded to 4.7.' );
    413527
    414                 // Make sure KSES is applying as expected.
    415528                add_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ), 10, 2 );
    416529                $this->assertTrue( current_user_can( 'unfiltered_html' ) );
    417530                $instance['text'] = '<script>alert( "Howdy!" );</script>';
    418531                $expected['text'] = $instance['text'];
    419532                $result = $widget->update( $instance, array() );
    420                 $this->assertEquals( $expected, $result );
     533                $this->assertEquals( $expected, $result, 'KSES should apply as expected.' );
    421534                remove_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ) );
    422535
    423536                add_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10, 2 );
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    425538                $instance['text'] = '<script>alert( "Howdy!" );</script>';
    426539                $expected['text'] = wp_kses_post( $instance['text'] );
    427540                $result = $widget->update( $instance, array() );
    428                 $this->assertEquals( $expected, $result );
     541                $this->assertEquals( $expected, $result, 'KSES should not apply since user can unfiltered_html.' );
    429542                remove_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10 );
    430543        }
    431544
    class Test_WP_Widget_Text extends WP_UnitTestCase { 
    437550        function test_update_legacy() {
    438551                $widget = new WP_Widget_Text();
    439552
    440                 // Updating a widget with explicit filter=true persists with legacy mode.
     553                // --
     554                $instance = array(
     555                        'title' => 'Legacy',
     556                        'text' => 'Text',
     557                        'filter' => false,
     558                );
     559                $result = $widget->update( $instance, array() );
     560                $this->assertEquals( $instance, $result, 'Updating a widget without visual prop and explicit filter=false leaves visual prop absent' );
     561
     562                // --
    441563                $instance = array(
    442564                        'title' => 'Legacy',
    443565                        'text' => 'Text',
    444566                        'filter' => true,
    445567                );
    446568                $result = $widget->update( $instance, array() );
     569                $this->assertEquals( $instance, $result, 'Updating a widget without visual prop and explicit filter=true leaves legacy prop absent.' );
     570
     571                // --
     572                $instance = array(
     573                        'title' => 'Legacy',
     574                        'text' => 'Text',
     575                        'visual' => true,
     576                );
     577                $old_instance = array_merge( $instance, array(
     578                        'filter' => false,
     579                ) );
    447580                $expected = array_merge( $instance, array(
    448                         'legacy' => true,
    449581                        'filter' => true,
    450582                ) );
    451                 $this->assertEquals( $expected, $result );
     583                $result = $widget->update( $instance, $old_instance );
     584                $this->assertEquals( $expected, $result, 'Updating a pre-existing widget with visual mode forces filter to be true.' );
    452585
    453                 // Updating a widget with explicit filter=false persists with legacy mode.
    454                 $instance['filter'] = false;
     586                // --
     587                $instance = array(
     588                        'title' => 'Legacy',
     589                        'text' => 'Text',
     590                        'filter' => true,
     591                );
     592                $old_instance = array_merge( $instance, array(
     593                        'visual' => true,
     594                ) );
     595                $result = $widget->update( $instance, $old_instance );
     596                $expected = array_merge( $instance, array(
     597                        'visual' => true,
     598                ) );
     599                $this->assertEquals( $expected, $result, 'Updating a pre-existing visual widget retains visual mode when updated.' );
     600
     601                // --
     602                $instance = array(
     603                        'title' => 'Legacy',
     604                        'text' => 'Text',
     605                );
     606                $old_instance = array_merge( $instance, array(
     607                        'visual' => true,
     608                ) );
     609                $result = $widget->update( $instance, $old_instance );
     610                $expected = array_merge( $instance, array(
     611                        'visual' => true,
     612                        'filter' => true,
     613                ) );
     614                $this->assertEquals( $expected, $result, 'Updating a pre-existing visual widget retains visual=true and supplies missing filter=true.' );
     615
     616                // --
     617                $instance = array(
     618                        'title' => 'Legacy',
     619                        'text' => 'Text',
     620                        'visual' => true,
     621                );
     622                $expected = array_merge( $instance, array(
     623                        'filter' => true,
     624                ) );
     625                $result = $widget->update( $instance, array() );
     626                $this->assertEquals( $expected, $result, 'Updating a widget with explicit visual=true and absent filter prop causes filter to be set to true.' );
     627
     628                // --
     629                $instance = array(
     630                        'title' => 'Legacy',
     631                        'text' => 'Text',
     632                        'visual' => false,
     633                );
    455634                $result = $widget->update( $instance, array() );
    456635                $expected = array_merge( $instance, array(
    457                         'legacy' => true,
    458636                        'filter' => false,
    459637                ) );
    460                 $this->assertEquals( $expected, $result );
     638                $this->assertEquals( $expected, $result, 'Updating a widget in legacy mode results in filter=false as if checkbox not checked.' );
     639
     640                // --
     641                $instance = array(
     642                        'title' => 'Title',
     643                        'text' => 'Text',
     644                        'filter' => false,
     645                );
     646                $old_instance = array_merge( $instance, array(
     647                        'visual' => false,
     648                        'filter' => true,
     649                ) );
     650                $result = $widget->update( $instance, $old_instance );
     651                $expected = array_merge( $instance, array(
     652                        'visual' => false,
     653                        'filter' => false,
     654                ) );
     655                $this->assertEquals( $expected, $result, 'Updating a widget that previously had legacy form results in filter allowed to be false.' );
    461656
    462                 // Updating a widget in legacy form results in filter=false when checkbox not checked.
    463                 $instance['filter'] = true;
     657                // --
     658                $instance = array(
     659                        'title' => 'Title',
     660                        'text' => 'Text',
     661                        'filter' => 'content',
     662                );
    464663                $result = $widget->update( $instance, array() );
    465664                $expected = array_merge( $instance, array(
    466                         'legacy' => true,
    467665                        'filter' => true,
     666                        'visual' => true,
    468667                ) );
    469                 $this->assertEquals( $expected, $result );
     668                $this->assertEquals( $expected, $result, 'Updating a widget that had \'content\' as its filter value persists non-legacy mode. This only existed in WP 4.8.0.' );
    470669
    471                 // Updating a widget that previously had legacy form results in filter persisting.
    472                 unset( $instance['legacy'] );
    473                 $instance['filter'] = true;
    474                 $result = $widget->update( $instance, array(
    475                         'legacy' => true,
     670                // --
     671                $instance = array(
     672                        'title' => 'Title',
     673                        'text' => 'Text',
     674                );
     675                $old_instance = array_merge( $instance, array(
     676                        'filter' => 'content',
    476677                ) );
     678                $result = $widget->update( $instance, $old_instance );
    477679                $expected = array_merge( $instance, array(
    478                         'legacy' => true,
     680                        'visual' => true,
    479681                        'filter' => true,
    480682                ) );
    481                 $this->assertEquals( $expected, $result );
     683                $this->assertEquals( $expected, $result, 'Updating a pre-existing widget with the filter=content prop in WP 4.8.0 upgrades to filter=true&visual=true.' );
     684
     685                // --
     686                $instance = array(
     687                        'title' => 'Title',
     688                        'text' => 'Text',
     689                        'filter' => 'content',
     690                );
     691                $result = $widget->update( $instance, array() );
     692                $expected = array_merge( $instance, array(
     693                        'filter' => true,
     694                        'visual' => true,
     695                ) );
     696                $this->assertEquals( $expected, $result, 'Updating a widget with filter=content (from WP 4.8.0) upgrades to filter=true&visual=true.' );
    482697        }
    483698
    484699        /**