diff --git a/amp.php b/amp.php index 276950c3f78..8c7924c56c4 100644 --- a/amp.php +++ b/amp.php @@ -145,7 +145,13 @@ function amp_init() { add_rewrite_endpoint( amp_get_slug(), EP_PERMALINK ); AMP_Theme_Support::init(); + AMP_Validation_Manager::init(); AMP_Post_Type_Support::add_post_type_support(); + + if ( defined( 'WP_CLI' ) ) { + WP_CLI::add_command( 'amp', new AMP_CLI() ); + } + add_filter( 'request', 'amp_force_query_var_value' ); add_action( 'admin_init', 'AMP_Options_Manager::register_settings' ); add_action( 'wp_loaded', 'amp_editor_core_blocks' ); diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php index 13dd181f764..57f232d11d3 100644 --- a/includes/amp-helper-functions.php +++ b/includes/amp-helper-functions.php @@ -287,6 +287,16 @@ function is_amp_endpoint() { return false; } + /* + * If this is a URL for validation, and validation is forced for all URLs, return true. + * Normally, this would be false if the user has deselected a template, + * like by unchecking 'Categories' in 'AMP Settings' > 'Supported Templates'. + * But there's a flag for the WP-CLI command that sets this query var to validate all URLs. + */ + if ( AMP_Validation_Manager::is_theme_support_forced() ) { + return true; + } + $availability = AMP_Theme_Support::get_template_availability(); return amp_is_canonical() ? $availability['supported'] : ( $has_amp_query_var && $availability['supported'] ); } diff --git a/includes/class-amp-autoloader.php b/includes/class-amp-autoloader.php index 04499b7e6b5..e13f006bac9 100644 --- a/includes/class-amp-autoloader.php +++ b/includes/class-amp-autoloader.php @@ -100,6 +100,7 @@ class AMP_Autoloader { 'AMP_Validation_Manager' => 'includes/validation/class-amp-validation-manager', 'AMP_Invalid_URL_Post_Type' => 'includes/validation/class-amp-invalid-url-post-type', 'AMP_Validation_Error_Taxonomy' => 'includes/validation/class-amp-validation-error-taxonomy', + 'AMP_CLI' => 'includes/class-amp-cli', 'AMP_String_Utils' => 'includes/utils/class-amp-string-utils', 'AMP_WP_Utils' => 'includes/utils/class-amp-wp-utils', 'AMP_Widget_Archives' => 'includes/widgets/class-amp-widget-archives', diff --git a/includes/class-amp-cli.php b/includes/class-amp-cli.php new file mode 100644 index 00000000000..77a7f8aeed0 --- /dev/null +++ b/includes/class-amp-cli.php @@ -0,0 +1,608 @@ + 'Supported Templates'. + * But with this flag, validation will ignore these options. + * + * @var string + */ + const FLAG_NAME_FORCE_VALIDATION = 'force'; + + /** + * The WP-CLI argument to validate based on certain conditionals + * + * For example, --include=is_tag,is_author + * Normally, this script will validate all of the templates that don't have AMP disabled. + * But this allows validating only specific templates. + * + * @var string + */ + const INCLUDE_ARGUMENT = 'include'; + + /** + * The WP-CLI argument for the maximum URLs to validate for each type. + * + * If this is passed in the command, + * it overrides the value of self::$maximum_urls_to_validate_for_each_type. + * + * @var string + */ + const LIMIT_URLS_ARGUMENT = 'limit'; + + /** + * The WP CLI progress bar. + * + * @see https://make.wordpress.org/cli/handbook/internal-api/wp-cli-utils-make-progress-bar/ + * @var \cli\progress\Bar|\WP_CLI\NoOp + */ + public static $wp_cli_progress; + + /** + * The total number of validation errors, regardless of whether they were accepted. + * + * @var int + */ + public static $total_errors = 0; + + /** + * The total number of unaccepted validation errors. + * + * If an error has been accepted in the /wp-admin validation UI, + * it won't count toward this. + * + * @var int + */ + public static $unaccepted_errors = 0; + + /** + * The number of URLs crawled, regardless of whether they have validation errors. + * + * @var int + */ + public static $number_crawled = 0; + + /** + * Whether to force crawling of URLs. + * + * By default, this script only crawls URLs that support AMP, + * where the user has not opted-out of AMP for the URL. + * For example, by un-checking 'Posts' in 'AMP Settings' > 'Supported Templates'. + * Or un-checking 'Enable AMP' in the post's editor. + * + * @var int + */ + public static $force_crawl_urls = false; + + /** + * A whitelist of conditionals to use for validation. + * + * Usually, this script will validate all of the templates that don't have AMP disabled. + * But this allows validating based on only these conditionals. + * This is set if the WP-CLI command has an --include argument. + * + * @var array + */ + public static $include_conditionals = array(); + + /** + * The maximum number of URLs to validate for each type. + * + * Templates are each a separate type, like those for is_category() and is_tag(). + * Also, each post type is a separate type. + * This value is overridden if the WP-CLI command has an --limit argument, like --limit=10. + * + * @var int + */ + public static $limit_type_validate_count; + + /** + * The validation counts by type, like template or post type. + * + * @var array[][] { + * Validity by type. + * + * @type int $valid The number of valid URLs for this type. + * @type int $total The total number of URLs for this type, valid or invalid. + * } + */ + public static $validity_by_type = array(); + + /** + * Crawl the entire site to get AMP validation results. + * + * ## OPTIONS + * + * [--limit=] + * : The maximum number of URLs to validate for each template and content type. + * --- + * default: 100 + * --- + * + * [--include=] + * : Only validates a URL if one of the conditionals is true. + * + * [--force] + * : Force validation of URLs even if their associated templates or object types do not have AMP enabled. + * + * ## EXAMPLES + * + * wp amp validate-site --include=is_author,is_tag + * + * @subcommand validate-site + * @param array $args Positional args. + * @param array $assoc_args Associative args. + * @throws Exception If an error happens. + */ + public function validate_site( $args, $assoc_args ) { + unset( $args ); + self::$include_conditionals = array(); + self::$force_crawl_urls = false; + self::$limit_type_validate_count = (int) $assoc_args[ self::LIMIT_URLS_ARGUMENT ]; + + /* + * Handle the argument and flag passed to the command: --include and --force. + * If the self::INCLUDE_ARGUMENT is present, force crawling or URLs. + * The WP-CLI command should indicate which templates are crawled, not the /wp-admin options. + */ + if ( ! empty( $assoc_args[ self::INCLUDE_ARGUMENT ] ) ) { + self::$include_conditionals = explode( ',', $assoc_args[ self::INCLUDE_ARGUMENT ] ); + self::$force_crawl_urls = true; + } elseif ( isset( $assoc_args[ self::FLAG_NAME_FORCE_VALIDATION ] ) ) { + self::$force_crawl_urls = true; + } + + if ( ! current_theme_supports( 'amp' ) ) { + if ( self::$force_crawl_urls ) { + /* + * There is no theme support added programmatically or via options. + * So make sure that theme support is present so that AMP_Validation_Manager::validate_url() + * will use a canonical URL as the basis for obtaining validation results. + */ + add_theme_support( 'amp' ); + } else { + WP_CLI::error( + sprintf( + /* translators: %s is the flag to force validation */ + __( 'The current template mode is Classic, which does not allow crawling the site. Please pass the --%s flag in order to force crawling for validation.', 'amp' ), + self::FLAG_NAME_FORCE_VALIDATION + ) + ); + } + } + + $number_urls_to_crawl = self::count_urls_to_validate(); + if ( ! $number_urls_to_crawl ) { + if ( ! empty( self::$include_conditionals ) ) { + WP_CLI::error( + sprintf( + /* translators: %s is the command line argument to include certain templates */ + __( 'The templates passed via the --%s argument did not match any URLs. You might try passing different templates to it.', 'amp' ), + self::INCLUDE_ARGUMENT + ) + ); + } else { + WP_CLI::error( + sprintf( + /* translators: %s is the command line argument to force validation */ + __( 'All of your templates might be unchecked in AMP Settings > Supported Templates. You might pass --%s to this command.', 'amp' ), + self::FLAG_NAME_FORCE_VALIDATION + ) + ); + } + } + + WP_CLI::log( __( 'Crawling the site for AMP validity.', 'amp' ) ); + + self::$wp_cli_progress = WP_CLI\Utils\make_progress_bar( + /* translators: %d is the number of URLs */ + sprintf( __( 'Validating %d URLs...', 'amp' ), $number_urls_to_crawl ), + $number_urls_to_crawl + ); + self::crawl_site(); + self::$wp_cli_progress->finish(); + + $key_template_type = __( 'Template or content type', 'amp' ); + $key_url_count = __( 'URL Count', 'amp' ); + $key_validity_rate = __( 'Validity Rate', 'amp' ); + + $table_validation_by_type = array(); + foreach ( self::$validity_by_type as $type_name => $validity ) { + $table_validation_by_type[] = array( + $key_template_type => $type_name, + $key_url_count => $validity['total'], + $key_validity_rate => sprintf( '%d%%', 100.0 * ( $validity['valid'] / $validity['total'] ) ), + ); + } + + if ( empty( $table_validation_by_type ) ) { + WP_CLI::error( __( 'No validation results were obtained from the URLs.', 'amp' ) ); + return; + } + + WP_CLI::success( + sprintf( + /* translators: $1%d is the number of URls crawled, $2%d is the number of validation issues, $3%d is the number of unaccepted issues, $4%s is the list of validation by type, $5%s is the link for more details */ + __( '%3$d crawled URLs have unaccepted issue(s) out of %2$d total with AMP validation issue(s); %1$d URLs were crawled.', 'amp' ), + self::$number_crawled, + self::$total_errors, + self::$unaccepted_errors + ) + ); + + // Output a table of validity by template/content type. + WP_CLI\Utils\format_items( + 'table', + $table_validation_by_type, + array( $key_template_type, $key_url_count, $key_validity_rate ) + ); + + $url_more_details = add_query_arg( + 'post_type', + AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG, + admin_url( 'edit.php' ) + ); + /* translators: %s is the URL to the admin */ + WP_CLI::line( sprintf( __( 'For more details, please see: %s', 'amp' ), $url_more_details ) ); + } + + /** + * Gets the total number of URLs to validate. + * + * By default, this only counts AMP-enabled posts and terms. + * But if $force_crawl_urls is true, it counts all of them, regardless of their AMP status. + * It also uses self::$maximum_urls_to_validate_for_each_type, + * which can be overridden with a command line argument. + * + * @return int The number of URLs to validate. + */ + public static function count_urls_to_validate() { + /* + * If the homepage is set to 'Your latest posts,' start the $total_count at 1. + * Otherwise, it will probably be counted in the query for pages below. + */ + $total_count = 'posts' === get_option( 'show_on_front' ) && self::is_template_supported( 'is_home' ) ? 1 : 0; + + $amp_enabled_taxonomies = array_filter( + get_taxonomies( array( 'public' => true ) ), + array( 'AMP_CLI', 'does_taxonomy_support_amp' ) + ); + + // Count all public taxonomy terms. + foreach ( $amp_enabled_taxonomies as $taxonomy ) { + $term_query = new WP_Term_Query( array( + 'taxonomy' => $taxonomy, + 'fields' => 'ids', + 'number' => self::$limit_type_validate_count, + ) ); + + // If $term_query->terms is an empty array, passing it to count() will throw an error. + $total_count += ! empty( $term_query->terms ) ? count( $term_query->terms ) : 0; + } + + // Count posts by type, like post, page, attachment, etc. + $public_post_types = get_post_types( array( 'public' => true ), 'names' ); + foreach ( $public_post_types as $post_type ) { + $posts = self::get_posts_that_support_amp( self::get_posts_by_type( $post_type ) ); + $total_count += ! empty( $posts ) ? count( $posts ) : 0; + } + + // Count author pages, like https://example.com/author/admin/. + $total_count += count( self::get_author_page_urls() ); + + // Count a single example date page, like https://example.com/?year=2019. + if ( self::get_date_page() ) { + $total_count++; + } + + // Count a single example search page, like https://example.com/?s=example. + if ( self::get_search_page() ) { + $total_count++; + } + + return $total_count; + } + + /** + * Gets the posts IDs that support AMP. + * + * By default, this only gets the post IDs if they support AMP. + * This means that 'Posts' isn't deselected in 'AMP Settings' > 'Supported Templates'. + * And 'Enable AMP' isn't unchecked in the post's editor. + * But if $force_crawl_urls is true, this simply returns all of the IDs. + * + * @param array $ids THe post IDs to check for AMP support. + * @return array The post IDs that support AMP, or an empty array. + */ + public static function get_posts_that_support_amp( $ids ) { + if ( ! self::is_template_supported( 'is_singular' ) ) { + return array(); + } + + if ( self::$force_crawl_urls ) { + return $ids; + } + + return array_filter( + $ids, + function( $id ) { + return post_supports_amp( $id ); + } + ); + } + + /** + * Gets whether the taxonomy supports AMP. + * + * This only gets the term IDs if they support AMP. + * If their taxonomy is unchecked in 'AMP Settings' > 'Supported Templates,' this does not return them. + * For example, if 'Categories' is unchecked. + * This can be overridden by passing the self::FLAG_NAME_FORCE_VALIDATION argument to the WP-CLI command. + * + * @param string $taxonomy The taxonomy. + * @return boolean Whether the taxonomy supports AMP. + */ + public static function does_taxonomy_support_amp( $taxonomy ) { + if ( 'post_tag' === $taxonomy ) { + $taxonomy = 'tag'; + } + $taxonomy_key = 'is_' . $taxonomy; + $custom_taxonomy_key = sprintf( 'is_tax[%s]', $taxonomy ); + return self::is_template_supported( $taxonomy_key ) || self::is_template_supported( $custom_taxonomy_key ); + } + + /** + * Gets whether the template is supported. + * + * If the user has passed an include argument to the WP-CLI command, use that to find if this template supports AMP. + * For example, wp amp validate-site --include=is_tag,is_category + * would return true only if is_tag() or is_category(). + * But passing the self::FLAG_NAME_FORCE_VALIDATION argument to the WP-CLI command overrides this. + * + * @param string $template The template to check. + * @return bool Whether the template is supported. + */ + public static function is_template_supported( $template ) { + // If the --include argument is present in the WP-CLI command, this template conditional must be present in it. + if ( ! empty( self::$include_conditionals ) ) { + return in_array( $template, self::$include_conditionals, true ); + } + if ( self::$force_crawl_urls ) { + return true; + } + + $supportable_templates = AMP_Theme_Support::get_supportable_templates(); + + // Check whether this taxonomy's template is supported, including in the 'AMP Settings' > 'Supported Templates' UI. + return ! empty( $supportable_templates[ $template ]['supported'] ); + } + + /** + * Gets the IDs of public, published posts. + * + * @param string $post_type The post type. + * @param int|null $offset The offset of the query (optional). + * @param int|null $number The number of posts to query for (optional). + * @return int[] $post_ids The post IDs in an array. + */ + public static function get_posts_by_type( $post_type, $offset = null, $number = null ) { + $args = array( + 'post_type' => $post_type, + 'posts_per_page' => is_int( $number ) ? $number : self::$limit_type_validate_count, + 'post_status' => 'publish', + 'orderby' => 'ID', + 'order' => 'DESC', + 'fields' => 'ids', + ); + if ( is_int( $offset ) ) { + $args['offset'] = $offset; + } + + // Attachment posts usually have the post_status of 'inherit,' so they can use the status of the post they're attached to. + if ( 'attachment' === $post_type ) { + $args['post_status'] = 'inherit'; + } + $query = new WP_Query( $args ); + + return $query->posts; + } + + /** + * Gets the front-end links for taxonomy terms. + * For example, https://example.org/?cat=2 + * + * @param string $taxonomy The name of the taxonomy, like 'category' or 'post_tag'. + * @param int|string $offset The number at which to offset the query (optional). + * @param int $number The maximum amount of links to get (optional). + * @return string[] The term links, as an array of strings. + */ + public static function get_taxonomy_links( $taxonomy, $offset = '', $number = 1 ) { + return array_map( + 'get_term_link', + get_terms( + array_merge( + compact( 'taxonomy', 'offset', 'number' ), + array( + 'orderby' => 'id', + ) + ) + ) + ); + } + + /** + * Gets the author page URLs, like https://example.com/author/admin/. + * + * Accepts an $offset parameter, for the query of authors. + * 0 is the first author in the query, and 1 is the second. + * + * @param int|string $offset The offset for the URL to query for, should be an int if passing an argument. + * @param int|string $number The total number to query for, should be an int if passing an argument. + * @return array The author page URLs, or an empty array. + */ + public static function get_author_page_urls( $offset = '', $number = '' ) { + $author_page_urls = array(); + if ( ! self::is_template_supported( 'is_author' ) ) { + return $author_page_urls; + } + + $number = ! empty( $number ) ? $number : self::$limit_type_validate_count; + foreach ( get_users( compact( 'offset', 'number' ) ) as $author ) { + $author_page_urls[] = get_author_posts_url( $author->ID, $author->user_nicename ); + } + + return $author_page_urls; + } + + /** + * Gets a single search page URL, like https://example.com/?s=example. + * + * @return string|null An example search page, or null. + */ + public static function get_search_page() { + if ( ! self::is_template_supported( 'is_search' ) ) { + return null; + } + + return add_query_arg( 's', 'example', home_url( '/' ) ); + } + + /** + * Gets a single date page URL, like https://example.com/?year=2018. + * + * @return string|null An example search page, or null. + */ + public static function get_date_page() { + if ( ! self::is_template_supported( 'is_date' ) ) { + return null; + } + + return add_query_arg( 'year', date( 'Y' ), home_url( '/' ) ); + } + + /** + * Validates the URLs of the entire site. + * + * Includes the URLs of public, published posts, public taxonomies, and other templates. + * This validates one of each type at a time, + * and iterates until it reaches the maximum number of URLs for each type. + */ + public static function crawl_site() { + /* + * If 'Your homepage displays' is set to 'Your latest posts', validate the homepage. + * It will not be part of the page validation below. + */ + if ( 'posts' === get_option( 'show_on_front' ) && self::is_template_supported( 'is_home' ) ) { + self::validate_and_store_url( home_url( '/' ), 'home' ); + } + + $amp_enabled_taxonomies = array_filter( + get_taxonomies( array( 'public' => true ) ), + array( 'AMP_CLI', 'does_taxonomy_support_amp' ) + ); + $public_post_types = get_post_types( array( 'public' => true ), 'names' ); + + // Validate one URL of each template/content type, then another URL of each type on the next iteration. + for ( $i = 0; $i < self::$limit_type_validate_count; $i++ ) { + // Validate all public, published posts. + foreach ( $public_post_types as $post_type ) { + $post_ids = self::get_posts_that_support_amp( self::get_posts_by_type( $post_type, $i, 1 ) ); + if ( ! empty( $post_ids[0] ) ) { + self::validate_and_store_url( get_permalink( $post_ids[0] ), $post_type ); + } + } + + foreach ( $amp_enabled_taxonomies as $taxonomy ) { + $taxonomy_links = self::get_taxonomy_links( $taxonomy, $i, 1 ); + $link = reset( $taxonomy_links ); + if ( ! empty( $link ) ) { + self::validate_and_store_url( $link, $taxonomy ); + } + } + + $author_page_urls = self::get_author_page_urls( $i, 1 ); + if ( ! empty( $author_page_urls[0] ) ) { + self::validate_and_store_url( $author_page_urls[0], 'author' ); + } + } + + // Only validate 1 date and 1 search page. + $url = self::get_date_page(); + if ( $url ) { + self::validate_and_store_url( $url, 'date' ); + } + $url = self::get_search_page(); + if ( $url ) { + self::validate_and_store_url( $url, 'search' ); + } + } + + /** + * Validates the URL, stores the results, and increments the counts. + * + * @param string $url The URL to validate. + * @param string $type The type of template, post, or taxonomy. + */ + public static function validate_and_store_url( $url, $type ) { + $validity = AMP_Validation_Manager::validate_url( $url ); + + /* + * If the request to validate this returns a WP_Error, return. + * One cause of an error is if the validation request results in a 404 response code. + */ + if ( is_wp_error( $validity ) ) { + /* translators: %1$s is error code, %2$s is error message, and %3$s is the actual URL. */ + WP_CLI::warning( sprintf( __( 'Validate URL error (%1$s): %2$s URL: %3$s', 'amp' ), $validity->get_error_code(), $validity->get_error_message(), $url ) ); + return; + } + if ( self::$wp_cli_progress ) { + self::$wp_cli_progress->tick(); + } + + AMP_Invalid_URL_Post_Type::store_validation_errors( $validity['validation_errors'], $validity['url'] ); + $unaccepted_error_count = count( array_filter( + $validity['validation_errors'], + function( $error ) { + $validation_status = AMP_Validation_Error_Taxonomy::get_validation_error_sanitization( $error ); + return AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_ACCEPTED_STATUS !== $validation_status['term_status']; + } + ) ); + + if ( count( $validity['validation_errors'] ) > 0 ) { + self::$total_errors++; + } + if ( $unaccepted_error_count > 0 ) { + self::$unaccepted_errors++; + } + + self::$number_crawled++; + + if ( ! isset( self::$validity_by_type[ $type ] ) ) { + self::$validity_by_type[ $type ] = array( + 'valid' => 0, + 'total' => 0, + ); + } + self::$validity_by_type[ $type ]['total']++; + if ( 0 === $unaccepted_error_count ) { + self::$validity_by_type[ $type ]['valid']++; + } + } +} diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 0ba680cf7cb..b213d055eb1 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -137,10 +137,6 @@ public static function init() { return; } - AMP_Validation_Manager::init( array( - 'should_locate_sources' => AMP_Validation_Manager::should_validate_response(), - ) ); - self::$init_start_time = microtime( true ); self::purge_amp_query_vars(); @@ -204,6 +200,8 @@ public static function read_theme_support() { 'paired' => ( 'paired' === $theme_support_option ), ) ); self::$support_added_via_option = true; + } elseif ( AMP_Validation_Manager::is_theme_support_forced() ) { + add_theme_support( 'amp' ); } } diff --git a/includes/options/class-amp-options-menu.php b/includes/options/class-amp-options-menu.php index 860fba5a9f7..327cb9f17d8 100644 --- a/includes/options/class-amp-options-menu.php +++ b/includes/options/class-amp-options-menu.php @@ -190,6 +190,39 @@ public function render_theme_support() {
+ + publish > 0 ) : ?> +
+

+ %s', + esc_url( add_query_arg( 'post_type', AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG, admin_url( 'edit.php' ) ) ), + esc_html( get_post_type_object( AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG )->labels->name ) + ), + sprintf( + '%s', + esc_url( + add_query_arg( + array( + 'taxonomy' => AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG, + 'post_type' => AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG, + ), + admin_url( 'edit-tags.php' ) + ) + ), + esc_html( get_taxonomy( AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG )->labels->name ) + ) + ) + ); + ?> +

+
+
diff --git a/includes/validation/class-amp-invalid-url-post-type.php b/includes/validation/class-amp-invalid-url-post-type.php index 58ead72abce..65b4597db31 100644 --- a/includes/validation/class-amp-invalid-url-post-type.php +++ b/includes/validation/class-amp-invalid-url-post-type.php @@ -96,7 +96,7 @@ public static function register() { 'supports' => false, 'public' => false, 'show_ui' => true, - 'show_in_menu' => AMP_Options_Manager::OPTION_NAME, + 'show_in_menu' => ( self::should_show_in_menu() || AMP_Validation_Error_Taxonomy::should_show_in_menu() ) ? AMP_Options_Manager::OPTION_NAME : false, // @todo Show in rest. ) ); @@ -109,6 +109,19 @@ public static function register() { } } + /** + * Determine whether the admin menu item should be included. + * + * @return bool Whether to show in menu. + */ + public static function should_show_in_menu() { + global $pagenow; + if ( current_theme_supports( 'amp' ) ) { + return true; + } + return ( 'edit.php' === $pagenow && ( isset( $_GET['post_type'] ) && self::POST_TYPE_SLUG === $_GET['post_type'] ) ); // WPCS: CSRF OK. + } + /** * Add admin hooks. */ diff --git a/includes/validation/class-amp-validation-error-taxonomy.php b/includes/validation/class-amp-validation-error-taxonomy.php index 9c8414dc7b7..8505e0f5669 100644 --- a/includes/validation/class-amp-validation-error-taxonomy.php +++ b/includes/validation/class-amp-validation-error-taxonomy.php @@ -146,7 +146,7 @@ public static function register() { 'show_tagcloud' => false, 'show_in_quick_edit' => false, 'hierarchical' => false, // Or true? Code could be the parent term? - 'show_in_menu' => true, + 'show_in_menu' => ( self::should_show_in_menu() || AMP_Invalid_URL_Post_Type::should_show_in_menu() ), 'meta_box_cb' => false, // See print_validation_errors_meta_box(). 'capabilities' => array( 'assign_terms' => 'do_not_allow', @@ -162,6 +162,19 @@ public static function register() { self::accept_validation_errors( AMP_Core_Theme_Sanitizer::get_acceptable_errors( get_template() ) ); } + /** + * Determine whether the admin menu item should be included. + * + * @return bool Whether to show in menu. + */ + public static function should_show_in_menu() { + global $pagenow; + if ( current_theme_supports( 'amp' ) ) { + return true; + } + return ( 'edit-tags.php' === $pagenow && ( isset( $_GET['taxonomy'] ) && self::TAXONOMY_SLUG === $_GET['taxonomy'] ) ); // WPCS: CSRF OK. + } + /** * Prepare a validation error for lookup or insertion as taxonomy term. * @@ -459,7 +472,9 @@ public static function add_admin_hooks() { add_filter( 'terms_clauses', array( __CLASS__, 'filter_terms_clauses_for_description_search' ), 10, 3 ); add_action( 'admin_notices', array( __CLASS__, 'add_admin_notices' ) ); add_filter( 'tag_row_actions', array( __CLASS__, 'filter_tag_row_actions' ), 10, 2 ); - add_action( 'admin_menu', array( __CLASS__, 'add_admin_menu_validation_error_item' ) ); + if ( get_taxonomy( self::TAXONOMY_SLUG )->show_in_menu ) { + add_action( 'admin_menu', array( __CLASS__, 'add_admin_menu_validation_error_item' ) ); + } add_filter( 'manage_' . self::TAXONOMY_SLUG . '_custom_column', array( __CLASS__, 'filter_manage_custom_columns' ), 10, 3 ); add_filter( 'views_edit-' . self::TAXONOMY_SLUG, array( __CLASS__, 'filter_views_edit' ) ); add_filter( 'posts_where', array( __CLASS__, 'filter_posts_where_for_validation_error_status' ), 10, 2 ); diff --git a/includes/validation/class-amp-validation-manager.php b/includes/validation/class-amp-validation-manager.php index d876e04f62c..42f3b0dc5d8 100644 --- a/includes/validation/class-amp-validation-manager.php +++ b/includes/validation/class-amp-validation-manager.php @@ -148,7 +148,7 @@ class AMP_Validation_Manager { public static function init( $args = array() ) { $args = array_merge( array( - 'should_locate_sources' => false, + 'should_locate_sources' => self::should_validate_response(), ), $args ); @@ -158,6 +158,11 @@ public static function init( $args = array() ) { AMP_Invalid_URL_Post_Type::register(); AMP_Validation_Error_Taxonomy::register(); + // Short-circuit if AMP is not supported as only the post types should be available. + if ( ! current_theme_supports( 'amp' ) ) { + return; + } + add_action( 'save_post', array( __CLASS__, 'handle_save_post_prompting_validation' ) ); add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_block_validation' ) ); @@ -186,6 +191,21 @@ public static function init( $args = array() ) { } } + /** + * Determine whether AMP theme support is forced via the amp_validate query param. + * + * @since 1.0 + * + * @return bool Whether theme support forced. + */ + public static function is_theme_support_forced() { + return ( + isset( $_GET[ self::VALIDATE_QUERY_VAR ] ) // WPCS: CSRF OK. + && + ( self::has_cap() || self::get_amp_validate_nonce() === $_GET[ self::VALIDATE_QUERY_VAR ] ) // WPCS: CSRF OK. + ); + } + /** * Return whether sanitization is forcibly accepted, whether because in native mode or via user option. * diff --git a/tests/test-amp-helper-functions.php b/tests/test-amp-helper-functions.php index 16894c7aa4e..5e8ef409dae 100644 --- a/tests/test-amp-helper-functions.php +++ b/tests/test-amp-helper-functions.php @@ -344,6 +344,26 @@ public function test_is_amp_endpoint() { $GLOBALS['pagenow'] = $page; $this->assertFalse( is_amp_endpoint() ); } + unset( $GLOBALS['pagenow'] ); + + /** + * Simulate a user unchecking almost all of the boxes in 'AMP Settings' > 'Supported Templates'. + * The user has chosen not to show them as AMP, so most URLs should not be AMP endpoints. + */ + AMP_Options_Manager::update_option( 'all_templates_supported', false ); + AMP_Options_Manager::update_option( 'supported_templates', array( 'is_author' ) ); + + // A post shouldn't be an AMP endpoint, as it was unchecked in the UI via the options above. + $this->go_to( $this->factory()->post->create() ); + $this->assertFalse( is_amp_endpoint() ); + + // The homepage shouldn't be an AMP endpoint, as it was also unchecked in the UI. + $this->go_to( home_url( '/' ) ); + $this->assertFalse( is_amp_endpoint() ); + + // When the user passes a flag to the WP-CLI command, it forces AMP validation no matter whether the user disabled AMP on any template. + $_GET[ AMP_Validation_Manager::VALIDATE_QUERY_VAR ] = AMP_Validation_Manager::get_amp_validate_nonce(); + $this->assertTrue( is_amp_endpoint() ); } /** diff --git a/tests/test-class-amp-cli.php b/tests/test-class-amp-cli.php new file mode 100644 index 00000000000..c8a518b4f76 --- /dev/null +++ b/tests/test-class-amp-cli.php @@ -0,0 +1,495 @@ +get_inital_url_count(); + $this->assertEquals( $number_original_urls, AMP_CLI::count_urls_to_validate() ); + AMP_CLI::$limit_type_validate_count = 100; + + $category = $this->factory()->term->create( array( 'taxonomy' => 'category' ) ); + $number_new_posts = AMP_CLI::$limit_type_validate_count / 2; + $post_ids = array(); + for ( $i = 0; $i < $number_new_posts; $i++ ) { + $post_ids[] = $this->factory()->post->create( array( + 'tax_input' => array( 'category' => $category ), + ) ); + } + + /* + * Add the number of new posts, original URLs, and 1 for the $category that all of them have. + * And ensure that the tested method finds a URL for all of them. + */ + $expected_url_count = $number_new_posts + $number_original_urls + 1; + $this->assertEquals( $expected_url_count, AMP_CLI::count_urls_to_validate() ); + + $number_of_new_terms = 20; + $expected_url_count += $number_of_new_terms; + $taxonomy = 'category'; + $terms_for_current_taxonomy = array(); + for ( $i = 0; $i < $number_of_new_terms; $i++ ) { + $terms_for_current_taxonomy[] = $this->factory()->term->create( array( + 'taxonomy' => $taxonomy, + ) ); + } + + // Terms need to be associated with a post in order to be returned in get_terms(). + wp_set_post_terms( + $post_ids[0], + $terms_for_current_taxonomy, + $taxonomy + ); + + $this->assertEquals( $expected_url_count, AMP_CLI::count_urls_to_validate() ); + } + + /** + * Test get_posts_that_support_amp. + * + * @covers AMP_CLI::get_posts_that_support_amp() + */ + public function test_get_posts_that_support_amp() { + $number_of_posts = 20; + $ids = array(); + for ( $i = 0; $i < $number_of_posts; $i++ ) { + $ids[] = $this->factory()->post->create(); + } + + // This should count all of the newly-created posts as supporting AMP. + $this->assertEquals( $ids, AMP_CLI::get_posts_that_support_amp( $ids ) ); + + // Simulate 'Enable AMP' being unchecked in the post editor, in which case get_url_count() should not count it. + $first_id = $ids[0]; + update_post_meta( + $first_id, + AMP_Post_Meta_Box::STATUS_POST_META_KEY, + AMP_Post_Meta_Box::DISABLED_STATUS + ); + $this->assertEquals( array(), AMP_CLI::get_posts_that_support_amp( array( $first_id ) ) ); + + update_post_meta( + $first_id, + AMP_Post_Meta_Box::STATUS_POST_META_KEY, + AMP_Post_Meta_Box::ENABLED_STATUS + ); + + // When the second $force_count_all_urls argument is true, all of the newly-created posts should be part of the URL count. + AMP_CLI::$force_crawl_urls = true; + $this->assertEquals( $ids, AMP_CLI::get_posts_that_support_amp( $ids ) ); + AMP_CLI::$force_crawl_urls = false; + + // In Native AMP, the IDs should include all of the newly-created posts. + add_theme_support( 'amp' ); + $this->assertEquals( $ids, AMP_CLI::get_posts_that_support_amp( $ids ) ); + + // In Paired Mode, the IDs should also include all of the newly-created posts. + add_theme_support( 'amp', array( + 'paired' => true, + ) ); + $this->assertEquals( $ids, AMP_CLI::get_posts_that_support_amp( $ids ) ); + + /* + * If the WP-CLI command has an include argument, and is_singular isn't in it, no posts will have AMP enabled. + * For example, wp amp validate-site --include=is_tag,is_category + */ + AMP_CLI::$include_conditionals = array( 'is_tag', 'is_category' ); + $this->assertEquals( array(), AMP_CLI::get_posts_that_support_amp( $ids ) ); + + /* + * If is_singular is in the WP-CLI argument, it should allow return these posts as being AMP-enabled. + * For example, wp amp validate-site include=is_singular,is_category + */ + AMP_CLI::$include_conditionals = array( 'is_singular', 'is_category' ); + $this->assertEmpty( array_diff( $ids, AMP_CLI::get_posts_that_support_amp( $ids ) ) ); + AMP_CLI::$include_conditionals = array(); + } + + /** + * Test does_taxonomy_support_amp. + * + * @covers AMP_CLI::does_taxonomy_support_amp() + */ + public function test_does_taxonomy_support_amp() { + $custom_taxonomy = 'foo_custom_taxonomy'; + register_taxonomy( $custom_taxonomy, 'post' ); + $taxonomies_to_test = array( $custom_taxonomy, 'category', 'post_tag' ); + AMP_Options_Manager::update_option( 'supported_templates', array( 'is_category', 'is_tag', sprintf( 'is_tax[%s]', $custom_taxonomy ) ) ); + + // When these templates are not unchecked in the 'AMP Settings' UI, these should be supported. + foreach ( $taxonomies_to_test as $taxonomy ) { + $this->assertTrue( AMP_CLI::does_taxonomy_support_amp( $taxonomy ) ); + } + + // When the user has not checked the boxes for 'Categories' and 'Tags,' this should be false. + AMP_Options_Manager::update_option( 'supported_templates', array( 'is_author' ) ); + AMP_Options_Manager::update_option( 'all_templates_supported', false ); + foreach ( $taxonomies_to_test as $taxonomy ) { + $this->assertFalse( AMP_CLI::does_taxonomy_support_amp( $taxonomy ) ); + } + + // When $force_crawl_urls is true, all taxonomies should be supported. + AMP_CLI::$force_crawl_urls = true; + foreach ( $taxonomies_to_test as $taxonomy ) { + $this->assertTrue( AMP_CLI::does_taxonomy_support_amp( $taxonomy ) ); + } + AMP_CLI::$force_crawl_urls = false; + + // When the user has checked the 'all_templates_supported' box, this should always be true. + AMP_Options_Manager::update_option( 'all_templates_supported', true ); + foreach ( $taxonomies_to_test as $taxonomy ) { + $this->assertTrue( AMP_CLI::does_taxonomy_support_amp( $taxonomy ) ); + } + AMP_Options_Manager::update_option( 'all_templates_supported', false ); + + /* + * If the user passed allowed conditionals to the WP-CLI command like wp amp validate-site --include=is_category,is_tag + * these should be supported taxonomies. + */ + AMP_CLI::$include_conditionals = array( 'is_category', 'is_tag' ); + $this->assertTrue( AMP_CLI::does_taxonomy_support_amp( 'category' ) ); + $this->assertTrue( AMP_CLI::does_taxonomy_support_amp( 'tag' ) ); + $this->assertFalse( AMP_CLI::does_taxonomy_support_amp( 'author' ) ); + $this->assertFalse( AMP_CLI::does_taxonomy_support_amp( 'search' ) ); + AMP_CLI::$include_conditionals = array(); + } + + /** + * Test is_template_supported. + * + * @covers AMP_CLI::is_template_supported() + */ + public function test_is_template_supported() { + $author_conditional = 'is_author'; + $search_conditional = 'is_search'; + + AMP_Options_Manager::update_option( 'supported_templates', array( $author_conditional ) ); + AMP_Options_Manager::update_option( 'all_templates_supported', false ); + $this->assertTrue( AMP_CLI::is_template_supported( $author_conditional ) ); + $this->assertFalse( AMP_CLI::is_template_supported( $search_conditional ) ); + + AMP_Options_Manager::update_option( 'supported_templates', array( $search_conditional ) ); + $this->assertTrue( AMP_CLI::is_template_supported( $search_conditional ) ); + $this->assertFalse( AMP_CLI::is_template_supported( $author_conditional ) ); + } + + /** + * Test get_posts_by_type. + * + * @covers AMP_CLI::get_posts_by_type() + */ + public function test_get_posts_by_type() { + $number_posts_each_post_type = 20; + $post_types = get_post_types( array( 'public' => true ), 'names' ); + + foreach ( $post_types as $post_type ) { + // Start the expected posts with the existing post(s). + $query = new WP_Query( array( + 'fields' => 'ids', + 'post_type' => $post_type, + ) ); + $expected_posts = $query->posts; + + for ( $i = 0; $i < $number_posts_each_post_type; $i++ ) { + array_unshift( $expected_posts, $this->factory()->post->create( array( + 'post_type' => $post_type, + ) ) ); + } + + $actual_posts = AMP_CLI::get_posts_by_type( $post_type ); + $this->assertEquals( $expected_posts, array_values( $actual_posts ) ); + + // Test with the $offset and $number arguments. + $offset = 0; + $actual_posts = AMP_CLI::get_posts_by_type( $post_type, $offset, $number_posts_each_post_type ); + $this->assertEquals( array_slice( $expected_posts, $offset, $number_posts_each_post_type ), $actual_posts ); + } + } + + /** + * Test get_taxonomy_links. + * + * @covers AMP_CLI::get_taxonomy_links() + */ + public function test_get_taxonomy_links() { + $number_links_each_taxonomy = 20; + $taxonomies = get_taxonomies( array( + 'public' => true, + ) ); + + foreach ( $taxonomies as $taxonomy ) { + // Begin the expected links with the term links that already exist. + $expected_links = array_map( 'get_term_link', get_terms( array( 'taxonomy' => $taxonomy ) ) ); + $terms_for_current_taxonomy = array(); + for ( $i = 0; $i < $number_links_each_taxonomy; $i++ ) { + $terms_for_current_taxonomy[] = $this->factory()->term->create( array( + 'taxonomy' => $taxonomy, + ) ); + } + + // Terms need to be associated with a post in order to be returned in get_terms(). + wp_set_post_terms( + $this->factory()->post->create(), + $terms_for_current_taxonomy, + $taxonomy + ); + + $expected_links = array_merge( + $expected_links, + array_map( 'get_term_link', $terms_for_current_taxonomy ) + ); + $number_of_links = 100; + $actual_links = AMP_CLI::get_taxonomy_links( $taxonomy, 0, $number_of_links ); + + // The get_terms() call in get_taxonomy_links() returns an array with a first index of 1, so correct for that with array_values(). + $this->assertEquals( $expected_links, array_values( $actual_links ) ); + $this->assertLessThan( $number_of_links, count( $actual_links ) ); + + $number_of_links = 5; + $offset = 10; + $actual_links_using_offset = AMP_CLI::get_taxonomy_links( $taxonomy, $offset, $number_of_links ); + $this->assertEquals( array_slice( $expected_links, $offset, $number_of_links ), array_values( $actual_links_using_offset ) ); + $this->assertEquals( $number_of_links, count( $actual_links_using_offset ) ); + } + } + + /** + * Test get_author_page_urls. + * + * @covers AMP_CLI::get_author_page_urls() + */ + public function test_get_author_page_urls() { + $this->factory()->user->create(); + $users = get_users(); + $first_author = $users[0]; + $first_author_url = get_author_posts_url( $first_author->ID, $first_author->user_nicename ); + $second_author = $users[1]; + $second_author_url = get_author_posts_url( $second_author->ID, $second_author->user_nicename ); + + // Passing 0 as the offset argument should get the first author. + $this->assertEquals( array( $first_author_url ), $actual_urls = AMP_CLI::get_author_page_urls( 0, 1 ) ); + + // Passing 1 as the offset argument should get the second author. + $this->assertEquals( array( $second_author_url ), $actual_urls = AMP_CLI::get_author_page_urls( 1, 1 ) ); + + // If $include_conditionals is set and does not have is_author, this should not return a URL. + AMP_CLI::$include_conditionals = array( 'is_category' ); + $this->assertEquals( array(), AMP_CLI::get_author_page_urls() ); + + // If $include_conditionals is set and has is_author, this should return URLs. + AMP_CLI::$include_conditionals = array( 'is_author' ); + $this->assertEquals( + array( $first_author_url, $second_author_url ), + AMP_CLI::get_author_page_urls() + ); + AMP_CLI::$include_conditionals = array(); + } + + /** + * Test get_search_page. + * + * @covers AMP_CLI::get_search_page() + */ + public function test_get_search_page() { + // Normally, this should return a string, unless the user has opted out of the search template. + $this->assertTrue( is_string( AMP_CLI::get_search_page() ) ); + + // If $include_conditionals is set and does not have is_search, this should not return a URL. + AMP_CLI::$include_conditionals = array( 'is_author' ); + $this->assertEquals( null, AMP_CLI::get_search_page() ); + + // If $include_conditionals has is_search, this should return a URL. + AMP_CLI::$include_conditionals = array( 'is_search' ); + $this->assertTrue( is_string( AMP_CLI::get_search_page() ) ); + AMP_CLI::$include_conditionals = array(); + } + + /** + * Test get_date_page. + * + * @covers AMP_CLI::get_date_page() + */ + public function test_get_date_page() { + $year = date( 'Y' ); + + // Normally, this should return the date page, unless the user has opted out of that template. + $this->assertContains( $year, AMP_CLI::get_date_page() ); + + // If $include_conditionals is set and does not have is_date, this should not return a URL. + AMP_CLI::$include_conditionals = array( 'is_search' ); + $this->assertEquals( null, AMP_CLI::get_date_page() ); + + // If $include_conditionals has is_date, this should return a URL. + AMP_CLI::$include_conditionals = array( 'is_date' ); + $parsed_page_url = wp_parse_url( AMP_CLI::get_date_page() ); + $this->assertContains( $year, $parsed_page_url['query'] ); + AMP_CLI::$include_conditionals = array(); + } + + /** + * Test crawl_site. + * + * @covers AMP_CLI::crawl_site() + */ + public function test_validate_site() { + $number_of_posts = 20; + $number_of_terms = 30; + $posts = array(); + $post_permalinks = array(); + $terms = array(); + + for ( $i = 0; $i < $number_of_posts; $i++ ) { + $post_id = $this->factory()->post->create(); + $posts[] = $post_id; + $post_permalinks[] = get_permalink( $post_id ); + } + AMP_CLI::crawl_site(); + + // All of the posts created above should be present in $validated_urls. + $this->assertEmpty( array_diff( $post_permalinks, self::get_validated_urls() ) ); + + for ( $i = 0; $i < $number_of_terms; $i++ ) { + $terms[] = $this->factory()->category->create(); + } + + // Terms need to be associated with a post in order to be returned in get_terms(). + wp_set_post_terms( $posts[0], $terms, 'category' ); + AMP_CLI::crawl_site(); + $expected_validated_urls = array_map( 'get_term_link', $terms ); + $actual_validated_urls = self::get_validated_urls(); + + // All of the terms created above should be present in $validated_urls. + $this->assertEmpty( array_diff( $expected_validated_urls, $actual_validated_urls ) ); + $this->assertTrue( in_array( home_url( '/' ), self::get_validated_urls(), true ) ); + } + + /** + * Test validate_and_store_url. + * + * @covers AMP_CLI::validate_and_store_url() + */ + public function test_validate_and_store_url() { + $single_post_permalink = get_permalink( $this->factory()->post->create() ); + AMP_CLI::validate_and_store_url( $single_post_permalink, 'post' ); + $this->assertTrue( in_array( $single_post_permalink, self::get_validated_urls(), true ) ); + + $number_of_posts = 30; + $post_permalinks = array(); + + for ( $i = 0; $i < $number_of_posts; $i++ ) { + $permalink = get_permalink( $this->factory()->post->create() ); + $post_permalinks[] = $permalink; + AMP_CLI::validate_and_store_url( $permalink, 'post' ); + } + + // All of the posts created should be present in the validated URLs. + $this->assertEmpty( array_diff( $post_permalinks, self::get_validated_urls() ) ); + } + + /** + * Gets the initial count of URLs on the site. + * + * @return int The initial count of URLs. + */ + public function get_inital_url_count() { + $total_count = 'posts' === get_option( 'show_on_front' ) ? 1 : 0; + $post_query = new WP_Query( array( 'post_type' => get_post_types( array( 'public' => true ), 'names' ) ) ); + $total_count += $post_query->found_posts; + + $term_query = new WP_Term_Query( array( + 'taxonomy' => get_taxonomies( array( 'public' => true ) ), + 'fields' => 'ids', + ) ); + + $total_count += count( $term_query->terms ); + $total_count += count( AMP_CLI::get_author_page_urls() ); + $total_count += is_string( AMP_CLI::get_search_page() ) ? 1 : 0; + $total_count += is_string( AMP_CLI::get_date_page() ) ? 1 : 0; + + return $total_count; + } + + /** + * Gets all of the validated URLs. + * + * @return string[] $urls The validated URLs. + */ + public function get_validated_urls() { + $query = new WP_Query( array( + 'post_type' => AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG, + 'posts_per_page' => 100, + 'fields' => 'ids', + ) ); + + return array_map( + function( $post ) { + return remove_query_arg( 'amp', AMP_Invalid_URL_Post_Type::get_url_from_post( $post ) ); + }, + $query->posts + ); + } + + /** + * Adds the AMP_VALIDATION_RESULTS: comment to the body. + * + * @return array The response, with a comment in the body. + */ + public function add_comment() { + $mock_validation_results = array( + array( + 'error' => array( + 'code' => 'foo', + ), + 'sanitized' => false, + ), + ); + + return array( + 'body' => sprintf( + '', + 'AMP_VALIDATION_RESULTS:' . wp_json_encode( $mock_validation_results ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'ok', + ), + ); + } +} diff --git a/tests/test-class-amp-theme-support.php b/tests/test-class-amp-theme-support.php index 527708a9989..13f9c1425b6 100644 --- a/tests/test-class-amp-theme-support.php +++ b/tests/test-class-amp-theme-support.php @@ -140,6 +140,13 @@ public function test_read_theme_support_and_support_args() { AMP_Theme_Support::read_theme_support(); $this->assertTrue( AMP_Theme_Support::is_support_added_via_option() ); $this->assertTrue( current_theme_supports( 'amp' ) ); + + remove_theme_support( 'amp' ); + AMP_Options_Manager::update_option( 'theme_support', 'disabled' ); + $_GET[ AMP_Validation_Manager::VALIDATE_QUERY_VAR ] = AMP_Validation_Manager::get_amp_validate_nonce(); + AMP_Theme_Support::read_theme_support(); + $this->assertTrue( AMP_Theme_Support::is_support_added_via_option() ); + $this->assertTrue( get_theme_support( 'amp' ) ); } /** diff --git a/tests/validation/test-class-amp-invalid-url-post-type.php b/tests/validation/test-class-amp-invalid-url-post-type.php index c2ab90ae52e..9d53e0d908a 100644 --- a/tests/validation/test-class-amp-invalid-url-post-type.php +++ b/tests/validation/test-class-amp-invalid-url-post-type.php @@ -30,6 +30,7 @@ public function tearDown() { * @covers \AMP_Invalid_URL_Post_Type::add_admin_hooks() */ public function test_register() { + add_theme_support( 'amp' ); $this->assertFalse( is_admin() ); AMP_Invalid_URL_Post_Type::register(); @@ -51,6 +52,27 @@ public function test_register() { $this->assertContains( AMP_Invalid_URL_Post_Type::REMAINING_ERRORS, wp_removable_query_args() ); } + /** + * Test should_show_in_menu. + * + * @covers AMP_Invalid_URL_Post_Type::should_show_in_menu() + */ + public function test_should_show_in_menu() { + global $pagenow; + add_theme_support( 'amp' ); + $this->assertTrue( AMP_Invalid_URL_Post_Type::should_show_in_menu() ); + + remove_theme_support( 'amp' ); + $this->assertFalse( AMP_Invalid_URL_Post_Type::should_show_in_menu() ); + + $pagenow = 'edit.php'; // WPCS: override ok. + $_GET['post_type'] = 'post'; + $this->assertFalse( AMP_Invalid_URL_Post_Type::should_show_in_menu() ); + + $_GET['post_type'] = AMP_Invalid_URL_Post_Type::POST_TYPE_SLUG; + $this->assertTrue( AMP_Invalid_URL_Post_Type::should_show_in_menu() ); + } + /** * Test add_admin_hooks. * diff --git a/tests/validation/test-class-amp-validation-error-taxonomy.php b/tests/validation/test-class-amp-validation-error-taxonomy.php index 4d625b0f48a..d18d0618f4d 100644 --- a/tests/validation/test-class-amp-validation-error-taxonomy.php +++ b/tests/validation/test-class-amp-validation-error-taxonomy.php @@ -44,6 +44,7 @@ public function tearDown() { */ public function test_register() { global $wp_taxonomies; + add_theme_support( 'amp' ); AMP_Validation_Error_Taxonomy::register(); $taxonomy_object = $wp_taxonomies[ AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG ]; @@ -79,6 +80,27 @@ public function test_register() { $this->assertEquals( 'Most Used Validation Errors', $labels->most_used ); } + /** + * Test should_show_in_menu. + * + * @covers AMP_Validation_Error_Taxonomy::should_show_in_menu() + */ + public function test_should_show_in_menu() { + global $pagenow; + add_theme_support( 'amp' ); + $this->assertTrue( AMP_Validation_Error_Taxonomy::should_show_in_menu() ); + + remove_theme_support( 'amp' ); + $this->assertFalse( AMP_Validation_Error_Taxonomy::should_show_in_menu() ); + + $pagenow = 'edit-tags.php'; // WPCS: override ok. + $_GET['taxonomy'] = 'post_tag'; + $this->assertFalse( AMP_Validation_Error_Taxonomy::should_show_in_menu() ); + + $_GET['taxonomy'] = AMP_Validation_Error_Taxonomy::TAXONOMY_SLUG; + $this->assertTrue( AMP_Validation_Error_Taxonomy::should_show_in_menu() ); + } + /** * Test prepare_validation_error_taxonomy_term. * @@ -315,6 +337,8 @@ public function test_summarize_validation_errors() { * @covers \AMP_Validation_Error_Taxonomy::add_admin_hooks() */ public function test_add_admin_hooks() { + add_theme_support( 'amp' ); + AMP_Validation_Error_Taxonomy::register(); // add_group_terms_clauses_filter() needs the screen to be set. set_current_screen( 'front' );