From a3b66814a795fe81f165e7f02e5c70653f083c54 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 17 Nov 2016 08:17:57 +0000 Subject: [PATCH] Customize: Add unit tests for importing theme starter content. Props welcher, westonruter. See #38114, #38533, #38615. Fixes #38540. git-svn-id: https://develop.svn.wordpress.org/trunk@39276 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-customize-manager.php | 26 +-- src/wp-includes/theme.php | 2 +- tests/phpunit/tests/customize/manager.php | 146 ++++++++++++++ .../tests/theme/getThemeStarterContent.php | 179 ++++++++++++++++++ 4 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 tests/phpunit/tests/theme/getThemeStarterContent.php diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 016c1d4d53..d047620ca1 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -895,7 +895,7 @@ final class WP_Customize_Manager { * @access private * @var array */ - protected $starter_content_settings_ids = array(); + protected $pending_starter_content_settings_ids = array(); /** * Import theme starter content into the customized state. @@ -953,7 +953,7 @@ final class WP_Customize_Manager { $setting_value = $this->widgets->sanitize_widget_js_instance( $instance ); if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) { $this->set_post_value( $setting_id, $setting_value ); - $this->starter_content_settings_ids[] = $setting_id; + $this->pending_starter_content_settings_ids[] = $setting_id; } $sidebar_widget_ids[] = $widget_id; } @@ -961,7 +961,7 @@ final class WP_Customize_Manager { $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id ); if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) { $this->set_post_value( $setting_id, $sidebar_widget_ids ); - $this->starter_content_settings_ids[] = $setting_id; + $this->pending_starter_content_settings_ids[] = $setting_id; } } @@ -1015,7 +1015,7 @@ final class WP_Customize_Manager { if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) { $nav_menus_created_posts = array_unique( array_merge( $nav_menus_created_posts, wp_list_pluck( $posts, 'ID' ) ) ); $this->set_post_value( $setting_id, array_values( $nav_menus_created_posts ) ); - $this->starter_content_settings_ids[] = $setting_id; + $this->pending_starter_content_settings_ids[] = $setting_id; } } @@ -1056,7 +1056,7 @@ final class WP_Customize_Manager { $this->set_post_value( $nav_menu_setting_id, array( 'name' => isset( $nav_menu['name'] ) ? $nav_menu['name'] : $nav_menu_location, ) ); - $this->starter_content_settings_ids[] = $nav_menu_setting_id; + $this->pending_starter_content_settings_ids[] = $nav_menu_setting_id; // @todo Add support for menu_item_parent. $position = 0; @@ -1083,14 +1083,14 @@ final class WP_Customize_Manager { if ( empty( $changeset_data[ $nav_menu_item_setting_id ] ) || ! empty( $changeset_data[ $nav_menu_item_setting_id ]['starter_content'] ) ) { $this->set_post_value( $nav_menu_item_setting_id, $nav_menu_item ); - $this->starter_content_settings_ids[] = $nav_menu_item_setting_id; + $this->pending_starter_content_settings_ids[] = $nav_menu_item_setting_id; } } $setting_id = sprintf( 'nav_menu_locations[%s]', $nav_menu_location ); if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) { $this->set_post_value( $setting_id, $nav_menu_term_id ); - $this->starter_content_settings_ids[] = $setting_id; + $this->pending_starter_content_settings_ids[] = $setting_id; } } @@ -1102,7 +1102,7 @@ final class WP_Customize_Manager { if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) { $this->set_post_value( $name, $value ); - $this->starter_content_settings_ids[] = $name; + $this->pending_starter_content_settings_ids[] = $name; } } @@ -1114,11 +1114,11 @@ final class WP_Customize_Manager { if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) { $this->set_post_value( $name, $value ); - $this->starter_content_settings_ids[] = $name; + $this->pending_starter_content_settings_ids[] = $name; } } - if ( ! empty( $this->starter_content_settings_ids ) ) { + if ( ! empty( $this->pending_starter_content_settings_ids ) ) { if ( did_action( 'customize_register' ) ) { $this->_save_starter_content_changeset(); } else { @@ -1135,14 +1135,16 @@ final class WP_Customize_Manager { */ public function _save_starter_content_changeset() { - if ( empty( $this->starter_content_settings_ids ) ) { + if ( empty( $this->pending_starter_content_settings_ids ) ) { return; } $this->save_changeset_post( array( - 'data' => array_fill_keys( $this->starter_content_settings_ids, array( 'starter_content' => true ) ), + 'data' => array_fill_keys( $this->pending_starter_content_settings_ids, array( 'starter_content' => true ) ), 'starter_content' => true, ) ); + + $this->pending_starter_content_settings_ids = array(); } /** diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index 7514dd936e..13bf9ca1a1 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -1824,7 +1824,7 @@ function get_editor_stylesheets() { */ function get_theme_starter_content() { $theme_support = get_theme_support( 'starter-content' ); - if ( ! empty( $theme_support ) ) { + if ( is_array( $theme_support ) && ! empty( $theme_support[0] ) && is_array( $theme_support[0] ) ) { $config = $theme_support[0]; } else { $config = array(); diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php index 950f29238f..54128f8cc6 100644 --- a/tests/phpunit/tests/customize/manager.php +++ b/tests/phpunit/tests/customize/manager.php @@ -163,8 +163,15 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { $this->assertInstanceOf( 'WPDieException', $exception ); $this->assertContains( 'Invalid changeset UUID', $exception->getMessage() ); + update_option( 'fresh_site', 0 ); $wp_customize = new WP_Customize_Manager(); $wp_customize->setup_theme(); + $this->assertFalse( has_action( 'after_setup_theme', array( $wp_customize, 'import_theme_starter_content' ) ) ); + + // Make sure that starter content import gets queued on a fresh site. + update_option( 'fresh_site', 1 ); + $wp_customize->setup_theme(); + $this->assertEquals( 100, has_action( 'after_setup_theme', array( $wp_customize, 'import_theme_starter_content' ) ) ); } /** @@ -298,6 +305,145 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { $this->assertEquals( $data, $wp_customize->changeset_data() ); } + /** + * Test WP_Customize_Manager::import_theme_starter_content(). + * + * @covers WP_Customize_Manager::import_theme_starter_content() + * @covers WP_Customize_Manager::_save_starter_content_changeset() + */ + function test_import_theme_starter_content() { + wp_set_current_user( self::$admin_user_id ); + + global $wp_customize; + $wp_customize = new WP_Customize_Manager(); + $starter_content_config = array( + 'widgets' => array( + 'sidebar-1' => array( + 'text_business_info', + 'meta_custom' => array( 'meta', array( + 'title' => 'Pre-hydrated meta widget.', + ) ), + ), + ), + 'nav_menus' => array( + 'top' => array( + 'name' => 'Menu Name', + 'items' => array( + 'page_home', + 'page_about', + 'page_blog', + 'link_email', + 'link_facebook', + 'link_custom' => array( + 'title' => 'Custom', + 'url' => 'https://custom.example.com/', + ), + ), + ), + ), + 'posts' => array( + 'home', + 'about', + 'blog', + 'custom' => array( + 'post_type' => 'post', + 'post_title' => 'Custom', + ), + ), + 'options' => array( + 'blogname' => 'Starter Content Title', + 'blogdescription' => 'Starter Content Tagline', + 'show_on_front' => 'page', + 'page_on_front' => '{{home}}', + 'page_for_posts' => '{{blog}}', + ), + ); + + add_theme_support( 'starter-content', $starter_content_config ); + $this->assertEmpty( $wp_customize->unsanitized_post_values() ); + $wp_customize->import_theme_starter_content(); + $changeset_values = $wp_customize->unsanitized_post_values(); + $expected_setting_ids = array( + 'blogname', + 'blogdescription', + 'widget_text[2]', + 'widget_meta[3]', + 'sidebars_widgets[sidebar-1]', + 'nav_menus_created_posts', + 'nav_menu[-1]', + 'nav_menu_item[-1]', + 'nav_menu_item[-2]', + 'nav_menu_item[-3]', + 'nav_menu_item[-4]', + 'nav_menu_item[-5]', + 'nav_menu_item[-6]', + 'nav_menu_locations[top]', + 'show_on_front', + 'page_on_front', + 'page_for_posts', + ); + $this->assertEqualSets( $expected_setting_ids, array_keys( $changeset_values ) ); + + foreach ( array( 'widget_text[2]', 'widget_meta[3]' ) as $setting_id ) { + $this->assertInternalType( 'array', $changeset_values[ $setting_id ] ); + $instance_data = $wp_customize->widgets->sanitize_widget_instance( $changeset_values[ $setting_id ] ); + $this->assertInternalType( 'array', $instance_data ); + $this->assertArrayHasKey( 'title', $instance_data ); + } + + $this->assertEquals( array( 'text-2', 'meta-3' ), $changeset_values['sidebars_widgets[sidebar-1]'] ); + + $posts_by_name = array(); + foreach ( $changeset_values['nav_menus_created_posts'] as $post_id ) { + $post = get_post( $post_id ); + $this->assertEquals( 'auto-draft', $post->post_status ); + $posts_by_name[ $post->post_name ] = $post->ID; + } + + $this->assertEquals( 'page', $changeset_values['show_on_front'] ); + $this->assertEquals( $posts_by_name['home'], $changeset_values['page_on_front'] ); + $this->assertEquals( $posts_by_name['blog'], $changeset_values['page_for_posts'] ); + + $this->assertEquals( -1, $changeset_values['nav_menu_locations[top]'] ); + $this->assertEquals( $posts_by_name['home'], $changeset_values['nav_menu_item[-1]']['object_id'] ); + + $this->assertEmpty( $wp_customize->changeset_data() ); + $this->assertNull( $wp_customize->changeset_post_id() ); + $this->assertEquals( 1000, has_action( 'customize_register', array( $wp_customize, '_save_starter_content_changeset' ) ) ); + do_action( 'customize_register', $wp_customize ); // This will trigger the changeset save. + $this->assertInternalType( 'int', $wp_customize->changeset_post_id() ); + $this->assertNotEmpty( $wp_customize->changeset_data() ); + foreach ( $wp_customize->changeset_data() as $setting_id => $setting_params ) { + $this->assertArrayHasKey( 'starter_content', $setting_params ); + $this->assertTrue( $setting_params['starter_content'] ); + } + + // Test that saving non-starter content on top of the changeset clears the starter_content flag. + $wp_customize->save_changeset_post( array( + 'data' => array( + 'blogname' => array( 'value' => 'Starter Content Modified' ), + ), + ) ); + $changeset_data = $wp_customize->changeset_data(); + $this->assertArrayNotHasKey( 'starter_content', $changeset_data['blogname'] ); + $this->assertArrayHasKey( 'starter_content', $changeset_data['blogdescription'] ); + + // Test that adding blogname starter content is ignored now that it is modified, but updating a non-modified starter content blog description passes. + $previous_blogname = $changeset_data['blogname']['value']; + $previous_blogdescription = $changeset_data['blogdescription']['value']; + $wp_customize->import_theme_starter_content( array( + 'options' => array( + 'blogname' => 'Newer Starter Content Title', + 'blogdescription' => 'Newer Starter Content Description', + ), + ) ); + $changeset_data = $wp_customize->changeset_data(); + $this->assertEquals( $previous_blogname, $changeset_data['blogname']['value'] ); + $this->assertArrayNotHasKey( 'starter_content', $changeset_data['blogname'] ); + $this->assertNotEquals( $previous_blogdescription, $changeset_data['blogdescription']['value'] ); + $this->assertArrayHasKey( 'starter_content', $changeset_data['blogdescription'] ); + } + /** * Test WP_Customize_Manager::customize_preview_init(). * diff --git a/tests/phpunit/tests/theme/getThemeStarterContent.php b/tests/phpunit/tests/theme/getThemeStarterContent.php new file mode 100644 index 0000000000..59505f19c3 --- /dev/null +++ b/tests/phpunit/tests/theme/getThemeStarterContent.php @@ -0,0 +1,179 @@ +assertEmpty( $starter_content ); + } + + /** + * Testing passing nothing as starter content. + */ + function test_add_theme_support_single_param() { + add_theme_support( 'starter-content' ); + $starter_content = get_theme_starter_content(); + + $this->assertEmpty( $starter_content ); + } + + /** + * Testing that placeholder starter content gets expanded, that unrecognized placeholders are discarded, and that custom items are recognized. + */ + function test_default_content_sections() { + /* + * All placeholder identifiers should be referenced in this sample starter + * content and then tested to ensure they get hydrated in the call to + * get_theme_starter_content() to ensure that the starter content + * placeholder identifiers remain intact in core. + */ + $dehydrated_starter_content = array( + 'widgets' => array( + 'sidebar-1' => array( + 'text_business_info', + 'text_about', + 'archives', + 'calendar', + 'categories', + 'meta', + 'recent-comments', + 'recent-posts', + 'search', + 'unknown', + 'meta_custom' => array( 'meta', array( + 'title' => 'Pre-hydrated meta widget.', + ) ), + ), + ), + 'nav_menus' => array( + 'top' => array( + 'name' => 'Menu Name', + 'items' => array( + 'page_home', + 'page_about', + 'page_blog', + 'page_news', + 'page_contact', + 'link_email', + 'link_facebook', + 'link_foursquare', + 'link_github', + 'link_instagram', + 'link_linkedin', + 'link_pinterest', + 'link_twitter', + 'link_yelp', + 'link_youtube', + 'link_unknown', + 'link_custom' => array( + 'title' => 'Custom', + 'url' => 'https://custom.example.com/', + ), + ), + ), + ), + 'posts' => array( + 'home', + 'about', + 'contact', + 'blog', + 'news', + 'homepage-section', + 'unknown', + 'custom' => array( + 'post_type' => 'post', + 'post_title' => 'Custom', + ), + ), + 'options' => array( + 'show_on_front' => 'page', + 'page_on_front' => '{{home}}', + 'page_for_posts' => '{{blog}}', + ), + 'theme_mods' => array( + 'panel_1' => '{{homepage-section}}', + 'panel_2' => '{{about}}', + 'panel_3' => '{{blog}}', + 'panel_4' => '{{contact}}', + ), + ); + + add_theme_support( 'starter-content', $dehydrated_starter_content ); + + $hydrated_starter_content = get_theme_starter_content(); + $this->assertSame( $hydrated_starter_content['theme_mods'], $dehydrated_starter_content['theme_mods'] ); + $this->assertSame( $hydrated_starter_content['options'], $dehydrated_starter_content['options'] ); + $this->assertCount( 16, $hydrated_starter_content['nav_menus']['top']['items'], 'Unknown should be dropped, custom should be present.' ); + $this->assertCount( 10, $hydrated_starter_content['widgets']['sidebar-1'], 'Unknown should be dropped.' ); + + foreach ( $hydrated_starter_content['widgets']['sidebar-1'] as $widget ) { + $this->assertInternalType( 'array', $widget ); + $this->assertCount( 2, $widget ); + $this->assertInternalType( 'string', $widget[0] ); + $this->assertInternalType( 'array', $widget[1] ); + $this->assertArrayHasKey( 'title', $widget[1] ); + } + + foreach ( $hydrated_starter_content['nav_menus']['top']['items'] as $nav_menu_item ) { + $this->assertInternalType( 'array', $nav_menu_item ); + $this->assertTrue( ! empty( $nav_menu_item['object_id'] ) || ! empty( $nav_menu_item['url'] ) ); + } + + foreach ( $hydrated_starter_content['posts'] as $key => $post ) { + $this->assertInternalType( 'string', $key ); + $this->assertFalse( is_numeric( $key ) ); + $this->assertInternalType( 'array', $post ); + $this->assertArrayHasKey( 'post_type', $post ); + $this->assertArrayHasKey( 'post_title', $post ); + } + } + + /** + * Testing the filter with the text_credits widget. + */ + function test_get_theme_starter_content_filter() { + + add_theme_support( 'starter-content', + array( + 'widgets' => array( + 'sidebar-1' => array( + 'text_about', + ), + ), + ) + ); + + add_filter( 'get_theme_starter_content', array( $this, 'filter_theme_starter_content' ), 10, 2 ); + $starter_content = get_theme_starter_content(); + + $this->assertCount( 2, $starter_content['widgets']['sidebar-1'] ); + $this->assertEquals( 'Filtered Widget', $starter_content['widgets']['sidebar-1'][1][1]['title'] ); + } + + /** + * Filter the append a widget starter content. + * + * @param array $content Starter content (hydrated). + * @param array $config Starter content config (pre-hydrated). + * @return array Filtered starter content. + */ + public function filter_theme_starter_content( $content, $config ) { + $this->assertInternalType( 'array', $config ); + $this->assertCount( 1, $config['widgets']['sidebar-1'] ); + $content['widgets']['sidebar-1'][] = array( 'text', array( + 'title' => 'Filtered Widget', + 'text' => 'Custom ', + ) ); + return $content; + } +}