WordPress.org

Make WordPress Core

Ticket #35243: 35243.diff

File 35243.diff, 23.5 KB (added by obenland, 6 months ago)

Add PHP unit tests

  • Gruntfile.js

     
    456456                                dest: BUILD_DIR, 
    457457                                ext: '.min.js', 
    458458                                src: [ 
    459                                         'wp-admin/js/*.js', 
     459                                        'wp-admin/js/**/*.js', 
    460460                                        'wp-includes/js/*.js', 
    461461                                        'wp-includes/js/mediaelement/wp-mediaelement.js', 
    462462                                        'wp-includes/js/mediaelement/wp-playlist.js', 
  • src/wp-admin/css/customize-widgets.css

     
    213213        display: block; 
    214214} 
    215215 
     216/* Text Widget */ 
     217.wp-customizer div.mce-inline-toolbar-grp, 
     218.wp-customizer div.mce-tooltip { 
     219        z-index: 500100 !important; 
     220} 
     221.wp-customizer .ui-autocomplete.wplink-autocomplete { 
     222        z-index: 500110; /* originally 100110, but z-index of .wp-full-overlay is 500000 */ 
     223} 
     224.wp-customizer #wp-link-backdrop { 
     225        z-index: 500100; /* originally 100100, but z-index of .wp-full-overlay is 500000 */ 
     226} 
     227.wp-customizer #wp-link-wrap { 
     228        z-index: 500105; /* originally 100105, but z-index of .wp-full-overlay is 500000 */ 
     229} 
     230 
    216231/** 
    217232 * Styles for new widget addition panel 
    218233 */ 
  • src/wp-admin/js/widgets/text-widgets.js

     
     1/* global tinymce, switchEditors */ 
     2/* eslint consistent-this: [ "error", "control" ] */ 
     3wp.textWidgets = ( function( $ ) { 
     4        'use strict'; 
     5 
     6        var component = {}; 
     7 
     8        /** 
     9         * Text widget control. 
     10         * 
     11         * @class TextWidgetControl 
     12         * @constructor 
     13         * @abstract 
     14         */ 
     15        component.TextWidgetControl = Backbone.View.extend({ 
     16 
     17                /** 
     18                 * View events. 
     19                 * 
     20                 * @type {Object} 
     21                 */ 
     22                events: {}, 
     23 
     24                /** 
     25                 * Initialize. 
     26                 * 
     27                 * @param {Object}         options - Options. 
     28                 * @param {Backbone.Model} options.model - Model. 
     29                 * @param {jQuery}         options.el - Control container element. 
     30                 * @returns {void} 
     31                 */ 
     32                initialize: function initialize( options ) { 
     33                        var control = this; 
     34 
     35                        if ( ! options.el ) { 
     36                                throw new Error( 'Missing options.el' ); 
     37                        } 
     38 
     39                        Backbone.View.prototype.initialize.call( control, options ); 
     40 
     41                        /* 
     42                         * Create a container element for the widget control fields. 
     43                         * This is inserted into the DOM immediately before the the .widget-content 
     44                         * element because the contents of this element are essentially "managed" 
     45                         * by PHP, where each widget update cause the entire element to be emptied 
     46                         * and replaced with the rendered output of WP_Widget::form() which is 
     47                         * sent back in Ajax request made to save/update the widget instance. 
     48                         * To prevent a "flash of replaced DOM elements and re-initialized JS 
     49                         * components", the JS template is rendered outside of the normal form 
     50                         * container. 
     51                         */ 
     52                        control.fieldContainer = $( '<div class="text-widget-fields"></div>' ); 
     53                        control.fieldContainer.html( wp.template( 'widget-text-control-fields' ) ); 
     54                        control.widgetContentContainer = control.$el.find( '.widget-content:first' ); 
     55                        control.widgetContentContainer.before( control.fieldContainer ); 
     56 
     57                        control.fields = { 
     58                                title: control.fieldContainer.find( '.title' ), 
     59                                text: control.fieldContainer.find( '.text' ) 
     60                        }; 
     61 
     62                        // Sync input fields to hidden sync fields which actually get sent to the server. 
     63                        _.each( control.fields, function( fieldInput, fieldName ) { 
     64                                fieldInput.on( 'input change', function updateSyncField() { 
     65                                        var syncInput = control.widgetContentContainer.find( 'input[type=hidden].' + fieldName ); 
     66                                        if ( syncInput.val() !== $( this ).val() ) { 
     67                                                syncInput.val( $( this ).val() ); 
     68                                                syncInput.trigger( 'change' ); 
     69                                        } 
     70                                }); 
     71 
     72                                // Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event. 
     73                                fieldInput.val( control.widgetContentContainer.find( 'input[type=hidden].' + fieldName ).val() ); 
     74                        }); 
     75                }, 
     76 
     77                /** 
     78                 * Update input fields from the sync fields. 
     79                 * 
     80                 * This function is called at the widget-updated and widget-synced events. 
     81                 * A field will only be updated if it is not currently focused, to avoid 
     82                 * overwriting content that the user is entering. 
     83                 * 
     84                 * @returns {void} 
     85                 */ 
     86                updateFields: function updateFields() { 
     87                        var control = this, syncInput; 
     88 
     89                        if ( ! control.fields.title.is( document.activeElement ) ) { 
     90                                syncInput = control.widgetContentContainer.find( 'input[type=hidden].title' ); 
     91                                control.fields.title.val( syncInput.val() ); 
     92                        } 
     93 
     94                        syncInput = control.widgetContentContainer.find( 'input[type=hidden].text' ); 
     95                        if ( control.fields.text.is( ':visible' ) ) { 
     96                                if ( ! control.fields.text.is( document.activeElement ) ) { 
     97                                        control.fields.text.val( syncInput.val() ); 
     98                                } 
     99                        } else if ( control.editor && ! control.editorFocused && syncInput.val() !== control.fields.text.val() ) { 
     100                                control.editor.setContent( wp.editor.autop( syncInput.val() ) ); 
     101                        } 
     102                }, 
     103 
     104                /** 
     105                 * Initialize editor. 
     106                 * 
     107                 * @returns {void} 
     108                 */ 
     109                initializeEditor: function initializeEditor() { 
     110                        var control = this, changeDebounceDelay = 1000, iframeKeepAliveInterval = 1000, id, textarea, restoreTextMode = false; 
     111                        textarea = control.fields.text; 
     112                        id = textarea.attr( 'id' ); 
     113 
     114                        /** 
     115                         * Build (or re-build) the visual editor. 
     116                         * 
     117                         * @returns {void} 
     118                         */ 
     119                        function buildEditor() { 
     120                                var editor, triggerChangeIfDirty, onInit; 
     121 
     122                                // Destroy any existing editor so that it can be re-initialized after a widget-updated event. 
     123                                if ( tinymce.get( id ) )    { 
     124                                        restoreTextMode = tinymce.get( id ).isHidden(); 
     125                                        wp.editor.remove( id ); 
     126                                } 
     127 
     128                                wp.editor.initialize( id, { 
     129                                        tinymce: { 
     130                                                wpautop: true 
     131                                        }, 
     132                                        quicktags: true 
     133                                } ); 
     134 
     135                                editor = window.tinymce.get( id ); 
     136                                if ( ! editor ) { 
     137                                        throw new Error( 'Failed to initialize editor' ); 
     138                                } 
     139                                onInit = function() { 
     140                                        watchForDestroyedBody( control.$el.find( 'iframe' )[0] ); 
     141 
     142                                        // If a prior mce instance was replaced, and it was in text mode, toggle to text mode. 
     143                                        if ( restoreTextMode ) { 
     144                                                switchEditors.go( id, 'toggle' ); 
     145                                        } 
     146                                }; 
     147 
     148                                if ( editor.initialized ) { 
     149                                        onInit(); 
     150                                } else { 
     151                                        editor.on( 'init', onInit ); 
     152                                } 
     153 
     154                                control.editorFocused = false; 
     155                                triggerChangeIfDirty = function() { 
     156                                        if ( editor.isDirty() ) { 
     157                                                editor.save(); 
     158                                                textarea.trigger( 'change' ); 
     159                                        } 
     160                                }; 
     161                                editor.on( 'focus', function() { 
     162                                        control.editorFocused = true; 
     163                                } ); 
     164                                editor.on( 'NodeChange', _.debounce( triggerChangeIfDirty, changeDebounceDelay ) ); 
     165                                editor.on( 'blur', function() { 
     166                                        control.editorFocused = false; 
     167                                        triggerChangeIfDirty(); 
     168                                } ); 
     169 
     170                                control.editor = editor; 
     171                        } 
     172 
     173                        /** 
     174                         * Watch an iframe for the destruction of its TinyMCE contenteditable contents. 
     175                         * 
     176                         * @todo There may be a better way to listen for an iframe being destroyed. 
     177                         * @param {HTMLIFrameElement} iframe - TinyMCE iframe. 
     178                         * @returns {void} 
     179                         */ 
     180                        function watchForDestroyedBody( iframe ) { 
     181                                var timeoutId = setInterval( function() { 
     182                                        if ( ! iframe.contentWindow || iframe.contentWindow.document.body.id ) { 
     183                                                return; 
     184                                        } 
     185                                        clearInterval( timeoutId ); 
     186                                        buildEditor(); 
     187                                }, iframeKeepAliveInterval ); 
     188                        } 
     189 
     190                        buildEditor(); 
     191                } 
     192        }); 
     193 
     194        /** 
     195         * Mapping of widget ID to instances of TextWidgetControl subclasses. 
     196         * 
     197         * @type {Object.<string, wp.textWidgets.TextWidgetControl>} 
     198         */ 
     199        component.widgetControls = {}; 
     200 
     201        /** 
     202         * Handle widget being added or initialized for the first time at the widget-added event. 
     203         * 
     204         * @param {jQuery.Event} event - Event. 
     205         * @param {jQuery}       widgetContainer - Widget container element. 
     206         * @returns {void} 
     207         */ 
     208        component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) { 
     209                var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, widgetInside, renderWhenAnimationDone; 
     210                widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen. 
     211 
     212                idBase = widgetForm.find( '> .id_base' ).val(); 
     213                if ( 'text' !== idBase ) { 
     214                        return; 
     215                } 
     216 
     217                // Prevent initializing already-added widgets. 
     218                widgetId = widgetForm.find( '> .widget-id' ).val(); 
     219                if ( component.widgetControls[ widgetId ] ) { 
     220                        return; 
     221                } 
     222 
     223                widgetControl = new component.TextWidgetControl({ 
     224                        el: widgetContainer 
     225                }); 
     226 
     227                component.widgetControls[ widgetId ] = widgetControl; 
     228 
     229                /* 
     230                 * Render the widget once the widget parent's container finishes animating, 
     231                 * as the widget-added event fires with a slideDown of the container. 
     232                 * This ensures that the textarea is visible and an iframe can be embedded 
     233                 * with TinyMCE being able to set contenteditable on it. 
     234                 */ 
     235                widgetInside = widgetContainer.parent(); 
     236                renderWhenAnimationDone = function() { 
     237                        if ( widgetInside.is( ':animated' ) ) { 
     238                                setTimeout( renderWhenAnimationDone, animatedCheckDelay ); 
     239                        } else { 
     240                                widgetControl.initializeEditor(); 
     241                        } 
     242                }; 
     243                renderWhenAnimationDone(); 
     244        }; 
     245 
     246        /** 
     247         * Sync widget instance data sanitized from server back onto widget model. 
     248         * 
     249         * This gets called via the 'widget-updated' event when saving a widget from 
     250         * the widgets admin screen and also via the 'widget-synced' event when making 
     251         * a change to a widget in the customizer. 
     252         * 
     253         * @param {jQuery.Event} event - Event. 
     254         * @param {jQuery}       widgetContainer - Widget container element. 
     255         * @returns {void} 
     256         */ 
     257        component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) { 
     258                var widgetForm, widgetId, widgetControl, idBase; 
     259                widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); 
     260 
     261                idBase = widgetForm.find( '> .id_base' ).val(); 
     262                if ( 'text' !== idBase ) { 
     263                        return; 
     264                } 
     265 
     266                widgetId = widgetForm.find( '> .widget-id' ).val(); 
     267                widgetControl = component.widgetControls[ widgetId ]; 
     268                if ( ! widgetControl ) { 
     269                        return; 
     270                } 
     271 
     272                widgetControl.updateFields(); 
     273        }; 
     274 
     275        /** 
     276         * Initialize functionality. 
     277         * 
     278         * This function exists to prevent the JS file from having to boot itself. 
     279         * When WordPress enqueues this script, it should have an inline script 
     280         * attached which calls wp.textWidgets.init(). 
     281         * 
     282         * @returns {void} 
     283         */ 
     284        component.init = function init() { 
     285                var $document = $( document ); 
     286                $document.on( 'widget-added', component.handleWidgetAdded ); 
     287                $document.on( 'widget-synced widget-updated', component.handleWidgetUpdated ); 
     288 
     289                /* 
     290                 * Manually trigger widget-added events for media widgets on the admin 
     291                 * screen once they are expanded. The widget-added event is not triggered 
     292                 * for each pre-existing widget on the widgets admin screen like it is 
     293                 * on the customizer. Likewise, the customizer only triggers widget-added 
     294                 * when the widget is expanded to just-in-time construct the widget form 
     295                 * when it is actually going to be displayed. So the following implements 
     296                 * the same for the widgets admin screen, to invoke the widget-added 
     297                 * handler when a pre-existing media widget is expanded. 
     298                 */ 
     299                $( function initializeExistingWidgetContainers() { 
     300                        var widgetContainers; 
     301                        if ( 'widgets' !== window.pagenow ) { 
     302                                return; 
     303                        } 
     304                        widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' ); 
     305                        widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() { 
     306                                var widgetContainer = $( this ); 
     307                                component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer ); 
     308                        }); 
     309                }); 
     310        }; 
     311 
     312        return component; 
     313})( jQuery ); 
  • src/wp-content/themes/twentyten/style.css

     
    841841        padding: 4px; 
    842842        text-align: center; 
    843843} 
     844.widget-container .wp-caption { 
     845        max-width: 100% !important; 
     846} 
    844847.wp-caption img { 
    845848        margin: 5px 5px 0; 
    846849        max-width: 622px; /* caption width - 10px */ 
  • src/wp-includes/default-filters.php

     
    164164 
    165165add_filter( 'wp_sprintf', 'wp_sprintf_l', 10, 2 ); 
    166166 
    167 add_filter( 'widget_text', 'balanceTags' ); 
     167add_filter( 'widget_text',         'balanceTags'          ); 
     168add_filter( 'widget_text_content', 'capital_P_dangit', 11 ); 
     169add_filter( 'widget_text_content', 'wptexturize'          ); 
     170add_filter( 'widget_text_content', 'convert_smilies',  20 ); 
     171add_filter( 'widget_text_content', 'wpautop'              ); 
    168172 
    169173add_filter( 'date_i18n', 'wp_maybe_decline_date' ); 
    170174 
  • src/wp-includes/script-loader.php

     
    602602                $scripts->add( 'admin-gallery', "/wp-admin/js/gallery$suffix.js", array( 'jquery-ui-sortable' ) ); 
    603603 
    604604                $scripts->add( 'admin-widgets', "/wp-admin/js/widgets$suffix.js", array( 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable' ), false, 1 ); 
     605                $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util' ) ); 
     606                $scripts->add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' ); 
    605607 
    606608                $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y' ), false, 1 ); 
    607609 
  • src/wp-includes/widgets/class-wp-widget-text.php

     
    2828                        'description' => __( 'Arbitrary text or HTML.' ), 
    2929                        'customize_selective_refresh' => true, 
    3030                ); 
    31                 $control_ops = array( 'width' => 400, 'height' => 350 ); 
     31                $control_ops = array( 
     32                        'width' => 400, 
     33                        'height' => 350, 
     34                ); 
    3235                parent::__construct( 'text', __( 'Text' ), $widget_ops, $control_ops ); 
    3336        } 
    3437 
    3538        /** 
     39         * Add hooks for enqueueing assets when registering all widget instances of this widget class. 
     40         * 
     41         * @since 4.8.0 
     42         * @access public 
     43         */ 
     44        public function _register() { 
     45 
     46                // Note that the widgets component in the customizer will also do the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts(). 
     47                add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) ); 
     48 
     49                // Note that the widgets component in the customizer will also do the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts(). 
     50                add_action( 'admin_footer-widgets.php', array( $this, 'render_control_template_scripts' ) ); 
     51 
     52                parent::_register(); 
     53        } 
     54 
     55        /** 
    3656         * Outputs the content for the current Text widget instance. 
    3757         * 
    3858         * @since 2.8.0 
     
    6181                 */ 
    6282                $text = apply_filters( 'widget_text', $widget_text, $instance, $this ); 
    6383 
     84                if ( isset( $instance['filter'] ) ) { 
     85                        if ( 'content' === $instance['filter'] ) { 
     86 
     87                                /** 
     88                                 * Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor. 
     89                                 * 
     90                                 * By default a subset of the_content filters are applied, including wpautop and wptexturize. 
     91                                 * 
     92                                 * @since 4.8.0 
     93                                 * 
     94                                 * @param string         $widget_text The widget content. 
     95                                 * @param array          $instance    Array of settings for the current widget. 
     96                                 * @param WP_Widget_Text $this        Current Text widget instance. 
     97                                 */ 
     98                                $text = apply_filters( 'widget_text_content', $widget_text, $instance, $this ); 
     99 
     100                        } elseif ( $instance['filter'] ) { 
     101                                $text = wpautop( $text ); // Back-compat for instances prior to 4.8. 
     102                        } 
     103                } 
     104 
    64105                echo $args['before_widget']; 
    65106                if ( ! empty( $title ) ) { 
    66107                        echo $args['before_title'] . $title . $args['after_title']; 
    67                 } ?> 
    68                         <div class="textwidget"><?php echo !empty( $instance['filter'] ) ? wpautop( $text ) : $text; ?></div> 
     108                } 
     109 
     110                ?> 
     111                        <div class="textwidget"><?php echo $text; ?></div> 
    69112                <?php 
    70113                echo $args['after_widget']; 
    71114        } 
     
    89132                } else { 
    90133                        $instance['text'] = wp_kses_post( $new_instance['text'] ); 
    91134                } 
    92                 $instance['filter'] = ! empty( $new_instance['filter'] ); 
     135 
     136                /* 
     137                 * Re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply. 
     138                 * Prior to 4.8, this is a boolean value used to indicate whether or not wpautop should be 
     139                 * applied. By re-using this property, downgrading WordPress from 4.8 to 4.7 will ensure 
     140                 * that the content for Text widgets created with TinyMCE will continue to get wpautop. 
     141                 */ 
     142                $instance['filter'] = 'content'; 
     143 
    93144                return $instance; 
    94145        } 
    95146 
    96147        /** 
     148         * Loads the required scripts and styles for the widget control. 
     149         * 
     150         * @since 4.8.0 
     151         * @access public 
     152         */ 
     153        public function enqueue_admin_scripts() { 
     154                wp_enqueue_editor(); 
     155                wp_enqueue_script( 'text-widgets' ); 
     156        } 
     157 
     158        /** 
    97159         * Outputs the Text widget settings form. 
    98160         * 
    99161         * @since 2.8.0 
     162         * @since 4.8.0 Form only contains hidden inputs which are synced with JS template. 
    100163         * @access public 
     164         * @see WP_Widget_Visual_Text::render_control_template_scripts() 
    101165         * 
    102166         * @param array $instance Current settings. 
     167         * @return void 
    103168         */ 
    104169        public function form( $instance ) { 
    105                 $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'text' => '' ) ); 
    106                 $filter = isset( $instance['filter'] ) ? $instance['filter'] : 0; 
    107                 $title = sanitize_text_field( $instance['title'] ); 
     170                $instance = wp_parse_args( 
     171                        (array) $instance, 
     172                        array( 
     173                                'title' => '', 
     174                                'text' => '', 
     175                        ) 
     176                ); 
    108177                ?> 
    109                 <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> 
    110                 <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($title); ?>" /></p> 
    111  
    112                 <p><label for="<?php echo $this->get_field_id( 'text' ); ?>"><?php _e( 'Content:' ); ?></label> 
    113                 <textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id('text'); ?>" name="<?php echo $this->get_field_name('text'); ?>"><?php echo esc_textarea( $instance['text'] ); ?></textarea></p> 
     178                <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'] ); ?>"> 
     179                <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'] ); ?>"> 
     180                <?php 
     181        } 
    114182 
    115                 <p><input id="<?php echo $this->get_field_id('filter'); ?>" name="<?php echo $this->get_field_name('filter'); ?>" type="checkbox"<?php checked( $filter ); ?> />&nbsp;<label for="<?php echo $this->get_field_id('filter'); ?>"><?php _e('Automatically add paragraphs'); ?></label></p> 
     183        /** 
     184         * Render form template scripts. 
     185         * 
     186         * @since 4.8.0 
     187         * @access public 
     188         */ 
     189        public function render_control_template_scripts() { 
     190                ?> 
     191                <script type="text/html" id="tmpl-widget-text-control-fields"> 
     192                        <# var elementIdPrefix = 'el' + String( Math.random() ).replace( /\D/g, '' ) + '_' #> 
     193                        <p> 
     194                                <label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:' ); ?></label> 
     195                                <input id="{{ elementIdPrefix }}title" type="text" class="widefat title"> 
     196                        </p> 
     197                        <p> 
     198                                <label for="{{ elementIdPrefix }}text" class="screen-reader-text"><?php esc_html_e( 'Content:' ); ?></label> 
     199                                <textarea id="{{ elementIdPrefix }}text" class="widefat text" style="height: 200px" rows="16" cols="20"></textarea> 
     200                        </p> 
     201                </script> 
    116202                <?php 
    117203        } 
    118204} 
  • tests/phpunit/tests/widgets/test-class-wp-widget-text.php

     
     1<?php 
     2/** 
     3 * Unit tests covering WP_Widget_Text functionality. 
     4 * 
     5 * @package    WordPress 
     6 * @subpackage widgets 
     7 */ 
     8 
     9/** 
     10 * Test wp-includes/widgets/class-wp-widget-text.php 
     11 * 
     12 * @group widgets 
     13 */ 
     14class Test_WP_Widget_Text extends WP_UnitTestCase { 
     15 
     16        /** 
     17         * Test enqueue_admin_scripts method. 
     18         * 
     19         * @covers WP_Widget_Text::_register 
     20         */ 
     21        function test__register() { 
     22                set_current_screen( 'widgets.php' ); 
     23                $widget = new WP_Widget_Text(); 
     24                $widget->_register(); 
     25 
     26                $this->assertEquals( 10, has_action( 'admin_print_scripts-widgets.php', array( $widget, 'enqueue_admin_scripts' ) ) ); 
     27                $this->assertEquals( 10, has_action( 'admin_footer-widgets.php', array( $widget, 'render_control_template_scripts' ) ) ); 
     28        } 
     29 
     30        /** 
     31         * Test widget method. 
     32         * 
     33         * @covers WP_Widget_Text::widget 
     34         */ 
     35        function test_widget() { 
     36                $widget = new WP_Widget_Text(); 
     37                $text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n Praesent ut turpis consequat lorem volutpat bibendum vitae vitae ante."; 
     38 
     39                $args = array( 
     40                        'before_title'  => '<h2>', 
     41                        'after_title'   => "</h2>\n", 
     42                        'before_widget' => '<section>', 
     43                        'after_widget'  => "</section>\n", 
     44                ); 
     45                $instance = array( 
     46                        'title'  => 'Foo', 
     47                        'text'   => $text, 
     48                        'filter' => false, 
     49                ); 
     50 
     51                ob_start(); 
     52                $widget->widget( $args, $instance ); 
     53                $output = ob_get_clean(); 
     54                $this->assertNotContains( '<p>', $output ); 
     55                $this->assertNotContains( '<br />', $output ); 
     56 
     57                $instance['filter'] = true; 
     58                ob_start(); 
     59                $widget->widget( $args, $instance ); 
     60                $output = ob_get_clean(); 
     61                $this->assertContains( '<p>', $output ); 
     62                $this->assertContains( '<br />', $output ); 
     63 
     64                $instance['filter'] = 'content'; 
     65                ob_start(); 
     66                $widget->widget( $args, $instance ); 
     67                $output = ob_get_clean(); 
     68                $this->assertContains( '<p>', $output ); 
     69                $this->assertContains( '<br />', $output ); 
     70        } 
     71 
     72        /** 
     73         * Test update method. 
     74         * 
     75         * @covers WP_Widget_Text::update 
     76         */ 
     77        function test_update() { 
     78                $widget = new WP_Widget_Text(); 
     79                $instance = array( 
     80                        'text'  => '', 
     81                        'title' => '', 
     82                ); 
     83 
     84                wp_set_current_user( $this->factory()->user->create( array( 
     85                        'role' => 'author', 
     86                ) ) ); 
     87 
     88                // Should return valid instance. 
     89                $expected = array( 
     90                        'text'   => '', 
     91                        'title'  => '', 
     92                        'filter' => 'content', 
     93                ); 
     94                $result = $widget->update( $expected, $instance ); 
     95                $this->assertSame( $result, $expected ); 
     96 
     97                // Back-compat with pre-4.8. 
     98                $this->assertTrue( ! empty( $expected['filter'] ) ); 
     99 
     100                wp_get_current_user()->add_cap( 'unfiltered_html' ); 
     101                $expected['text'] = '<script>alert( "Howdy!" );</script>'; 
     102                $result = $widget->update( $expected, $instance ); 
     103                $this->assertSame( $result, $expected ); 
     104        } 
     105 
     106        /** 
     107         * Test enqueue_admin_scripts method. 
     108         * 
     109         * @covers WP_Widget_Text::enqueue_admin_scripts 
     110         */ 
     111        function test_enqueue_admin_scripts() { 
     112                set_current_screen( 'widgets.php' ); 
     113                $widget = new WP_Widget_Text(); 
     114                $widget->enqueue_admin_scripts(); 
     115 
     116                $this->assertTrue( wp_script_is( 'text-widgets' ) ); 
     117        } 
     118 
     119        /** 
     120         * Test render_control_template_scripts method. 
     121         * 
     122         * @covers WP_Widget_Text::render_control_template_scripts 
     123         */ 
     124        function test_render_control_template_scripts() { 
     125                $widget = new WP_Widget_Text(); 
     126 
     127                ob_start(); 
     128                $widget->render_control_template_scripts(); 
     129                $output = ob_get_clean(); 
     130 
     131                $this->assertContains( '<script type="text/html" id="tmpl-widget-text-control-fields">', $output ); 
     132        } 
     133}