Ticket #36944: 36944.3.diff
File 36944.3.diff, 23.7 KB (added by , 8 years ago) |
---|
-
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..c5c41c2 100644
43 43 case 'postMessage': 44 44 return this.previewer.send( 'setting', [ this.id, this() ] ); 45 45 } 46 }, 47 48 /** 49 * Find controls associated with this setting. 50 * 51 * @since 4.6.0 52 * @returns {wp.customize.Control[]} Controls associated with setting. 53 */ 54 findControls: function() { 55 var setting = this, controls = []; 56 api.control.each( function( control ) { 57 _.each( control.settings, function( controlSetting ) { 58 if ( controlSetting.id === setting.id ) { 59 controls.push( control ); 60 } 61 } ); 62 } ); 63 return controls; 46 64 } 47 65 }); 48 66 … … 1543 1561 1544 1562 control.setting = control.settings['default'] || null; 1545 1563 1564 // Add setting notifications to the control notification. 1546 1565 _.each( control.settings, function( setting ) { 1547 1566 setting.notifications.bind( 'add', function( settingNotification ) { 1548 var controlNotification = new api.Notification( setting.id + ':' + settingNotification.code, settingNotification ); 1567 var controlNotification, code, params; 1568 code = setting.id + ':' + settingNotification.code; 1569 params = _.extend( 1570 {}, 1571 settingNotification, 1572 { 1573 setting: setting.id 1574 } 1575 ); 1576 controlNotification = new api.Notification( code, params ); 1549 1577 control.notifications.add( controlNotification.code, controlNotification ); 1550 1578 } ); 1551 1579 setting.notifications.bind( 'remove', function( settingNotification ) { … … 2908 2936 } 2909 2937 } ); 2910 2938 } ); 2939 2940 if ( data.settingValidities ) { 2941 api._handleSettingValidities( { 2942 settingValidities: data.settingValidities, 2943 focusInvalidControl: false 2944 } ); 2945 } 2911 2946 } ); 2912 2947 2913 2948 this.request = $.ajax( this.previewUrl(), { … … 3430 3465 }; 3431 3466 }, 3432 3467 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.03439 * @private3440 *3441 * @param {object} response3442 * @param {object} response.invalid_settings3443 * @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 3489 3468 save: function() { 3490 3469 var self = this, 3491 3470 processing = api.state( 'processing' ), 3492 3471 submitWhenDoneProcessing, 3493 3472 submit, 3494 modifiedWhileSaving = {}; 3473 modifiedWhileSaving = {}, 3474 invalidSettings = [], 3475 invalidControls; 3495 3476 3496 3477 body.addClass( 'saving' ); 3497 3478 … … 3502 3483 3503 3484 submit = function () { 3504 3485 var request, query; 3486 3487 /* 3488 * Block saving if there are any settings that are marked as 3489 * invalid from the client (not from the server). Focus on 3490 * the control. 3491 */ 3492 api.each( function( setting ) { 3493 setting.notifications.each( function( notification ) { 3494 if ( 'error' === notification.type && ( ! notification.data || ! notification.data.from_server ) ) { 3495 invalidSettings.push( setting.id ); 3496 } 3497 } ); 3498 } ); 3499 invalidControls = api.findControlsForSettings( invalidSettings ); 3500 if ( ! _.isEmpty( invalidControls ) ) { 3501 _.values( invalidControls )[0][0].focus(); 3502 body.removeClass( 'saving' ); 3503 api.unbind( 'change', captureSettingModifiedDuringSave ); 3504 return; 3505 } 3506 3505 3507 query = $.extend( self.query(), { 3506 3508 nonce: self.nonce.save 3507 3509 } ); … … 3512 3514 3513 3515 api.trigger( 'save', request ); 3514 3516 3515 /*3516 * Remove all setting error notifications prior to save, allowing3517 * 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 3527 3517 request.always( function () { 3528 3518 body.removeClass( 'saving' ); 3529 3519 saveBtn.prop( 'disabled', false ); … … 3548 3538 } ); 3549 3539 } 3550 3540 3551 self._handleInvalidSettingsError( response ); 3541 if ( response.setting_validities ) { 3542 api._handleSettingValidities( { 3543 settingValidities: response.setting_validities, 3544 focusInvalidControl: true 3545 } ); 3546 } 3552 3547 3553 3548 api.trigger( 'error', response ); 3554 3549 } ); … … 3564 3559 3565 3560 api.previewer.send( 'saved', response ); 3566 3561 3562 if ( response.setting_validities ) { 3563 api._handleSettingValidities( { 3564 settingValidities: response.setting_validities, 3565 focusInvalidControl: true 3566 } ); 3567 } 3568 3567 3569 api.trigger( 'saved', response ); 3568 3570 3569 3571 // Restore the global dirty state if any settings were modified during save. … … 3670 3672 }); 3671 3673 3672 3674 /** 3675 * Handle setting_validities in an error response for the customize-save request. 3676 * 3677 * Add notifications to the settings and focus on the first control that has an invalid setting. 3678 * 3679 * @since 4.6.0 3680 * @private 3681 * 3682 * @param {object} args 3683 * @param {object} args.settingValidities 3684 * @param {boolean} [args.focusInvalidControl=false] 3685 * @returns {void} 3686 */ 3687 api._handleSettingValidities = function handleSettingValidities( args ) { 3688 var invalidSettingControls, invalidSettings = [], wasFocused = false; 3689 3690 // Find the controls that correspond to each invalid setting. 3691 _.each( args.settingValidities, function( validity, settingId ) { 3692 var setting = api( settingId ); 3693 if ( setting ) { 3694 3695 // Add notifications for invalidities. 3696 if ( _.isObject( validity ) ) { 3697 _.each( validity, function( params, code ) { 3698 var notification = new api.Notification( code, params ), existingNotification, needsReplacement = false; 3699 3700 // Remove existing notification if already exists for code but differs in parameters. 3701 existingNotification = setting.notifications( notification.code ); 3702 if ( existingNotification ) { 3703 needsReplacement = ( notification.type !== existingNotification.type ) || ! _.isEqual( notification.data, existingNotification.data ); 3704 } 3705 if ( needsReplacement ) { 3706 setting.notifications.remove( code ); 3707 } 3708 3709 if ( ! setting.notifications.has( notification.code ) ) { 3710 setting.notifications.add( code, notification ); 3711 } 3712 invalidSettings.push( setting.id ); 3713 } ); 3714 } 3715 3716 // Remove notification errors that are no longer valid. 3717 setting.notifications.each( function( notification ) { 3718 if ( 'error' === notification.type && ( true === validity || ! validity[ notification.code ] ) ) { 3719 setting.notifications.remove( notification.code ); 3720 } 3721 } ); 3722 } 3723 } ); 3724 3725 if ( args.focusInvalidControl ) { 3726 invalidSettingControls = api.findControlsForSettings( invalidSettings ); 3727 3728 // Focus on the first control that is inside of an expanded section (one that is visible). 3729 _( _.values( invalidSettingControls ) ).find( function( controls ) { 3730 return _( controls ).find( function( control ) { 3731 var isExpanded = control.section() && api.section.has( control.section() ) && api.section( control.section() ).expanded(); 3732 if ( isExpanded && control.expanded ) { 3733 isExpanded = control.expanded(); 3734 } 3735 if ( isExpanded ) { 3736 control.focus(); 3737 wasFocused = true; 3738 } 3739 return wasFocused; 3740 } ); 3741 } ); 3742 3743 // Focus on the first invalid control. 3744 if ( ! wasFocused && ! _.isEmpty( invalidSettingControls ) ) { 3745 _.values( invalidSettingControls )[0][0].focus(); 3746 } 3747 } 3748 }; 3749 3750 /** 3751 * Find all controls associated with the given settings. 3752 * 3753 * @since 4.6.0 3754 * @param {string[]} settingIds Setting IDs. 3755 * @returns {object<string, wp.customize.Control>} Mapping setting ids to arrays of controls. 3756 */ 3757 api.findControlsForSettings = function findControlsForSettings( settingIds ) { 3758 var controls = {}, settingControls; 3759 _.each( _.unique( settingIds ), function( settingId ) { 3760 var setting = api( settingId ); 3761 if ( setting ) { 3762 settingControls = setting.findControls(); 3763 if ( settingControls && settingControls.length > 0 ) { 3764 controls[ settingId ] = settingControls; 3765 } 3766 } 3767 } ); 3768 return controls; 3769 }; 3770 3771 /** 3673 3772 * Sort panels, sections, controls by priorities. Hide empty sections and panels. 3674 3773 * 3675 3774 * @since 4.1.0 … … 4040 4139 }); 4041 4140 }); 4042 4141 4142 // Update the setting validities. 4143 api.previewer.bind( 'selective-refresh-setting-validities', function handleSelectiveRefreshedSettingValidities( settingValidities ) { 4144 api._handleSettingValidities( { 4145 settingValidities: settingValidities, 4146 focusInvalidControl: false 4147 } ); 4148 } ); 4149 4043 4150 // Focus on the control that is associated with the given setting. 4044 4151 api.previewer.bind( 'focus-control-for-setting', function( settingId ) { 4045 4152 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 { 825 825 * @since 3.4.0 826 826 */ 827 827 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 828 831 $settings = array( 829 832 'theme' => array( 830 833 'stylesheet' => $this->get_stylesheet(), … … final class WP_Customize_Manager { 837 840 'activePanels' => array(), 838 841 'activeSections' => array(), 839 842 'activeControls' => array(), 843 'settingValidities' => $exported_setting_validities, 840 844 'nonce' => $this->get_nonces(), 841 845 'l10n' => array( 842 846 'shiftClickToEdit' => __( 'Shift-click to edit this element.' ), … … final class WP_Customize_Manager { 991 995 * @since 4.6.0 992 996 * @access public 993 997 * @see WP_REST_Request::has_valid_params() 998 * @see WP_Customize_Setting::validate() 994 999 * 995 1000 * @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`. 997 1002 */ 998 1003 public function validate_setting_values( $setting_values ) { 999 $validit y_errors = array();1004 $validities = array(); 1000 1005 foreach ( $setting_values as $setting_id => $unsanitized_value ) { 1001 1006 $setting = $this->get_setting( $setting_id ); 1002 1007 if ( ! $setting || is_null( $unsanitized_value ) ) { … … final class WP_Customize_Manager { 1006 1011 if ( false === $validity || null === $validity ) { 1007 1012 $validity = new WP_Error( 'invalid_value', __( 'Invalid value.' ) ); 1008 1013 } 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 ); 1011 1049 } 1050 return $notification; 1051 } else { 1052 return true; 1012 1053 } 1013 return $validity_errors;1014 1054 } 1015 1055 1016 1056 /** … … final class WP_Customize_Manager { 1041 1081 do_action( 'customize_save_validation_before', $this ); 1042 1082 1043 1083 // 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 ) { 1057 1088 $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 ) ), 1060 1091 ); 1061 1092 1062 1093 /** This filter is documented in wp-includes/class-wp-customize-manager.php */ … … final class WP_Customize_Manager { 1097 1128 */ 1098 1129 do_action( 'customize_save_after', $this ); 1099 1130 1131 $data = array( 1132 'setting_validities' => $exported_setting_validities, 1133 ); 1134 1100 1135 /** 1101 1136 * Filters response data for a successful customize_save AJAX request. 1102 1137 * … … final class WP_Customize_Manager { 1108 1143 * event on `wp.customize`. 1109 1144 * @param WP_Customize_Manager $this WP_Customize_Manager instance. 1110 1145 */ 1111 $response = apply_filters( 'customize_save_response', array(), $this );1146 $response = apply_filters( 'customize_save_response', $data, $this ); 1112 1147 wp_send_json_success( $response ); 1113 1148 } 1114 1149 -
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 { 402 402 $response['errors'] = $this->triggered_errors; 403 403 } 404 404 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 405 409 /** 406 410 * Filters the response from rendering the partials. 407 411 * -
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
172 172 api.preview.send( 'ready', { 173 173 activePanels: api.settings.activePanels, 174 174 activeSections: api.settings.activeSections, 175 activeControls: api.settings.activeControls 175 activeControls: api.settings.activeControls, 176 settingValidities: api.settings.settingValidities 176 177 } ); 177 178 178 179 // 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 ) { 847 847 } 848 848 } ); 849 849 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 850 862 api.preview.bind( 'active', function() { 851 863 852 864 // 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 { 196 196 * @see WP_Customize_Manager::validate_setting_values() 197 197 */ 198 198 function test_validate_setting_values() { 199 $default_value = 'foo_default';200 199 $setting = $this->manager->add_setting( 'foo', array( 201 200 'validate_callback' => array( $this, 'filter_customize_validate_foo' ), 202 201 'sanitize_callback' => array( $this, 'filter_customize_sanitize_foo' ), … … class Tests_WP_Customize_Manager extends WP_UnitTestCase { 204 203 205 204 $post_value = 'bar'; 206 205 $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 ); 208 209 209 210 $this->manager->set_post_value( 'foo', 'return_wp_error_in_sanitize' ); 210 211 $invalid_settings = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() ); … … class Tests_WP_Customize_Manager extends WP_UnitTestCase { 234 235 } 235 236 236 237 /** 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 /** 237 262 * Test WP_Customize_Manager::set_post_value(). 238 263 * 239 264 * @see WP_Customize_Manager::set_post_value() … … class Tests_WP_Customize_Manager extends WP_UnitTestCase { 565 590 $this->assertArrayHasKey( 'activePanels', $settings ); 566 591 $this->assertArrayHasKey( 'activeSections', $settings ); 567 592 $this->assertArrayHasKey( 'activeControls', $settings ); 593 $this->assertArrayHasKey( 'settingValidities', $settings ); 568 594 $this->assertArrayHasKey( 'nonce', $settings ); 569 595 $this->assertArrayHasKey( '_dirty', $settings ); 570 596 -
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 { 344 344 $this->assertEquals( $count_customize_render_partials_after + 1, has_action( 'customize_render_partials_after' ) ); 345 345 $output = json_decode( ob_get_clean(), true ); 346 346 $this->assertEquals( array( get_bloginfo( 'name', 'display' ) ), $output['data']['contents']['test_blogname'] ); 347 $this->assertArrayHasKey( 'setting_validities', $output['data'] ); 347 348 } 348 349 349 350 /** -
tests/qunit/wp-admin/js/customize-controls.js
diff --git tests/qunit/wp-admin/js/customize-controls.js tests/qunit/wp-admin/js/customize-controls.js index 23ca9ac..d806e46 100644
jQuery( window ).load( function (){ 96 96 ok( setting.notifications.extended( wp.customize.Values ) ); 97 97 equal( wp.customize.Notification, setting.notifications.prototype.constructor.defaultConstructor ); 98 98 } ); 99 test( 'Setting has findControls method', function() { 100 var controls, setting = wp.customize( 'fixture-setting' ); 101 equal( 'function', typeof setting.findControls ); 102 controls = setting.findControls(); 103 equal( 1, controls.length ); 104 equal( 'fixture-control', controls[0].id ); 105 } ); 99 106 test( 'Setting constructor object exists', function( assert ) { 100 107 assert.ok( _.isObject( wp.customize.settingConstructor ) ); 101 108 } ); … … jQuery( window ).load( function (){ 505 512 test( 'Panel instance is not contextuallyActive', function () { 506 513 equal( mockPanel.isContextuallyActive(), false ); 507 514 }); 515 516 module( 'Test wp.customize.findControlsForSettings' ); 517 test( 'findControlsForSettings(blogname)', function() { 518 var controlsForSettings, settingId = 'fixture-setting', controlId = 'fixture-control'; 519 ok( wp.customize.control.has( controlId ) ); 520 ok( wp.customize.has( settingId ) ); 521 controlsForSettings = wp.customize.findControlsForSettings( [ settingId ] ); 522 ok( _.isObject( controlsForSettings ), 'Response is object' ); 523 ok( _.isArray( controlsForSettings['fixture-setting'] ), 'Response has a fixture-setting array' ); 524 equal( 1, controlsForSettings['fixture-setting'].length ); 525 equal( wp.customize.control( controlId ), controlsForSettings['fixture-setting'][0] ); 526 } ); 508 527 });