diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 36351bdaa0d..a8743e54722 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -1914,7 +1914,7 @@ public static function prepare_response( $response, $args = [] ) { } $did_redirect = $status_code >= 300 && $status_code < 400 && $sent_location_header; - if ( AMP_Validation_Manager::$is_validate_request && ! $did_redirect ) { + if ( AMP_Validation_Manager::is_validate_request() && ! $did_redirect ) { if ( ! headers_sent() ) { status_header( 400 ); header( 'Content-Type: application/json; charset=utf-8' ); @@ -1973,7 +1973,7 @@ public static function prepare_response( $response, $args = [] ) { $dom = Document::fromHtml( $response, Options::DEFAULTS ); - if ( AMP_Validation_Manager::$is_validate_request ) { + if ( AMP_Validation_Manager::is_validate_request() ) { AMP_Validation_Manager::remove_illegal_source_stack_comments( $dom ); } @@ -2029,18 +2029,12 @@ public static function prepare_response( $response, $args = [] ) { do_action( 'amp_server_timing_stop', 'amp_sanitizer' ); // Respond early with results if performing a validate request. - if ( AMP_Validation_Manager::$is_validate_request ) { - status_header( 200 ); - header( 'Content-Type: application/json; charset=utf-8' ); - $data = [ - 'http_status_code' => $status_code, - 'php_fatal_error' => false, - ]; - if ( $last_error && in_array( $last_error['type'], [ E_ERROR, E_RECOVERABLE_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_PARSE ], true ) ) { - $data['php_fatal_error'] = $last_error; - } - $data = array_merge( $data, AMP_Validation_Manager::get_validate_response_data( $sanitization_results ) ); - return wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); + if ( AMP_Validation_Manager::is_validate_request() ) { + return AMP_Validation_Manager::send_validate_response( + $sanitization_results, + $status_code, + $last_error + ); } /** diff --git a/includes/validation/class-amp-validated-url-post-type.php b/includes/validation/class-amp-validated-url-post-type.php index a96ed3154b8..012a3d1e1bb 100644 --- a/includes/validation/class-amp-validated-url-post-type.php +++ b/includes/validation/class-amp-validated-url-post-type.php @@ -765,7 +765,7 @@ public static function normalize_url_for_storage( $url ) { // Query args to be removed from validated URLs. $removable_query_vars = array_merge( wp_removable_query_args(), - [ 'preview_id', 'preview_nonce', 'preview', QueryVar::NOAMP ] + [ 'preview_id', 'preview_nonce', 'preview', QueryVar::NOAMP, AMP_Validation_Manager::VALIDATE_QUERY_VAR ] ); // Normalize query args, removing all that are not recognized or which are removable. diff --git a/includes/validation/class-amp-validation-manager.php b/includes/validation/class-amp-validation-manager.php index 40a97aa9558..c130c03d601 100644 --- a/includes/validation/class-amp-validation-manager.php +++ b/includes/validation/class-amp-validation-manager.php @@ -7,6 +7,7 @@ use AmpProject\AmpWP\DevTools\UserAccess; use AmpProject\AmpWP\Icon; +use AmpProject\AmpWP\Option; use AmpProject\AmpWP\QueryVar; use AmpProject\AmpWP\Sandboxing; use AmpProject\AmpWP\Services; @@ -30,6 +31,42 @@ class AMP_Validation_Manager { */ const VALIDATE_QUERY_VAR = 'amp_validate'; + /** + * Key for amp_validate query var array for nonce to authorize validation. + * + * @var string + */ + const VALIDATE_QUERY_VAR_NONCE = 'nonce'; + + /** + * Key for amp_validate query var array for whether to store the validation results in an amp_validated_url post. + * + * @var string + */ + const VALIDATE_QUERY_VAR_CACHE = 'cache'; + + /** + * Key for amp_validate query var array for whether to return previously-stored the validation results if an + * amp_validated_url post exists for the URL and it is not stale. + * + * @var string + */ + const VALIDATE_QUERY_VAR_CACHED_IF_FRESH = 'cached_if_fresh'; + + /** + * Key for amp_validate query var array for whether to omit stylesheets data. + * + * @var string + */ + const VALIDATE_QUERY_VAR_OMIT_STYLESHEETS = 'omit_stylesheets'; + + /** + * Key for amp_validate query var array to bust the cache. + * + * @var string + */ + const VALIDATE_QUERY_VAR_CACHE_BUST = 'cache_bust'; + /** * Meta capability for validation. * @@ -55,13 +92,6 @@ class AMP_Validation_Manager { */ const VALIDATION_ERROR_TERM_STATUS_QUERY_VAR = 'amp_validation_error_term_status'; - /** - * Query var for cache-busting. - * - * @var string - */ - const CACHE_BUST_QUERY_VAR = 'amp_cache_bust'; - /** * Transient key to store validation errors when activating a plugin. * @@ -170,7 +200,7 @@ class AMP_Validation_Manager { * * @var bool */ - public static $is_validate_request = false; + protected static $is_validate_request = false; /** * Overrides for validation errors. @@ -222,7 +252,122 @@ static function() { add_action( 'all_admin_notices', [ __CLASS__, 'print_plugin_notice' ] ); add_action( 'admin_bar_menu', [ __CLASS__, 'add_admin_bar_menu_items' ], 101 ); add_action( 'wp', [ __CLASS__, 'maybe_fail_validate_request' ] ); + add_action( 'wp', [ __CLASS__, 'maybe_send_cached_validate_response' ], 20 ); add_action( 'wp', [ __CLASS__, 'override_validation_error_statuses' ] ); + + // Allow query parameter to force a response to be served with Standard mode (AMP-first). This query parameter + // is only honored when doing a validation request or when the user is able to do validation. This is used as + // part of Site Scanning in order to determine if the primary theme is suitable for serving AMP. + if ( ! amp_is_canonical() ) { + add_filter( + 'option_' . AMP_Options_Manager::OPTION_NAME, + [ __CLASS__, 'filter_options_for_standard_mode_when_amp_first_override' ] + ); + } + } + + /** + * Filter AMP options to set Standard template mode if it is an AMP-override request. + * + * @param array $options Options. + * @return array Filtered options. + */ + public static function filter_options_for_standard_mode_when_amp_first_override( $options ) { + if ( self::is_amp_first_override_request() ) { + $options[ Option::THEME_SUPPORT ] = AMP_Theme_Support::STANDARD_MODE_SLUG; + } + return $options; + } + + /** + * Determine whether the request includes the AMP-first override. + * + * The logic in here is admittedly a mess. It was first worked out in the context of the Web Stories plugin to + * force a single web story to be served without any paired endpoint when a site is running the AMP plugin in + * a paired template mode (Transitional or Reader). + * + * @since 2.2 + * @see \Google\Web_Stories\Integrations\AMP::get_request_post_type() + * @link https://github.com/google/web-stories-wp/pull/3621 + * + * @return bool Whether + */ + private static function is_amp_first_override_request() { + // phpcs:disable WordPress.Security.NonceVerification.Recommended + + // Frontend. + if ( + isset( $_GET[ QueryVar::AMP_FIRST ] ) + && + ( self::is_validate_request() || self::has_cap() ) + ) { + return true; + } + + // If not in the admin or the user doesn't have the validate capability, then abort. + if ( ! is_admin() || ! self::has_cap() ) { + return false; + } + + // Admin request for validation. + if ( + isset( $_GET['action'] ) + && + self::VALIDATE_QUERY_VAR === $_GET['action'] + && + ( + // First admin request to validate a URL. + ( + isset( $_GET['url'] ) + && + self::is_amp_first_override_url( esc_url_raw( $_GET['url'] ) ) + ) + || + // Subsequent admin request to validate a URL. + ( + isset( $_GET['post'] ) + && + get_post_type( (int) $_GET['post'] ) === AMP_Validated_URL_Post_Type::POST_TYPE_SLUG + && + self::is_amp_first_override_url( get_post( (int) $_GET['post'] )->post_title ) + ) + ) + ) { + return true; + } + + // Admin screen for validated URL screen and Validated URLs post list table (where this may only return true + // selectively based on the current post in the loop). + $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; + if ( + $current_screen instanceof WP_Screen + && + AMP_Validated_URL_Post_Type::POST_TYPE_SLUG === $current_screen->post_type + && + self::is_amp_first_override_url( get_post()->post_title ) + ) { + return true; + } + + // phpcs:enable WordPress.Security.NonceVerification.Recommended + return false; + } + + /** + * Determine whether the URL includes the AMP-first override query var. + * + * @since 2.2 + * + * @param string $url URL. + * @return bool Whether the URL has the AMP-first override. + */ + private static function is_amp_first_override_url( $url ) { + $query_string = wp_parse_url( $url, PHP_URL_QUERY ); + $query_vars = []; + if ( $query_string ) { + wp_parse_str( $query_string, $query_vars ); + } + return array_key_exists( QueryVar::AMP_FIRST, $query_vars ); } /** @@ -485,7 +630,7 @@ public static function override_validation_error_statuses() { * @since 2.1 */ public static function maybe_fail_validate_request() { - if ( ! self::$is_validate_request || amp_is_request() ) { + if ( ! self::is_validate_request() || amp_is_request() ) { return; } @@ -499,6 +644,68 @@ public static function maybe_fail_validate_request() { wp_send_json( compact( 'code', 'message' ), 400 ); } + /** + * Whether a validate request is being performed. + * + * When responding to a request to validate a URL, instead of an HTML document being returned, a JSON document is + * returned with any errors that were encountered during validation. + * + * @see AMP_Validation_Manager::get_validate_response_data() + * + * @return bool + */ + public static function is_validate_request() { + return self::$is_validate_request; + } + + /** + * Get validate request args. + * + * @return array { + * Args. + * + * @type string|null $nonce None to authorize validate request or null if none was supplied. + * @type bool $cache Whether to store results in amp_validated_url post. + * @type bool $cached_if_fresh Whether to return previously-stored results if not stale. + * @type bool $omit_stylesheets Whether to omit stylesheet data in the validate response. + * } + */ + private static function get_validate_request_args() { + $defaults = [ + self::VALIDATE_QUERY_VAR_NONCE => null, + self::VALIDATE_QUERY_VAR_CACHE => false, + self::VALIDATE_QUERY_VAR_CACHED_IF_FRESH => false, + self::VALIDATE_QUERY_VAR_OMIT_STYLESHEETS => false, + ]; + + if ( ! isset( $_GET[ self::VALIDATE_QUERY_VAR ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + return $defaults; + } + + $unsanitized_values = $_GET[ self::VALIDATE_QUERY_VAR ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + + if ( is_string( $unsanitized_values ) ) { + $unsanitized_values = [ + self::VALIDATE_QUERY_VAR_NONCE => $unsanitized_values, + ]; + } elseif ( ! is_array( $unsanitized_values ) ) { + return $defaults; + } + + $args = $defaults; + foreach ( $unsanitized_values as $key => $unsanitized_value ) { + switch ( $key ) { + case self::VALIDATE_QUERY_VAR_NONCE: + $args[ $key ] = sanitize_key( $unsanitized_value ); + break; + default: + $args[ $key ] = rest_sanitize_boolean( $unsanitized_value ); + } + } + + return $args; + } + /** * Initialize a validate request. * @@ -632,7 +839,7 @@ public static function add_validation_error( array $error, array $data = [] ) { $node = $data['node']; } - if ( self::$is_validate_request ) { + if ( self::is_validate_request() ) { if ( ! empty( $error['sources'] ) ) { $sources = $error['sources']; } elseif ( $node ) { @@ -1479,12 +1686,13 @@ public static function get_amp_validate_nonce() { * validate response should be served. */ public static function should_validate_response() { - if ( ! isset( $_GET[ self::VALIDATE_QUERY_VAR ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args = self::get_validate_request_args(); + + if ( null === $args[ self::VALIDATE_QUERY_VAR_NONCE ] ) { return false; } - $validate_key = wp_unslash( $_GET[ self::VALIDATE_QUERY_VAR ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( ! hash_equals( self::get_amp_validate_nonce(), $validate_key ) ) { + if ( ! hash_equals( self::get_amp_validate_nonce(), $args[ self::VALIDATE_QUERY_VAR_NONCE ] ) ) { return new WP_Error( 'http_request_failed', __( 'Nonce authentication failed.', 'amp' ) @@ -1583,6 +1791,144 @@ public static function remove_illegal_source_stack_comments( Document $dom ) { } } + /** + * Send validate response. + * + * @since 2.2 + * @see AMP_Theme_Support::prepare_response() + * + * @param array $sanitization_results Sanitization results. + * @param int $status_code Status code. + * @param array|null $last_error Last error. + * @return string JSON. + */ + public static function send_validate_response( $sanitization_results, $status_code, $last_error ) { + status_header( 200 ); + if ( ! headers_sent() ) { + header( 'Content-Type: application/json; charset=utf-8' ); + } + $data = [ + 'http_status_code' => $status_code, + 'php_fatal_error' => false, + ]; + if ( $last_error && in_array( $last_error['type'], [ E_ERROR, E_RECOVERABLE_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_PARSE ], true ) ) { + $data['php_fatal_error'] = $last_error; + } + $data = array_merge( $data, self::get_validate_response_data( $sanitization_results ) ); + + $args = self::get_validate_request_args(); + + $data['revalidated'] = true; + + if ( $args[ self::VALIDATE_QUERY_VAR_CACHE ] ) { + $validation_errors = wp_list_pluck( $data['results'], 'error' ); + + $validated_url_post_id = AMP_Validated_URL_Post_Type::store_validation_errors( + $validation_errors, + amp_get_current_url(), + $data + ); + if ( is_wp_error( $validated_url_post_id ) ) { + status_header( 500 ); + return wp_json_encode( + [ + 'code' => $validated_url_post_id->get_error_code(), + 'message' => $validated_url_post_id->get_error_message(), + ] + ); + } else { + status_header( 201 ); + $data['validated_url_post'] = [ + 'id' => $validated_url_post_id, + 'edit_link' => get_edit_post_link( $validated_url_post_id, 'raw' ), + ]; + } + } + + if ( $args[ self::VALIDATE_QUERY_VAR_OMIT_STYLESHEETS ] ) { + unset( $data['stylesheets'] ); + } + + $data['url'] = remove_query_arg( self::VALIDATE_QUERY_VAR, $data['url'] ); + + return wp_json_encode( $data, JSON_UNESCAPED_SLASHES ); + } + + /** + * Send cached validate response if it is requested and available. + * + * When a validate request is made with the `amp_validate[cached_if_fresh]=true` query parameter, before a page + * begins rendering a check is made for whether there is already an `amp_validated_url` post for the current URL. + * If there is, and the post is not stale, then the previous results are returned without re-rendering page and + * obtaining the validation data. + */ + public static function maybe_send_cached_validate_response() { + if ( ! self::is_validate_request() ) { + return; + } + $args = self::get_validate_request_args(); + + if ( ! $args[ self::VALIDATE_QUERY_VAR_CACHED_IF_FRESH ] ) { + return; + } + + $post = AMP_Validated_URL_Post_Type::get_invalid_url_post( amp_get_current_url() ); + if ( ! ( $post instanceof WP_Post ) ) { + return; + } + + $staleness = AMP_Validated_URL_Post_Type::get_post_staleness( $post ); + if ( count( $staleness ) > 0 ) { + return; + } + + $response = [ + 'http_status_code' => 200, // Note: This is not currently cached in postmeta. + 'php_fatal_error' => false, + 'results' => [], + 'queried_object' => null, + 'url' => null, + 'revalidated' => false, // Since cached was used. + 'validated_url_post' => [ + 'id' => $post->ID, + 'edit_link' => get_edit_post_link( $post->ID, 'raw' ), + ], + ]; + + if ( ! $args[ self::VALIDATE_QUERY_VAR_OMIT_STYLESHEETS ] ) { + $stylesheets = get_post_meta( $post->ID, AMP_Validated_URL_Post_Type::STYLESHEETS_POST_META_KEY, true ); + if ( $stylesheets ) { + $response['stylesheets'] = json_decode( $stylesheets, true ); + } + } + + $stored_validation_errors = json_decode( $post->post_content, true ); + if ( is_array( $stored_validation_errors ) ) { + $response['results'] = array_map( + static function ( $stored_validation_error ) { + $error = $stored_validation_error['data']; + $sanitized = AMP_Validation_Error_Taxonomy::is_validation_error_sanitized( $error ); + return compact( 'error', 'sanitized' ); + }, + $stored_validation_errors + ); + } + + $queried_object = get_post_meta( $post->ID, AMP_Validated_URL_Post_Type::QUERIED_OBJECT_POST_META_KEY, true ); + if ( $queried_object ) { + $response['queried_object'] = $queried_object; + } + + $php_fatal_error = get_post_meta( $post->ID, AMP_Validated_URL_Post_Type::PHP_FATAL_ERROR_POST_META_KEY, true ); + if ( $php_fatal_error ) { + $response['php_fatal_error'] = $php_fatal_error; + } + + $response['url'] = AMP_Validated_URL_Post_Type::get_url_from_post( $post ); + + wp_send_json( $response, 200, JSON_UNESCAPED_SLASHES ); + } + /** * Finalize validation. * @@ -1774,7 +2120,7 @@ public static function filter_sanitizer_args( $sanitizers ) { } if ( isset( $sanitizers[ AMP_Style_Sanitizer::class ] ) ) { - $sanitizers[ AMP_Style_Sanitizer::class ]['should_locate_sources'] = self::$is_validate_request; + $sanitizers[ AMP_Style_Sanitizer::class ]['should_locate_sources'] = self::is_validate_request(); $css_validation_errors = []; foreach ( self::$validation_error_status_overrides as $slug => $status ) { @@ -1867,8 +2213,10 @@ public static function validate_url( $url ) { } $added_query_vars = [ - self::VALIDATE_QUERY_VAR => self::get_amp_validate_nonce(), - self::CACHE_BUST_QUERY_VAR => wp_rand(), + self::VALIDATE_QUERY_VAR => [ + self::VALIDATE_QUERY_VAR_NONCE => self::get_amp_validate_nonce(), + self::VALIDATE_QUERY_VAR_CACHE_BUST => wp_rand(), + ], ]; // Ensure the URL to be validated is on the site. diff --git a/src/QueryVar.php b/src/QueryVar.php index a208329890d..83564806bb3 100644 --- a/src/QueryVar.php +++ b/src/QueryVar.php @@ -31,6 +31,17 @@ interface QueryVar { */ const AMP = 'amp'; + /** + * Query var which overrides the template mode to be Standard (aka AMP-first aka canonical). + * + * This is only honored during validation requests or if the user has validation capability. + * + * @see \AMP_Validation_Manager::is_validate_request() + * @see \AMP_Validation_Manager::has_cap() + * @var string + */ + const AMP_FIRST = 'amp-first'; + /** * Query var used to signal the request for an non-AMP page. * diff --git a/tests/php/src/PluginSuppressionTest.php b/tests/php/src/PluginSuppressionTest.php index db780110723..751797cc248 100644 --- a/tests/php/src/PluginSuppressionTest.php +++ b/tests/php/src/PluginSuppressionTest.php @@ -45,11 +45,13 @@ public function setUp() { add_filter( 'pre_http_request', function( $r, /** @noinspection PhpUnusedParameterInspection */ $args, $url ) { - if ( false === strpos( $url, 'amp_validate=' ) ) { + $url_query_vars = []; + parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $url_query_vars ); + if ( ! array_key_exists( AMP_Validation_Manager::VALIDATE_QUERY_VAR, $url_query_vars ) ) { return $r; } - $this->attempted_validate_request_urls[] = remove_query_arg( [ 'amp_validate', 'amp_cache_bust' ], $url ); + $this->attempted_validate_request_urls[] = remove_query_arg( AMP_Validation_Manager::VALIDATE_QUERY_VAR, $url ); return [ 'body' => '', 'response' => [ diff --git a/tests/php/test-amp-helper-functions.php b/tests/php/test-amp-helper-functions.php index 0a86fba2719..4074ac7f2ef 100644 --- a/tests/php/test-amp-helper-functions.php +++ b/tests/php/test-amp-helper-functions.php @@ -9,6 +9,7 @@ use AmpProject\AmpWP\QueryVar; use AmpProject\AmpWP\Tests\Helpers\HandleValidation; use AmpProject\AmpWP\Tests\Helpers\LoadsCoreThemes; +use AmpProject\AmpWP\Tests\Helpers\PrivateAccess; use AmpProject\AmpWP\Tests\DependencyInjectedTestCase; use AmpProject\AmpWP\AmpSlugCustomizationWatcher; @@ -19,6 +20,7 @@ class Test_AMP_Helper_Functions extends DependencyInjectedTestCase { use HandleValidation; use LoadsCoreThemes; + use PrivateAccess; /** * The mock Site Icon value to use in a filter. @@ -50,7 +52,7 @@ public function setUp() { */ public function tearDown() { AMP_Options_Manager::update_option( Option::THEME_SUPPORT, AMP_Theme_Support::READER_MODE_SLUG ); - AMP_Validation_Manager::$is_validate_request = false; + $this->set_private_property( AMP_Validation_Manager::class, 'is_validate_request', false ); global $wp_scripts, $pagenow, $show_admin_bar, $current_screen; $wp_scripts = null; $show_admin_bar = null; diff --git a/tests/php/test-class-amp-base-sanitizer.php b/tests/php/test-class-amp-base-sanitizer.php index 4fb54c5996f..2303b3d9255 100644 --- a/tests/php/test-class-amp-base-sanitizer.php +++ b/tests/php/test-class-amp-base-sanitizer.php @@ -34,7 +34,7 @@ public function setUp() { public function tearDown() { parent::tearDown(); AMP_Validation_Manager::reset_validation_results(); - AMP_Validation_Manager::$is_validate_request = false; + $this->set_private_property( AMP_Validation_Manager::class, 'is_validate_request', false ); } /** diff --git a/tests/php/test-class-amp-theme-support.php b/tests/php/test-class-amp-theme-support.php index 31b54764439..621b3c5c502 100644 --- a/tests/php/test-class-amp-theme-support.php +++ b/tests/php/test-class-amp-theme-support.php @@ -81,7 +81,7 @@ public function tearDown() { parent::tearDown(); unset( $GLOBALS['show_admin_bar'] ); - AMP_Validation_Manager::$is_validate_request = false; + $this->set_private_property( AMP_Validation_Manager::class, 'is_validate_request', false ); AMP_Validation_Manager::reset_validation_results(); $this->set_template_mode( AMP_Theme_Support::READER_MODE_SLUG ); remove_theme_support( 'custom-header' ); @@ -1956,18 +1956,84 @@ public function test_prepare_response_for_submitted_form() { } /** - * Test prepare_response when validating an invalid AMP page. + * Test prepare_response when validating a non-AMP page. * * @covers AMP_Theme_Support::prepare_response() */ - public function test_prepare_response_for_validating_invalid_amp_page() { - AMP_Validation_Manager::$is_validate_request = true; + public function test_prepare_response_for_validating_non_amp_page() { + $this->set_private_property( AMP_Validation_Manager::class, 'is_validate_request', true ); $response = AMP_Theme_Support::prepare_response( '' ); $this->assertJson( $response ); $this->assertStringContainsString( 'RENDERED_PAGE_NOT_AMP', $response ); } + /** @return array */ + public function get_data_to_test_prepare_response_for_validating_amp_page() { + return [ + 'no-store' => [ + 'args' => [ + AMP_Validation_Manager::VALIDATE_QUERY_VAR_NONCE => AMP_Validation_Manager::get_amp_validate_nonce(), + ], + ], + 'store' => [ + 'args' => [ + AMP_Validation_Manager::VALIDATE_QUERY_VAR_NONCE => AMP_Validation_Manager::get_amp_validate_nonce(), + AMP_Validation_Manager::VALIDATE_QUERY_VAR_CACHE => true, + ], + ], + 'store_but_omit_styleshets' => [ + 'args' => [ + AMP_Validation_Manager::VALIDATE_QUERY_VAR_NONCE => AMP_Validation_Manager::get_amp_validate_nonce(), + AMP_Validation_Manager::VALIDATE_QUERY_VAR_CACHE => true, + AMP_Validation_Manager::VALIDATE_QUERY_VAR_OMIT_STYLESHEETS => true, + ], + ], + ]; + } + + /** + * Test prepare_response when validating an AMP page. + * + * @dataProvider get_data_to_test_prepare_response_for_validating_amp_page + * @covers AMP_Theme_Support::prepare_response() + * @covers AMP_Validation_Manager::send_validate_response() + */ + public function test_prepare_response_for_validating_amp_page( $args ) { + wp_set_current_user( self::factory()->user->create( [ 'role' => 'administrator' ] ) ); + $this->set_template_mode( AMP_Theme_Support::STANDARD_MODE_SLUG ); + $this->go_to( '/' ); + + $_GET[ AMP_Validation_Manager::VALIDATE_QUERY_VAR ] = $args; + AMP_Validation_Manager::init_validate_request(); + AMP_Theme_Support::finish_init(); + $response = AMP_Theme_Support::prepare_response( '