From c8191812bd5f9b0dc6a8adbd7e08b3095ee90cdf Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Thu, 6 Nov 2025 15:19:11 +0000 Subject: [PATCH] Add admin notice for WPGraphQL Logging plugin Introduces an admin notice to inform users about potential performance impact of WPGraphQL Logging under heavy usage. Includes new AdminNotice class, template, AJAX dismissal handling, and related unit and e2e tests. Updates plugin setup to initialize the notice and ensures user meta is reset in test utilities. --- plugins/wpgraphql-logging/phpcs.xml | 9 +- .../src/Admin/AdminNotice.php | 105 +++++++++++++++ .../src/Admin/SettingsPage.php | 2 +- .../View/Templates/WPGraphQLLoggerNotice.php | 39 ++++++ .../src/Admin/ViewLogsPage.php | 2 +- plugins/wpgraphql-logging/src/Plugin.php | 2 + .../reset-wpgraphql-logging-settings.php | 6 +- .../tests/e2e/specs/admin-notice.spec.js | 33 +++++ .../tests/wpunit/Admin/AdminNoticeTest.php | 124 ++++++++++++++++++ 9 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 plugins/wpgraphql-logging/src/Admin/AdminNotice.php create mode 100644 plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php create mode 100644 plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js create mode 100644 plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php diff --git a/plugins/wpgraphql-logging/phpcs.xml b/plugins/wpgraphql-logging/phpcs.xml index 82c557f3..77b82827 100644 --- a/plugins/wpgraphql-logging/phpcs.xml +++ b/plugins/wpgraphql-logging/phpcs.xml @@ -54,7 +54,14 @@ - + + + + + + + + diff --git a/plugins/wpgraphql-logging/src/Admin/AdminNotice.php b/plugins/wpgraphql-logging/src/Admin/AdminNotice.php new file mode 100644 index 00000000..ecda3e74 --- /dev/null +++ b/plugins/wpgraphql-logging/src/Admin/AdminNotice.php @@ -0,0 +1,105 @@ +setup(); + } + + do_action( 'wpgraphql_logging_admin_notice_init', self::$instance ); + + return self::$instance; + } + + /** + * Setup the admin notice. + */ + public function setup(): void { + $key = self::ADMIN_NOTICE_KEY; + $is_dismissed = $this->is_notice_dismissed(); + + // Exit if the notice has been dismissed. + if ( $is_dismissed ) { + return; + } + + add_action( 'admin_notices', [ $this, 'register_admin_notice' ], 10, 0 ); + add_action( 'wp_ajax_' . $key, [ $this, 'process_ajax_request' ], 10, 0 ); + } + + /** + * Register admin notice to inform users about WPGraphQL Logging. + */ + public function register_admin_notice(): void { + $template = __DIR__ . '/View/Templates/WPGraphQLLoggerNotice.php'; + + if ( ! file_exists( $template ) ) { + return; + } + + require $template; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable + } + + /** + * Process the AJAX request. + */ + public function process_ajax_request(): void { + $key = self::ADMIN_NOTICE_KEY; + if ( ! isset( $_POST['action'] ) || esc_attr( $key ) !== $_POST['action'] ) { + return; + } + + check_ajax_referer( $key ); + + self::dismiss_admin_notice(); + } + + /** + * Check if the admin notice is dismissed. + */ + public function is_notice_dismissed(): bool { + $key = self::ADMIN_NOTICE_KEY; + return (bool) get_user_meta( get_current_user_id(), $key, true ); + } + + /** + * Dismiss the admin notice. + */ + public static function dismiss_admin_notice(): void { + $key = self::ADMIN_NOTICE_KEY; + update_user_meta( get_current_user_id(), $key, 1 ); + } +} diff --git a/plugins/wpgraphql-logging/src/Admin/SettingsPage.php b/plugins/wpgraphql-logging/src/Admin/SettingsPage.php index 558c1f7b..2b8ecd6c 100644 --- a/plugins/wpgraphql-logging/src/Admin/SettingsPage.php +++ b/plugins/wpgraphql-logging/src/Admin/SettingsPage.php @@ -43,7 +43,7 @@ class SettingsPage { /** * Initializes the settings page. */ - public static function init(): ?SettingsPage { + public static function init(): ?self { if ( ! current_user_can( 'manage_options' ) ) { return null; } diff --git a/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php new file mode 100644 index 00000000..834b3957 --- /dev/null +++ b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php @@ -0,0 +1,39 @@ + + +
+

+
+ + + +prefix . 'wpgraphql_logging'; $wpdb->query("TRUNCATE TABLE {$table_name}"); + + + // Remove admin notice dismissed meta data + delete_user_meta(get_current_user_id(), 'wpgraphql-logging-admin-notice'); } -}); \ No newline at end of file +}); diff --git a/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js b/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js new file mode 100644 index 00000000..6235c7b8 --- /dev/null +++ b/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js @@ -0,0 +1,33 @@ +import { expect, test } from "@wordpress/e2e-test-utils-playwright"; +import { + resetPluginSettings, + goToLoggingSettingsPage, +} from "../utils"; + +test.describe("WPGraphQL Logging Admin Notice", () => { + test.beforeEach(async ({ admin, page }) => { + await resetPluginSettings(admin); // Reset user meta data + await goToLoggingSettingsPage(admin); + await expect(page.locator("h1")).toHaveText("WPGraphQL Logging Settings"); + }); + + test("admin notice is displayed", async ({ + page, + admin, + }) => { + await goToLoggingSettingsPage(admin); + await expect( + page.locator("#wpgraphql-logging-admin-notice") + ).toBeVisible(); + + await page.locator("#wpgraphql-logging-admin-notice.notice .notice-dismiss").click(); + await expect( + page.locator("#wpgraphql-logging-admin-notice"), + ).not.toBeVisible(); + + await page.reload(); + await expect( + page.locator("#wpgraphql-logging-admin-notice"), + ).not.toBeVisible(); + }); +}); diff --git a/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php b/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php new file mode 100644 index 00000000..9cfd6f42 --- /dev/null +++ b/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php @@ -0,0 +1,124 @@ +factory->user->create(['role' => 'administrator']); + wp_set_current_user($admin_user); + set_current_screen('dashboard'); + } + + public function set_dismissed_notice_meta_data(): void { + update_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY, 1 ); + } + + public function unset_dismissed_notice_meta_data(): void { + delete_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY ); + } + + public function test_admin_notice_instance() { + $this->set_as_admin(); + + $reflection = new ReflectionClass( AdminNotice::class ); + $instanceProperty = $reflection->getProperty( 'instance' ); + $instanceProperty->setAccessible( true ); + $instanceProperty->setValue( null ); + + $this->assertNull( $instanceProperty->getValue() ); + $instance = AdminNotice::init(); + + $this->assertInstanceOf( AdminNotice::class, $instanceProperty->getValue() ); + $this->assertSame( $instance, $instanceProperty->getValue(), 'AdminNotice::init() should set the static instance property' ); + } + + public function test_initialization_sets_instance_with_no_hooks_registered_as_not_admin(): void { + wp_set_current_user(0); + $instance = AdminNotice::init(); + $this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice'])); + $this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request'])); + } + + public function test_initialization_sets_instance_with_no_hooks_registered_as_dismissed(): void { + $this->set_as_admin(); + $this->set_dismissed_notice_meta_data(); + + $instance = AdminNotice::init(); + $this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice'])); + $this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request'])); + } + + public function test_check_notice_is_displayed(): void { + $this->set_as_admin(); + + $reflection = new ReflectionClass( AdminNotice::class ); + $instanceProperty = $reflection->getProperty( 'instance' ); + $instanceProperty->setAccessible( true ); + $instanceProperty->setValue( null ); + + $notice = AdminNotice::init(); // Notice should be now registered + + $hook = has_action('admin_notices', [$notice, 'register_admin_notice']); + $this->assertNotFalse($hook); + $this->assertEquals(10, $hook); + + $ajax_hook = has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$notice, 'process_ajax_request']); + $this->assertNotFalse($ajax_hook); + $this->assertEquals(10, $ajax_hook); + } + + public function test_register_admin_notice_outputs_template(): void { + $this->set_as_admin(); + + $notice = AdminNotice::init(); + + ob_start(); + $notice->register_admin_notice(); + $output = ob_get_clean(); + + $this->assertStringContainsString('wpgraphql-logging-admin-notice', $output); + $this->assertStringContainsString('notice-warning', $output); + $this->assertStringContainsString('is-dismissible', $output); + $this->assertStringContainsString('Heads up! While very useful for debugging', $output); + $this->assertStringContainsString('