Make WordPress Core

Ticket #36944: 36944.1.diff

File 36944.1.diff, 21.4 KB (added by westonruter, 9 years ago)

https://github.com/xwp/wordpress-develop/pull/151/commits/e39c8ce8937d5d4e17c3bc5e9054ad2e08ae240f

  • src/wp-admin/js/customize-controls.js

    diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
    index 2a5a5b9..1b3f9cd 100644
     
    15431543
    15441544                                        control.setting = control.settings['default'] || null;
    15451545
     1546                                        // Add setting notifications to the control notification.
    15461547                                        _.each( control.settings, function( setting ) {
    15471548                                                setting.notifications.bind( 'add', function( settingNotification ) {
    1548                                                         var controlNotification = new api.Notification( setting.id + ':' + settingNotification.code, settingNotification );
     1549                                                        var controlNotification, code, params;
     1550                                                        code = setting.id + ':' + settingNotification.code;
     1551                                                        params = _.extend(
     1552                                                                {},
     1553                                                                settingNotification,
     1554                                                                {
     1555                                                                        setting: setting.id
     1556                                                                }
     1557                                                        );
     1558                                                        controlNotification = new api.Notification( code, params );
    15491559                                                        control.notifications.add( controlNotification.code, controlNotification );
    15501560                                                } );
    15511561                                                setting.notifications.bind( 'remove', function( settingNotification ) {
     
    29082918                                                }
    29092919                                        } );
    29102920                                } );
     2921
     2922                                if ( data.settingValidities ) {
     2923                                        api._handleSettingValidities( {
     2924                                                settingValidities: data.settingValidities,
     2925                                                focusInvalidControl: false
     2926                                        } );
     2927                                }
    29112928                        } );
    29122929
    29132930                        this.request = $.ajax( this.previewUrl(), {
     
    34303447                                };
    34313448                        },
    34323449
    3433                         /**
    3434                          * Handle invalid_settings in an error response for the customize-save request.
    3435                          *
    3436                          * Add notifications to the settings and focus on the first control that has an invalid setting.
    3437                          *
    3438                          * @since 4.6.0
    3439                          * @private
    3440                          *
    3441                          * @param {object} response
    3442                          * @param {object} response.invalid_settings
    3443                          * @returns {void}
    3444                          */
    3445                         _handleInvalidSettingsError: function( response ) {
    3446                                 var invalidControls = [], wasFocused = false;
    3447                                 if ( _.isEmpty( response.invalid_settings ) ) {
    3448                                         return;
    3449                                 }
    3450 
    3451                                 // Find the controls that correspond to each invalid setting.
    3452                                 _.each( response.invalid_settings, function( notifications, settingId ) {
    3453                                         var setting = api( settingId );
    3454                                         if ( setting ) {
    3455                                                 _.each( notifications, function( notificationParams, code ) {
    3456                                                         var notification = new api.Notification( code, notificationParams );
    3457                                                         setting.notifications.add( code, notification );
    3458                                                 } );
    3459                                         }
    3460 
    3461                                         api.control.each( function( control ) {
    3462                                                 _.each( control.settings, function( controlSetting ) {
    3463                                                         if ( controlSetting.id === settingId ) {
    3464                                                                 invalidControls.push( control );
    3465                                                         }
    3466                                                 } );
    3467                                         } );
    3468                                 } );
    3469 
    3470                                 // Focus on the first control that is inside of an expanded section (one that is visible).
    3471                                 _( invalidControls ).find( function( control ) {
    3472                                         var isExpanded = control.section() && api.section.has( control.section() ) && api.section( control.section() ).expanded();
    3473                                         if ( isExpanded && control.expanded ) {
    3474                                                 isExpanded = control.expanded();
    3475                                         }
    3476                                         if ( isExpanded ) {
    3477                                                 control.focus();
    3478                                                 wasFocused = true;
    3479                                         }
    3480                                         return wasFocused;
    3481                                 } );
    3482 
    3483                                 // Focus on the first invalid control.
    3484                                 if ( ! wasFocused && invalidControls[0] ) {
    3485                                         invalidControls[0].focus();
    3486                                 }
    3487                         },
    3488 
    34893450                        save: function() {
    34903451                                var self = this,
    34913452                                        processing = api.state( 'processing' ),
    34923453                                        submitWhenDoneProcessing,
    34933454                                        submit,
    3494                                         modifiedWhileSaving = {};
     3455                                        modifiedWhileSaving = {},
     3456                                        invalidSettings = [],
     3457                                        invalidControls;
    34953458
    34963459                                body.addClass( 'saving' );
    34973460
     
    35023465
    35033466                                submit = function () {
    35043467                                        var request, query;
     3468
     3469                                        /*
     3470                                         * Block saving if there are any settings that are marked as
     3471                                         * invalid from the client (not from the server). Focus on
     3472                                         * the control.
     3473                                         */
     3474                                        api.each( function( setting ) {
     3475                                                setting.notifications.each( function( notification ) {
     3476                                                        if ( 'error' === notification.type && ( ! notification.data || ! notification.data.from_server ) ) {
     3477                                                                invalidSettings.push( setting.id );
     3478                                                        }
     3479                                                } );
     3480                                        } );
     3481                                        invalidControls = api.findControlsForSettings( invalidSettings );
     3482                                        if ( ! _.isEmpty( invalidControls ) ) {
     3483                                                _.values( invalidControls )[0][0].focus();
     3484                                                body.removeClass( 'saving' );
     3485                                                api.unbind( 'change', captureSettingModifiedDuringSave );
     3486                                                return;
     3487                                        }
     3488
    35053489                                        query = $.extend( self.query(), {
    35063490                                                nonce:  self.nonce.save
    35073491                                        } );
     
    35123496
    35133497                                        api.trigger( 'save', request );
    35143498
    3515                                         /*
    3516                                          * Remove all setting error notifications prior to save, allowing
    3517                                          * server to respond with fresh validation error notifications.
    3518                                          */
    3519                                         api.each( function( setting ) {
    3520                                                 setting.notifications.each( function( notification ) {
    3521                                                         if ( 'error' === notification.type ) {
    3522                                                                 setting.notifications.remove( notification.code );
    3523                                                         }
    3524                                                 } );
    3525                                         } );
    3526 
    35273499                                        request.always( function () {
    35283500                                                body.removeClass( 'saving' );
    35293501                                                saveBtn.prop( 'disabled', false );
     
    35483520                                                        } );
    35493521                                                }
    35503522
    3551                                                 self._handleInvalidSettingsError( response );
     3523                                                if ( response.setting_validities ) {
     3524                                                        api._handleSettingValidities( {
     3525                                                                settingValidities: response.setting_validities,
     3526                                                                focusInvalidControl: true
     3527                                                        } );
     3528                                                }
    35523529
    35533530                                                api.trigger( 'error', response );
    35543531                                        } );
     
    35643541
    35653542                                                api.previewer.send( 'saved', response );
    35663543
     3544                                                if ( response.setting_validities ) {
     3545                                                        api._handleSettingValidities( {
     3546                                                                settingValidities: response.setting_validities,
     3547                                                                focusInvalidControl: true
     3548                                                        } );
     3549                                                }
     3550
    35673551                                                api.trigger( 'saved', response );
    35683552
    35693553                                                // Restore the global dirty state if any settings were modified during save.
     
    36703654                });
    36713655
    36723656                /**
     3657                 * Handle setting_validities in an error response for the customize-save request.
     3658                 *
     3659                 * Add notifications to the settings and focus on the first control that has an invalid setting.
     3660                 *
     3661                 * @since 4.6.0
     3662                 * @private
     3663                 *
     3664                 * @param {object}  args
     3665                 * @param {object}  args.settingValidities
     3666                 * @param {boolean} [args.focusInvalidControl=false]
     3667                 * @returns {void}
     3668                 */
     3669                api._handleSettingValidities = function handleSettingValidities( args ) {
     3670                        var invalidSettingControls, invalidSettings = [], wasFocused = false;
     3671
     3672                        // Find the controls that correspond to each invalid setting.
     3673                        _.each( args.settingValidities, function( validity, settingId ) {
     3674                                var setting = api( settingId );
     3675                                if ( setting ) {
     3676
     3677                                        // Add notifications for invalidities.
     3678                                        if ( _.isObject( validity ) ) {
     3679                                                _.each( validity, function( params, code ) {
     3680                                                        var notification = new api.Notification( code, params ), existingNotification, needsReplacement = false;
     3681
     3682                                                        // Remove existing notification if already exists for code but differs in parameters.
     3683                                                        existingNotification = setting.notifications( notification.code );
     3684                                                        if ( existingNotification ) {
     3685                                                                needsReplacement = ( notification.type !== existingNotification.type ) || ! _.isEqual( notification.data, existingNotification.data );
     3686                                                        }
     3687                                                        if ( needsReplacement ) {
     3688                                                                setting.notifications.remove( code );
     3689                                                        }
     3690
     3691                                                        if ( ! setting.notifications.has( notification.code ) ) {
     3692                                                                setting.notifications.add( code, notification );
     3693                                                        }
     3694                                                        invalidSettings.push( setting.id );
     3695                                                } );
     3696                                        }
     3697
     3698                                        // Remove notification errors that are no longer valid.
     3699                                        setting.notifications.each( function( notification ) {
     3700                                                if ( 'error' === notification.type && ( true === validity || ! validity[ notification.code ] ) ) {
     3701                                                        setting.notifications.remove( notification.code );
     3702                                                }
     3703                                        } );
     3704                                }
     3705                        } );
     3706
     3707                        if ( args.focusInvalidControl ) {
     3708                                invalidSettingControls = api.findControlsForSettings( invalidSettings );
     3709
     3710                                // Focus on the first control that is inside of an expanded section (one that is visible).
     3711                                _( _.values( invalidSettingControls ) ).find( function( controls ) {
     3712                                        return _( controls ).find( function( control ) {
     3713                                                var isExpanded = control.section() && api.section.has( control.section() ) && api.section( control.section() ).expanded();
     3714                                                if ( isExpanded && control.expanded ) {
     3715                                                        isExpanded = control.expanded();
     3716                                                }
     3717                                                if ( isExpanded ) {
     3718                                                        control.focus();
     3719                                                        wasFocused = true;
     3720                                                }
     3721                                                return wasFocused;
     3722                                        } );
     3723                                } );
     3724
     3725                                // Focus on the first invalid control.
     3726                                if ( ! wasFocused && ! _.isEmpty( invalidSettingControls ) ) {
     3727                                        _.values( invalidSettingControls )[0][0].focus();
     3728                                }
     3729                        }
     3730                };
     3731
     3732                /**
     3733                 * Find all controls associated with the given settings.
     3734                 *
     3735                 * @since 4.6.0
     3736                 * @param {string[]} settingIds Setting IDs.
     3737                 * @returns {object<string, wp.customize.Control>} Mapping setting ids to arrays of controls.
     3738                 */
     3739                api.findControlsForSettings = function findControlsForSettings( settingIds ) {
     3740                        var controls = {};
     3741                        _.each( _.unique( settingIds ), function( settingId ) {
     3742                                api.control.each( function( control ) {
     3743                                        _.each( control.settings, function( controlSetting ) {
     3744                                                if ( controlSetting.id === settingId ) {
     3745                                                        if ( ! controls[ settingId ] ) {
     3746                                                                controls[ settingId ] = [];
     3747                                                        }
     3748                                                        controls[ settingId ].push( control );
     3749                                                }
     3750                                        } );
     3751                                } );
     3752                        } );
     3753                        return controls;
     3754                };
     3755
     3756                /**
    36733757                 * Sort panels, sections, controls by priorities. Hide empty sections and panels.
    36743758                 *
    36753759                 * @since 4.1.0
     
    40404124                        });
    40414125                });
    40424126
     4127                // Update the setting validities
     4128                api.previewer.bind( 'selective-refresh-setting-validities', function handleSelectiveRefreshedSettingValidities( settingValidities ) {
     4129                        api._handleSettingValidities( {
     4130                                settingValidities: settingValidities,
     4131                                focusInvalidControl: false
     4132                        } );
     4133                } );
     4134
    40434135                // Focus on the control that is associated with the given setting.
    40444136                api.previewer.bind( 'focus-control-for-setting', function( settingId ) {
    40454137                        var matchedControl;
  • src/wp-includes/class-wp-customize-manager.php

    diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
    index f89887e..63d9f1d 100644
    final class WP_Customize_Manager { 
    825825         * @since 3.4.0
    826826         */
    827827        public function customize_preview_settings() {
     828                $setting_validities = $this->validate_setting_values( $this->unsanitized_post_values() );
     829                $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities );
     830
    828831                $settings = array(
    829832                        'theme' => array(
    830833                                'stylesheet' => $this->get_stylesheet(),
    final class WP_Customize_Manager { 
    837840                        'activePanels' => array(),
    838841                        'activeSections' => array(),
    839842                        'activeControls' => array(),
     843                        'settingValidities' => $exported_setting_validities,
    840844                        'nonce' => $this->get_nonces(),
    841845                        'l10n' => array(
    842846                                'shiftClickToEdit' => __( 'Shift-click to edit this element.' ),
    final class WP_Customize_Manager { 
    991995         * @since 4.6.0
    992996         * @access public
    993997         * @see WP_REST_Request::has_valid_params()
     998         * @see WP_Customize_Setting::validate()
    994999         *
    9951000         * @param array $setting_values Mapping of setting IDs to values to sanitize and validate.
    996          * @return array Empty array if all settings were valid. One or more instances of `WP_Error` if any were invalid.
     1001         * @return array Mapping of setting IDs to return value of validate method calls, either `true` or `WP_Error`.
    9971002         */
    9981003        public function validate_setting_values( $setting_values ) {
    999                 $validity_errors = array();
     1004                $validities = array();
    10001005                foreach ( $setting_values as $setting_id => $unsanitized_value ) {
    10011006                        $setting = $this->get_setting( $setting_id );
    10021007                        if ( ! $setting || is_null( $unsanitized_value ) ) {
    final class WP_Customize_Manager { 
    10061011                        if ( false === $validity || null === $validity ) {
    10071012                                $validity = new WP_Error( 'invalid_value', __( 'Invalid value.' ) );
    10081013                        }
    1009                         if ( is_wp_error( $validity ) ) {
    1010                                 $validity_errors[ $setting_id ] = $validity;
     1014                        $validities[ $setting_id ] = $validity;
     1015                }
     1016                return $validities;
     1017        }
     1018
     1019        /**
     1020         * Prepare setting validity for exporting to the client (JS).
     1021         *
     1022         * Converts `WP_Error` instance into array suitable for passing into the
     1023         * `wp.customize.Notification` JS model.
     1024         *
     1025         * @since 4.6.0
     1026         * @access public
     1027         *
     1028         * @param true|WP_Error $validity Setting validity.
     1029         * @return true|array If `$validity` was `WP_Error` then array mapping the error
     1030         *                    codes to their respective `message` and `data` to pass
     1031         *                    into the `wp.customize.Notification` JS model.
     1032         */
     1033        public function prepare_setting_validity_for_js( $validity ) {
     1034                if ( is_wp_error( $validity ) ) {
     1035                        $notification = array();
     1036                        foreach ( $validity->errors as $error_code => $error_messages ) {
     1037                                $error_data = $validity->get_error_data( $error_code );
     1038                                if ( is_null( $error_data ) ) {
     1039                                        $error_data = array();
     1040                                }
     1041                                $error_data = array_merge(
     1042                                        $error_data,
     1043                                        array( 'from_server' => true )
     1044                                );
     1045                                $notification[ $error_code ] = array(
     1046                                        'message' => join( ' ', $error_messages ),
     1047                                        'data' => $error_data,
     1048                                );
    10111049                        }
     1050                        return $notification;
     1051                } else {
     1052                        return true;
    10121053                }
    1013                 return $validity_errors;
    10141054        }
    10151055
    10161056        /**
    final class WP_Customize_Manager { 
    10411081                do_action( 'customize_save_validation_before', $this );
    10421082
    10431083                // Validate settings.
    1044                 $validity_errors = $this->validate_setting_values( $this->unsanitized_post_values() );
    1045                 $invalid_count = count( $validity_errors );
    1046                 if ( $invalid_count > 0 ) {
    1047                         $settings_errors = array();
    1048                         foreach ( $validity_errors as $setting_id => $validity_error ) {
    1049                                 $settings_errors[ $setting_id ] = array();
    1050                                 foreach ( $validity_error->errors as $error_code => $error_messages ) {
    1051                                         $settings_errors[ $setting_id ][ $error_code ] = array(
    1052                                                 'message' => join( ' ', $error_messages ),
    1053                                                 'data' => $validity_error->get_error_data( $error_code ),
    1054                                         );
    1055                                 }
    1056                         }
     1084                $setting_validities = $this->validate_setting_values( $this->unsanitized_post_values() );
     1085                $invalid_setting_count = count( array_filter( $setting_validities, 'is_wp_error' ) );
     1086                $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities );
     1087                if ( $invalid_setting_count > 0 ) {
    10571088                        $response = array(
    1058                                 'invalid_settings' => $settings_errors,
    1059                                 'message' => sprintf( _n( 'There is %s invalid setting.', 'There are %s invalid settings.', $invalid_count ), number_format_i18n( $invalid_count ) ),
     1089                                'setting_validities' => $exported_setting_validities,
     1090                                'message' => sprintf( _n( 'There is %s invalid setting.', 'There are %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ),
    10601091                        );
    10611092
    10621093                        /** This filter is documented in wp-includes/class-wp-customize-manager.php */
    final class WP_Customize_Manager { 
    10971128                 */
    10981129                do_action( 'customize_save_after', $this );
    10991130
     1131                $data = array(
     1132                        'setting_validities' => $exported_setting_validities,
     1133                );
     1134
    11001135                /**
    11011136                 * Filters response data for a successful customize_save AJAX request.
    11021137                 *
    final class WP_Customize_Manager { 
    11081143                 *                                   event on `wp.customize`.
    11091144                 * @param WP_Customize_Manager $this WP_Customize_Manager instance.
    11101145                 */
    1111                 $response = apply_filters( 'customize_save_response', array(), $this );
     1146                $response = apply_filters( 'customize_save_response', $data, $this );
    11121147                wp_send_json_success( $response );
    11131148        }
    11141149
  • src/wp-includes/customize/class-wp-customize-selective-refresh.php

    diff --git src/wp-includes/customize/class-wp-customize-selective-refresh.php src/wp-includes/customize/class-wp-customize-selective-refresh.php
    index f90f0f9..245d32a 100644
    final class WP_Customize_Selective_Refresh { 
    402402                        $response['errors'] = $this->triggered_errors;
    403403                }
    404404
     405                $setting_validities = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() );
     406                $exported_setting_validities = array_map( array( $this->manager, 'prepare_setting_validity_for_js' ), $setting_validities );
     407                $response['setting_validities'] = $exported_setting_validities;
     408
    405409                /**
    406410                 * Filters the response from rendering the partials.
    407411                 *
  • src/wp-includes/js/customize-preview.js

    diff --git src/wp-includes/js/customize-preview.js src/wp-includes/js/customize-preview.js
    index ac77551..f5569ed 100644
     
    172172                api.preview.send( 'ready', {
    173173                        activePanels: api.settings.activePanels,
    174174                        activeSections: api.settings.activeSections,
    175                         activeControls: api.settings.activeControls
     175                        activeControls: api.settings.activeControls,
     176                        settingValidities: api.settings.settingValidities
    176177                } );
    177178
    178179                // Display a loading indicator when preview is reloading, and remove on failure.
  • src/wp-includes/js/customize-selective-refresh.js

    diff --git src/wp-includes/js/customize-selective-refresh.js src/wp-includes/js/customize-selective-refresh.js
    index 7efee3d..ec51058 100644
    wp.customize.selectiveRefresh = ( function( $, api ) { 
    847847                        }
    848848                } );
    849849
     850                /**
     851                 * Handle setting validities in partial refresh response.
     852                 *
     853                 * @param {object} data Response data.
     854                 * @param {object} data.setting_validities Setting validities.
     855                 */
     856                api.selectiveRefresh.bind( 'render-partials-response', function handleSettingValiditiesResponse( data ) {
     857                        if ( data.setting_validities ) {
     858                                api.preview.send( 'selective-refresh-setting-validities', data.setting_validities );
     859                        }
     860                } );
     861
    850862                api.preview.bind( 'active', function() {
    851863
    852864                        // Make all partials ready.
  • tests/phpunit/tests/customize/manager.php

    diff --git tests/phpunit/tests/customize/manager.php tests/phpunit/tests/customize/manager.php
    index 666db6f..437f410 100644
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    196196         * @see WP_Customize_Manager::validate_setting_values()
    197197         */
    198198        function test_validate_setting_values() {
    199                 $default_value = 'foo_default';
    200199                $setting = $this->manager->add_setting( 'foo', array(
    201200                        'validate_callback' => array( $this, 'filter_customize_validate_foo' ),
    202201                        'sanitize_callback' => array( $this, 'filter_customize_sanitize_foo' ),
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    204203
    205204                $post_value = 'bar';
    206205                $this->manager->set_post_value( 'foo', $post_value );
    207                 $this->assertEmpty( $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() ) );
     206                $validities = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() );
     207                $this->assertCount( 1, $validities );
     208                $this->assertEquals( array( 'foo' => true ), $validities );
    208209
    209210                $this->manager->set_post_value( 'foo', 'return_wp_error_in_sanitize' );
    210211                $invalid_settings = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() );
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    234235        }
    235236
    236237        /**
     238         * Test WP_Customize_Manager::prepare_setting_validity_for_js().
     239         *
     240         * @see WP_Customize_Manager::prepare_setting_validity_for_js()
     241         */
     242        function test_prepare_setting_validity_for_js() {
     243                $this->assertTrue( $this->manager->prepare_setting_validity_for_js( true ) );
     244                $error = new WP_Error();
     245                $error->add( 'bad_letter', 'Bad letter' );
     246                $error->add( 'bad_letter', 'Bad letra' );
     247                $error->add( 'bad_number', 'Bad number', array( 'number' => 123 ) );
     248                $validity = $this->manager->prepare_setting_validity_for_js( $error );
     249                $this->assertInternalType( 'array', $validity );
     250                foreach ( $error->errors as $code => $messages ) {
     251                        $this->assertArrayHasKey( $code, $validity );
     252                        $this->assertInternalType( 'array', $validity[ $code ] );
     253                        $this->assertEquals( join( ' ', $messages ), $validity[ $code ]['message'] );
     254                        $this->assertArrayHasKey( 'data', $validity[ $code ] );
     255                        $this->assertArrayHasKey( 'from_server', $validity[ $code ]['data'] );
     256                }
     257                $this->assertArrayHasKey( 'number', $validity['bad_number']['data'] );
     258                $this->assertEquals( 123, $validity['bad_number']['data']['number'] );
     259        }
     260
     261        /**
    237262         * Test WP_Customize_Manager::set_post_value().
    238263         *
    239264         * @see WP_Customize_Manager::set_post_value()
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    565590                $this->assertArrayHasKey( 'activePanels', $settings );
    566591                $this->assertArrayHasKey( 'activeSections', $settings );
    567592                $this->assertArrayHasKey( 'activeControls', $settings );
     593                $this->assertArrayHasKey( 'settingValidities', $settings );
    568594                $this->assertArrayHasKey( 'nonce', $settings );
    569595                $this->assertArrayHasKey( '_dirty', $settings );
    570596
  • tests/phpunit/tests/customize/selective-refresh-ajax.php

    diff --git tests/phpunit/tests/customize/selective-refresh-ajax.php tests/phpunit/tests/customize/selective-refresh-ajax.php
    index b60f23a..d2a9cdf 100644
    class Test_WP_Customize_Selective_Refresh_Ajax extends WP_UnitTestCase { 
    344344                $this->assertEquals( $count_customize_render_partials_after + 1, has_action( 'customize_render_partials_after' ) );
    345345                $output = json_decode( ob_get_clean(), true );
    346346                $this->assertEquals( array( get_bloginfo( 'name', 'display' ) ), $output['data']['contents']['test_blogname'] );
     347                $this->assertArrayHasKey( 'setting_validities', $output['data'] );
    347348        }
    348349
    349350        /**