diff --git a/.eslintrc b/.eslintrc index 2ec892c61af..54d6396fbf6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,6 +5,9 @@ "plugin:import/recommended", "plugin:eslint-comments/recommended" ], + "env": { + "browser": true + }, "rules": { "block-scoped-var": "error", "complexity": ["error", { "max": 20 } ], diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index cd9765b85cc..2e9a22ea927 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -14,6 +14,7 @@ 'admin.options_menu' => \AmpProject\AmpWP\Admin\OptionsMenu::class, 'admin.polyfills' => \AmpProject\AmpWP\Admin\Polyfills::class, 'admin.paired_browsing' => \AmpProject\AmpWP\Admin\PairedBrowsing::class, + 'admin.validation_counts' => \AmpProject\AmpWP\Admin\ValidationCounts::class, 'amp_slug_customization_watcher' => \AmpProject\AmpWP\AmpSlugCustomizationWatcher::class, 'css_transient_cache.ajax_handler' => \AmpProject\AmpWP\Admin\ReenableCssTransientCachingAjaxAction::class, 'css_transient_cache.monitor' => \AmpProject\AmpWP\BackgroundTask\MonitorCssTransientCaching::class, @@ -33,6 +34,7 @@ 'plugin_suppression' => \AmpProject\AmpWP\PluginSuppression::class, 'reader_theme_loader' => \AmpProject\AmpWP\ReaderThemeLoader::class, 'rest.options_controller' => \AmpProject\AmpWP\OptionsRESTController::class, + 'rest.validation_counts_controller' => \AmpProject\AmpWP\Validation\ValidationCountsRestController::class, 'server_timing' => \AmpProject\AmpWP\Instrumentation\ServerTiming::class, 'site_health_integration' => \AmpProject\AmpWP\Admin\SiteHealth::class, 'validated_url_stylesheet_gc' => \AmpProject\AmpWP\BackgroundTask\ValidatedUrlStylesheetDataGarbageCollection::class, diff --git a/assets/js/amp-service-worker-runtime-precaching.js b/assets/js/amp-service-worker-runtime-precaching.js index 8706b4474b4..ecd606a1ff1 100644 --- a/assets/js/amp-service-worker-runtime-precaching.js +++ b/assets/js/amp-service-worker-runtime-precaching.js @@ -1,4 +1,4 @@ -/* global self, caches, URLS */ +/* global URLS */ // See AMP_Service_Workers::add_amp_runtime_caching() and . { self.addEventListener( 'install', ( event ) => { diff --git a/assets/src/amp-validation/counts/index.js b/assets/src/amp-validation/counts/index.js new file mode 100644 index 00000000000..eb91e945af5 --- /dev/null +++ b/assets/src/amp-validation/counts/index.js @@ -0,0 +1,95 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import apiFetch from '@wordpress/api-fetch'; +import domReady from '@wordpress/dom-ready'; + +/** + * Internal dependencies + */ +import './style.css'; + +/** + * Updates a menu item with its count. + * + * If the count is not a number or is `0`, the element that contains the count is instead removed (as it would be no longer relevant). + * + * @param {HTMLElement} itemEl Menu item element. + * @param {number} count Count to set. + */ +function updateMenuItem( itemEl, count ) { + if ( isNaN( count ) || count === 0 ) { + itemEl.parentNode.parentNode.removeChild( itemEl.parentNode ); + } else { + itemEl.classList.remove( 'loading' ); + itemEl.textContent = count.toLocaleString(); + } +} + +/** + * Updates the 'Validated URLs' and 'Error Index' menu items with their respective unreviewed count. + * + * @param {Object} counts Counts for menu items. + * @param {number} counts.validated_urls Unreviewed validated URLs count. + * @param {number} counts.errors Unreviewed validation errors count. + */ +function updateMenuItemCounts( counts ) { + const { validated_urls: newValidatedUrlCount, errors: newErrorCount } = counts; + + const errorCountEl = document.getElementById( 'new-error-index-count' ); + if ( errorCountEl ) { + updateMenuItem( errorCountEl, newErrorCount ); + } + + const validatedUrlsCountEl = document.getElementById( 'new-validation-url-count' ); + if ( validatedUrlsCountEl ) { + updateMenuItem( validatedUrlsCountEl, newValidatedUrlCount ); + } +} + +/** + * Fetches the validation counts only when the AMP submenu is open for the first time. + * + * @param {HTMLElement} root AMP submenu item. + */ +function createObserver( root ) { + // IntersectionObserver is not available in IE11, so just hide the counts entirely for that browser. + if ( ! ( 'IntersectionObserver' in window ) ) { + updateMenuItemCounts( { validated_urls: 0, errors: 0 } ); + return; + } + + const target = root.querySelector( 'ul' ); + + const observer = new IntersectionObserver( ( [ entry ] ) => { + if ( ! entry || ! entry.isIntersecting ) { + return; + } + + observer.unobserve( target ); + + apiFetch( { path: '/amp/v1/unreviewed-validation-counts' } ).then( ( counts ) => { + updateMenuItemCounts( counts ); + } ).catch( ( error ) => { + updateMenuItemCounts( { validated_urls: 0, errors: 0 } ); + + const message = error?.message || __( 'An unknown error occurred while retrieving the validation counts', 'amp' ); + // eslint-disable-next-line no-console + console.error( `[AMP Plugin] ${ message }` ); + } ); + }, { root } ); + + observer.observe( target ); +} + +domReady( () => { + const ampMenuItem = document.getElementById( 'toplevel_page_amp-options' ); + + // Bail if the AMP submenu is not in the DOM. + if ( ! ampMenuItem ) { + return; + } + + createObserver( ampMenuItem ); +} ); diff --git a/assets/src/amp-validation/counts/style.css b/assets/src/amp-validation/counts/style.css new file mode 100644 index 00000000000..c328d626768 --- /dev/null +++ b/assets/src/amp-validation/counts/style.css @@ -0,0 +1,24 @@ +@keyframes rotate-forever { + + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +#toplevel_page_amp-options > ul > li span.loading { + animation-duration: 0.75s; + animation-iteration-count: infinite; + animation-name: rotate-forever; + animation-timing-function: linear; + height: 4px; + width: 4px; + border: 2px solid #fff; + border-right-color: transparent; + border-top-color: transparent; + border-radius: 50%; + display: inline-block; +} diff --git a/includes/validation/class-amp-validated-url-post-type.php b/includes/validation/class-amp-validated-url-post-type.php index febf7df7a94..3abf54817fd 100644 --- a/includes/validation/class-amp-validated-url-post-type.php +++ b/includes/validation/class-amp-validated-url-post-type.php @@ -5,10 +5,8 @@ * @package AMP */ -use AmpProject\AmpWP\DevTools\UserAccess; use AmpProject\AmpWP\Admin\OptionsMenu; use AmpProject\AmpWP\Icon; -use AmpProject\AmpWP\PluginRegistry; use AmpProject\AmpWP\Option; use AmpProject\AmpWP\QueryVar; use AmpProject\AmpWP\Services; @@ -270,13 +268,6 @@ public static function handle_plugin_update( $old_version ) { public static function add_admin_hooks() { add_action( 'admin_enqueue_scripts', [ __CLASS__, 'enqueue_post_list_screen_scripts' ] ); - $dev_tools_user_access = Services::get( 'dev_tools.user_access' ); - - if ( $dev_tools_user_access->is_user_enabled() ) { - add_filter( 'dashboard_glance_items', [ __CLASS__, 'filter_dashboard_glance_items' ] ); - add_action( 'rightnow_end', [ __CLASS__, 'print_dashboard_glance_styles' ] ); - } - // Edit post screen hooks. add_action( 'admin_enqueue_scripts', [ __CLASS__, 'enqueue_edit_post_screen_scripts' ] ); add_action( 'add_meta_boxes', [ __CLASS__, 'add_meta_boxes' ], PHP_INT_MAX ); @@ -490,11 +481,8 @@ public static function update_validated_url_menu_item() { $menu_name_label = get_post_type_object( self::POST_TYPE_SLUG )->labels->menu_name; $submenu_item[0] = $menu_name_label; - // Display the count of new validation errors next to the label, if there are any. - $new_validation_error_url_count = self::get_validation_error_urls_count(); - if ( 0 < $new_validation_error_url_count ) { - $submenu_item[0] .= ' ' . esc_html( number_format_i18n( $new_validation_error_url_count ) ) . ''; - } + // Append markup to display a loading spinner while the unreviewed count is being fetched. + $submenu_item[0] .= ' '; break; } } @@ -507,7 +495,7 @@ public static function update_validated_url_menu_item() { * * @return int Count of new validation error URLs. */ - protected static function get_validation_error_urls_count() { + public static function get_validation_error_urls_count() { $count = get_transient( static::NEW_VALIDATION_ERROR_URLS_COUNT_TRANSIENT ); if ( false !== $count ) { // Handle case where integer stored in transient gets returned as string when persistent object cache is not @@ -2937,69 +2925,6 @@ public static function get_recheck_url( $url_or_post ) { ); } - /** - * Filter At a Glance items add AMP Validation Errors. - * - * @param array $items At a glance items. - * @return array Items. - */ - public static function filter_dashboard_glance_items( $items ) { - $count = self::get_validation_error_urls_count(); - - if ( 0 !== $count ) { - $items[] = sprintf( - '%s', - esc_url( - admin_url( - add_query_arg( - [ - 'post_type' => self::POST_TYPE_SLUG, - AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_STATUS_QUERY_VAR => [ - AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_REJECTED_STATUS, - AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_ACCEPTED_STATUS, - ], - ], - 'edit.php' - ) - ) - ), - esc_html( - sprintf( - /* translators: %s is the validation error count */ - _n( - '%s URL w/ new AMP errors', - '%s URLs w/ new AMP errors', - $count, - 'amp' - ), - number_format_i18n( $count ) - ) - ) - ); - } - return $items; - } - - /** - * Print styles for the At a Glance widget. - */ - public static function print_dashboard_glance_styles() { - ?> - - [ self::VALIDATION_ERROR_NEW_REJECTED_STATUS, self::VALIDATION_ERROR_NEW_ACCEPTED_STATUS ], - ] - ); - if ( $new_error_count ) { - $menu_item_label .= ' ' . esc_html( number_format_i18n( $new_error_count ) ) . ''; - } + // Append markup to display a loading spinner while the unreviewed count is being fetched. + $menu_item_label .= ' '; $post_menu_slug = 'edit.php?post_type=' . AMP_Validated_URL_Post_Type::POST_TYPE_SLUG; $term_menu_slug = 'edit-tags.php?taxonomy=' . self::TAXONOMY_SLUG . '&post_type=' . AMP_Validated_URL_Post_Type::POST_TYPE_SLUG; diff --git a/src/Admin/ValidationCounts.php b/src/Admin/ValidationCounts.php new file mode 100644 index 00000000000..fe019c6e88a --- /dev/null +++ b/src/Admin/ValidationCounts.php @@ -0,0 +1,82 @@ +is_user_enabled(); + } + + /** + * Runs on instantiation. + */ + public function register() { + $this->enqueue_scripts(); + } + + /** + * Enqueue admin assets. + */ + public function enqueue_scripts() { + $asset_file = AMP__DIR__ . '/assets/js/' . self::ASSETS_HANDLE . '.asset.php'; + $asset = require $asset_file; + $dependencies = $asset['dependencies']; + $version = $asset['version']; + + wp_enqueue_script( + self::ASSETS_HANDLE, + amp_get_asset_url( 'js/' . self::ASSETS_HANDLE . '.js' ), + $dependencies, + $version, + true + ); + + wp_enqueue_style( + self::ASSETS_HANDLE, + amp_get_asset_url( 'css/' . self::ASSETS_HANDLE . '.css' ), + false, + $version + ); + } +} diff --git a/src/AmpWpPlugin.php b/src/AmpWpPlugin.php index e3f9d79d2a3..dbe83c93547 100644 --- a/src/AmpWpPlugin.php +++ b/src/AmpWpPlugin.php @@ -66,6 +66,7 @@ final class AmpWpPlugin extends ServiceBasedPlugin { 'admin.options_menu' => Admin\OptionsMenu::class, 'admin.polyfills' => Admin\Polyfills::class, 'admin.paired_browsing' => Admin\PairedBrowsing::class, + 'admin.validation_counts' => Admin\ValidationCounts::class, 'amp_slug_customization_watcher' => AmpSlugCustomizationWatcher::class, 'css_transient_cache.ajax_handler' => Admin\ReenableCssTransientCachingAjaxAction::class, 'css_transient_cache.monitor' => BackgroundTask\MonitorCssTransientCaching::class, @@ -84,6 +85,7 @@ final class AmpWpPlugin extends ServiceBasedPlugin { 'plugin_suppression' => PluginSuppression::class, 'reader_theme_loader' => ReaderThemeLoader::class, 'rest.options_controller' => OptionsRESTController::class, + 'rest.validation_counts_controller' => Validation\ValidationCountsRestController::class, 'server_timing' => Instrumentation\ServerTiming::class, 'site_health_integration' => Admin\SiteHealth::class, 'validated_url_stylesheet_gc' => BackgroundTask\ValidatedUrlStylesheetDataGarbageCollection::class, diff --git a/src/DevTools/UserAccess.php b/src/DevTools/UserAccess.php index 4d63f7bb1d9..28457939083 100644 --- a/src/DevTools/UserAccess.php +++ b/src/DevTools/UserAccess.php @@ -35,8 +35,6 @@ final class UserAccess implements Service, Registerable { /** * Runs on instantiation. - * - * @action rest_api_init */ public function register() { add_action( 'rest_api_init', [ $this, 'register_rest_field' ] ); diff --git a/src/Validation/ValidationCountsRestController.php b/src/Validation/ValidationCountsRestController.php new file mode 100644 index 00000000000..010b830c10d --- /dev/null +++ b/src/Validation/ValidationCountsRestController.php @@ -0,0 +1,140 @@ +namespace = 'amp/v1'; + $this->rest_base = 'unreviewed-validation-counts'; + $this->dev_tools_user_access = $user_access; + } + + /** + * Registers all routes for the controller. + */ + public function register() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => [ $this, 'get_items' ], + 'args' => [], + 'permission_callback' => [ $this, 'get_items_permissions_check' ], + ], + 'schema' => [ $this, 'get_public_item_schema' ], + ] + ); + } + + /** + * Checks whether the current user has access to Dev Tools. + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has permission; WP_Error object otherwise. + */ + public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $dev_tools_access = $this->dev_tools_user_access->is_user_enabled(); + + if ( ! $dev_tools_access ) { + return new WP_Error( + 'amp_rest_no_dev_tools_access', + __( 'Sorry, you are not allowed to view unreviewed counts for validation errors.', 'amp' ), + [ 'status' => rest_authorization_required_code() ] + ); + } + + return true; + } + + /** + * Retrieves total unreviewed count for validation URLs and errors. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $unreviewed_validated_url_count = AMP_Validated_URL_Post_Type::get_validation_error_urls_count(); + $unreviewed_validation_error_count = AMP_Validation_Error_Taxonomy::get_validation_error_count( + [ + 'group' => [ + AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_REJECTED_STATUS, + AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_NEW_ACCEPTED_STATUS, + ], + ] + ); + + $response = [ + 'validated_urls' => $unreviewed_validated_url_count, + 'errors' => $unreviewed_validation_error_count, + ]; + + return rest_ensure_response( $response ); + } + + /** + * Retrieves the block type' schema, conforming to JSON Schema. + * + * @return array Item schema data. + */ + public function get_item_schema() { + return [ + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'amp-wp-validation-status', + 'type' => 'object', + 'properties' => [ + 'validation_urls' => [ + 'type' => 'integer', + 'readonly' => true, + ], + 'errors' => [ + 'type' => 'integer', + 'readonly' => true, + ], + ], + ]; + } +} diff --git a/tests/php/src/Admin/ValidationCountsTest.php b/tests/php/src/Admin/ValidationCountsTest.php new file mode 100644 index 00000000000..a1051fe8657 --- /dev/null +++ b/tests/php/src/Admin/ValidationCountsTest.php @@ -0,0 +1,86 @@ +instance = new ValidationCounts(); + } + + public function test__construct() { + $this->assertInstanceOf( ValidationCounts::class, $this->instance ); + $this->assertInstanceOf( Delayed::class, $this->instance ); + $this->assertInstanceOf( Conditional::class, $this->instance ); + $this->assertInstanceOf( Service::class, $this->instance ); + $this->assertInstanceOf( Registerable::class, $this->instance ); + } + + /** + * Test ::get_registration_action(). + * + * @covers ::get_registration_action() + */ + public function test_get_registration_action() { + self::assertEquals( 'admin_enqueue_scripts', ValidationCounts::get_registration_action() ); + } + + /** + * Test ::register(). + * + * @covers ::register() + */ + public function test_register() { + $this->instance->register(); + + $this->assertTrue( wp_script_is( ValidationCounts::ASSETS_HANDLE ) ); + $this->assertTrue( wp_style_is( ValidationCounts::ASSETS_HANDLE ) ); + } + + /** + * Test ::is_needed(). + * + * @covers ::is_needed() + */ + public function test_is_needed() { + $this->assertFalse( ValidationCounts::is_needed() ); + + $admin_user = self::factory()->user->create_and_get( [ 'role' => 'administrator' ] ); + wp_set_current_user( $admin_user->ID ); + update_user_meta( $admin_user->ID, UserAccess::USER_FIELD_DEVELOPER_TOOLS_ENABLED, wp_json_encode( true ) ); + + $this->assertTrue( ValidationCounts::is_needed() ); + } +} diff --git a/tests/php/src/OptionsRESTControllerTest.php b/tests/php/src/OptionsRESTControllerTest.php index 36c58a0729c..d35531f1729 100644 --- a/tests/php/src/OptionsRESTControllerTest.php +++ b/tests/php/src/OptionsRESTControllerTest.php @@ -2,7 +2,7 @@ /** * Tests for OptionsRESTController. * - * @package AMP + * @package AmpProject\AmpWP */ namespace AmpProject\AmpWP\Tests; @@ -16,8 +16,6 @@ /** * Tests for OptionsRESTController. * - * @group amp-options - * * @coversDefaultClass \AmpProject\AmpWP\OptionsRESTController */ class OptionsRESTControllerTest extends DependencyInjectedTestCase { diff --git a/tests/php/src/Validation/ValidationCountsRestControllerTest.php b/tests/php/src/Validation/ValidationCountsRestControllerTest.php new file mode 100644 index 00000000000..65f761da755 --- /dev/null +++ b/tests/php/src/Validation/ValidationCountsRestControllerTest.php @@ -0,0 +1,113 @@ +controller = $this->injector->make( ValidationCountsRestController::class ); + } + + /** + * Test ::get_registration_action(). + * + * @covers ::get_registration_action() + */ + public function test_get_registration_action() { + self::assertEquals( 'rest_api_init', ValidationCountsRestController::get_registration_action() ); + } + + /** + * Test ::get_items_permissions_check(). + * + * @covers ::get_items_permissions_check() + */ + public function test_get_items_permissions_check() { + $admin_user = self::factory()->user->create_and_get( [ 'role' => 'administrator' ] ); + + $this->assertWPError( $this->controller->get_items_permissions_check( new WP_REST_Request( 'GET', '/amp/v1/unreviewed-validation-counts' ) ) ); + + wp_set_current_user( $admin_user->ID ); + $this->assertWPError( $this->controller->get_items_permissions_check( new WP_REST_Request( 'GET', '/amp/v1/unreviewed-validation-counts' ) ) ); + + update_user_meta( $admin_user->ID, UserAccess::USER_FIELD_DEVELOPER_TOOLS_ENABLED, wp_json_encode( true ) ); + $this->assertTrue( $this->controller->get_items_permissions_check( new WP_REST_Request( 'GET', '/amp/v1/unreviewed-validation-counts' ) ) ); + } + + /** + * Test ::get_items(). + * + * @covers ::get_items() + */ + public function test_get_items() { + $admin_user = self::factory()->user->create_and_get( [ 'role' => 'administrator' ] ); + wp_set_current_user( $admin_user->ID ); + update_user_meta( $admin_user->ID, UserAccess::USER_FIELD_DEVELOPER_TOOLS_ENABLED, wp_json_encode( true ) ); + + $request = new WP_REST_Request( 'GET', '/amp/v1/unreviewed-validation-counts' ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( + [ + 'validated_urls' => 0, + 'errors' => 0, + ], + $response->get_data() + ); + + AMP_Validated_URL_Post_Type::store_validation_errors( + [ + [ + 'code' => 'foobar', + ], + ], + get_permalink( self::factory()->post->create() ) + ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( + [ + 'validated_urls' => 1, + 'errors' => 1, + ], + $response->get_data() + ); + } + + /** + * Test ::get_item_schema(). + * + * @covers ::get_item_schema() + */ + public function test_get_item_schema() { + self::assertEquals( [ 'validation_urls', 'errors' ], array_keys( $this->controller->get_item_schema()['properties'] ) ); + } +} diff --git a/tests/php/validation/test-class-amp-validated-url-post-type.php b/tests/php/validation/test-class-amp-validated-url-post-type.php index 8b4a5b5c43b..2524ccbbe26 100644 --- a/tests/php/validation/test-class-amp-validated-url-post-type.php +++ b/tests/php/validation/test-class-amp-validated-url-post-type.php @@ -89,9 +89,6 @@ public function test_add_admin_hooks() { wp_set_current_user( self::factory()->user->create( [ 'role' => 'administrator' ] ) ); AMP_Validated_URL_Post_Type::add_admin_hooks(); - $this->assertEquals( 10, has_filter( 'dashboard_glance_items', [ self::TESTED_CLASS, 'filter_dashboard_glance_items' ] ) ); - $this->assertEquals( 10, has_action( 'rightnow_end', [ self::TESTED_CLASS, 'print_dashboard_glance_styles' ] ) ); - $this->assertEquals( 10, has_action( 'admin_enqueue_scripts', [ self::TESTED_CLASS, 'enqueue_edit_post_screen_scripts' ] ) ); $this->assertEquals( PHP_INT_MAX, has_action( 'add_meta_boxes', [ self::TESTED_CLASS, 'add_meta_boxes' ] ) ); $this->assertEquals( 10, has_action( 'edit_form_top', [ self::TESTED_CLASS, 'print_url_as_title' ] ) ); @@ -155,19 +152,9 @@ public function test_update_validated_url_menu_item() { ], ]; - $invalid_url_post_id = AMP_Validated_URL_Post_Type::store_validation_errors( - [ - [ - 'code' => 'hello', - ], - ], - get_permalink( self::factory()->post->create() ) - ); - $this->assertNotInstanceOf( 'WP_Error', $invalid_url_post_id ); - AMP_Validated_URL_Post_Type::update_validated_url_menu_item(); - $this->assertSame( 'Validated URLs 1', $submenu[ AMP_Options_Manager::OPTION_NAME ][2][0] ); + $this->assertSame( 'Validated URLs ', $submenu[ AMP_Options_Manager::OPTION_NAME ][2][0] ); $submenu = $original_submenu; } @@ -1654,32 +1641,6 @@ public function test_get_recheck_url() { $this->assertStringContains( wp_create_nonce( AMP_Validated_URL_Post_Type::NONCE_ACTION ), $link ); } - /** - * Test for filter_dashboard_glance_items() - * - * @covers \AMP_Validated_URL_Post_Type::filter_dashboard_glance_items() - */ - public function test_filter_dashboard_glance_items() { - - // There are no validation errors, so this should return the argument unchanged. - $this->assertEmpty( AMP_Validated_URL_Post_Type::filter_dashboard_glance_items( [] ) ); - - // Create validation errors, so that the method returns items. - $post_id = self::factory()->post->create(); - AMP_Validated_URL_Post_Type::store_validation_errors( - [ - [ 'code' => 'accepted' ], - [ 'code' => 'rejected' ], - [ 'code' => 'new' ], - ], - get_permalink( $post_id ) - ); - $items = AMP_Validated_URL_Post_Type::filter_dashboard_glance_items( [] ); - $this->assertStringContains( '1 URL w/ new AMP errors', $items[0] ); - $this->assertStringContains( AMP_Validated_URL_Post_Type::POST_TYPE_SLUG, $items[0] ); - $this->assertStringContains( AMP_Validation_Error_Taxonomy::VALIDATION_ERROR_STATUS_QUERY_VAR, $items[0] ); - } - /** * Test for get_validated_url_title() * diff --git a/tests/php/validation/test-class-amp-validation-error-taxonomy.php b/tests/php/validation/test-class-amp-validation-error-taxonomy.php index 96764d7ef02..8cb3d018a1e 100644 --- a/tests/php/validation/test-class-amp-validation-error-taxonomy.php +++ b/tests/php/validation/test-class-amp-validation-error-taxonomy.php @@ -1008,10 +1008,10 @@ public function test_add_admin_menu_validation_error_item() { wp_set_current_user( self::factory()->user->create( [ 'role' => 'administrator' ] ) ); AMP_Validation_Error_Taxonomy::add_admin_menu_validation_error_item(); $expected_submenu = [ - 'Error Index', + 'Error Index ', AMP_Validation_Manager::VALIDATE_CAPABILITY, 'edit-tags.php?taxonomy=amp_validation_error&post_type=amp_validated_url', - 'Error Index', + 'Error Index ', ]; $amp_options = $submenu[ AMP_Options_Manager::OPTION_NAME ]; $this->assertEquals( $expected_submenu, end( $amp_options ) ); diff --git a/webpack.config.js b/webpack.config.js index 23c7fa17b2e..e999d93f425 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -79,6 +79,7 @@ const ampValidation = { 'amp-validated-urls-index': './assets/src/amp-validation/amp-validated-urls-index.js', 'amp-validation-detail-toggle': './assets/src/amp-validation/amp-validation-detail-toggle.js', 'amp-validation-single-error-url-details': './assets/src/amp-validation/amp-validation-single-error-url-details.js', + 'amp-validation-counts': './assets/src/amp-validation/counts/index.js', }, plugins: [ ...sharedConfig.plugins,