diff --git a/src/class-tiny-notices.php b/src/class-tiny-notices.php index fce2979..eae648d 100644 --- a/src/class-tiny-notices.php +++ b/src/class-tiny-notices.php @@ -22,6 +22,30 @@ class Tiny_Notices extends Tiny_WP_Base { private $notices; private $dismissals; + /** + * Tiny_Settings instance. + * + * @var Tiny_Settings + */ + private $settings; + + /** + * The number of compressions required before showing the feedback notice. + * + * @var int + */ + private $compressions_for_feedback = 10; + + /** + * Tiny_Notices constructor. + * + * @param Tiny_Settings $settings The settings instance. + */ + public function __construct( $settings ) { + parent::__construct(); + $this->settings = $settings; + } + protected static $incompatible_plugins = array( 'CheetahO Image Optimizer' => 'cheetaho-image-optimizer/cheetaho.php', 'EWWW Image Optimizer' => 'ewww-image-optimizer/ewww-image-optimizer.php', @@ -102,6 +126,7 @@ private function show_stored() { public function show_notices() { $this->incompatible_plugins_notice(); $this->outdated_platform_notice(); + $this->feedback_notice(); } public function add( $name, $message ) { @@ -303,4 +328,21 @@ function () use ( $notice ) { } ); } + + /** + * Checks if the feedback notice should be displayed and hooks it to admin_notices. + * + * @return void + */ + private function feedback_notice() { + if ( ! isset( $this->dismissals['feedback'] ) && + $this->settings->get_compression_count() > $this->compressions_for_feedback + ) { + add_action( 'admin_notices', array( $this, 'feedback_notice_show' ) ); + } + } + + public function feedback_notice_show() { + include __DIR__ . '/views/notice-feedback.php'; + } } diff --git a/src/class-tiny-plugin.php b/src/class-tiny-plugin.php index 2236332..facff18 100644 --- a/src/class-tiny-plugin.php +++ b/src/class-tiny-plugin.php @@ -861,18 +861,6 @@ public function clean_attachment( $post_id ) { $tiny_image->delete_converted_image(); } - public static function request_review() { - $review_url = - 'https://wordpress.org/support/plugin/tiny-compress-images/reviews/#new-post'; - $review_block = esc_html__( 'Enjoying TinyPNG?', 'tiny-compress-images' ); - $review_block .= ' '; - $review_block .= sprintf( - '%s', - esc_url( $review_url ), - esc_html__( 'Write a review', 'tiny-compress-images' ) - ); - return $review_block; - } public function mark_image_as_compressed() { $response = $this->validate_ajax_attachment_request(); diff --git a/src/class-tiny-settings.php b/src/class-tiny-settings.php index 290b8b0..d19e876 100644 --- a/src/class-tiny-settings.php +++ b/src/class-tiny-settings.php @@ -29,7 +29,7 @@ class Tiny_Settings extends Tiny_WP_Base { public function __construct() { parent::__construct(); - $this->notices = new Tiny_Notices(); + $this->notices = new Tiny_Notices( $this ); new Tiny_Diagnostics( $this ); } diff --git a/src/views/bulk-optimization.php b/src/views/bulk-optimization.php index 3ab5b62..109afe3 100644 --- a/src/views/bulk-optimization.php +++ b/src/views/bulk-optimization.php @@ -55,6 +55,8 @@ } elseif ( 0 == $stats['available-unoptimized-sizes'] ) { /* translators: %s: friendly user name */ printf( esc_html__( '%s, this is great! Your entire library is optimized!', 'tiny-compress-images' ), $this->friendly_user_name() ); + echo '
'; + require __DIR__ . '/request-review.php'; } elseif ( $stats['optimized-image-sizes'] > 0 ) { if ( $percentage_of_files > 75 ) { /* translators: %s: friendly user name */ diff --git a/src/views/dashboard-widget.php b/src/views/dashboard-widget.php index fadab1e..2441757 100644 --- a/src/views/dashboard-widget.php +++ b/src/views/dashboard-widget.php @@ -72,7 +72,7 @@ /* translators: %s: friendly user name */ printf( esc_html__( '%s, this is great! Your entire library is optimized!', 'tiny-compress-images' ), $this->friendly_user_name() ); echo ' '; - echo Tiny_Plugin::request_review(); + require __DIR__ . '/request-review.php'; ?>

diff --git a/src/views/notice-feedback.php b/src/views/notice-feedback.php new file mode 100644 index 0000000..813a909 --- /dev/null +++ b/src/views/notice-feedback.php @@ -0,0 +1,16 @@ +%s', + esc_url( $review_url ), + esc_html__( 'Leave a review', 'tiny-compress-images' ) +); +?> + +
+

+ + + +

+
\ No newline at end of file diff --git a/src/views/request-review.php b/src/views/request-review.php new file mode 100644 index 0000000..0d43993 --- /dev/null +++ b/src/views/request-review.php @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/views/settings.php b/src/views/settings.php index c12d4a6..e240ac3 100644 --- a/src/views/settings.php +++ b/src/views/settings.php @@ -56,8 +56,8 @@ - -

+ +

diff --git a/test/helpers/wordpress.php b/test/helpers/wordpress.php index ff1b2c0..45e2406 100644 --- a/test/helpers/wordpress.php +++ b/test/helpers/wordpress.php @@ -337,6 +337,34 @@ public function getTestMetadata($path = '14/01', $name = 'test') * @param mixed $expected_args arguments to the hook */ public static function assertHook($hookname, $expected_args = null) + { + $found = self::findHook($hookname, $expected_args); + + $message = is_null($expected_args) + ? sprintf('Expected hook "%s" to be called.', $hookname) + : sprintf('Expected hook "%s" to be called with the given arguments.', $hookname); + + Assert::assertTrue($found, $message); + } + + /** + * Testhelper to assert a hook has NOT been registered. + * + * @param string $hookname name of the filter or action + * @param mixed $expected_args arguments to the hook + */ + public static function assertNotHook($hookname, $expected_args = null) + { + $found = self::findHook($hookname, $expected_args); + + $message = is_null($expected_args) + ? sprintf('Expected hook "%s" NOT to be called.', $hookname) + : sprintf('Expected hook "%s" NOT to be called with the given arguments.', $hookname); + + Assert::assertFalse($found, $message); + } + + private static function findHook($hookname, $expected_args = null) { $hooks = array('add_action', 'add_filter'); $found = false; @@ -363,11 +391,7 @@ public static function assertHook($hookname, $expected_args = null) } } - $message = is_null($expected_args) - ? sprintf('Expected hook "%s" to be called.', $hookname) - : sprintf('Expected hook "%s" to be called with the given arguments.', $hookname); - - Assert::assertTrue($found, $message); + return $found; } } diff --git a/test/integration/compression.spec.ts b/test/integration/compression.spec.ts index 140d7d0..711ef28 100644 --- a/test/integration/compression.spec.ts +++ b/test/integration/compression.spec.ts @@ -220,7 +220,7 @@ test.describe('compression', () => { // Since 4.2 it is no longer a link but a button. const isButton = WPVersion >= 4.2; if (isButton) { - await page.getByRole('button', { name: 'Dismiss this notice.' }).click(); + await page.locator('.error.notice').getByRole('button', { name: 'Dismiss this notice.' }).click(); } else { await page.getByRole('link', { name: 'Dismiss' }).click(); } diff --git a/test/integration/utils.ts b/test/integration/utils.ts index 25ca8fd..8ee8a1b 100644 --- a/test/integration/utils.ts +++ b/test/integration/utils.ts @@ -202,7 +202,7 @@ export async function deactivatePlugin(page: Page, pluginSlug: string) { await plugin.getByLabel('Deactivate').click(); } -export async function setConversionSettings(page: Page, settings: { convert: boolean; output?: 'smallest' | 'webp' | 'avif'; delivery: 'picture' | 'htaccess' }) { +export async function setConversionSettings(page: Page, settings: { convert: boolean; output?: 'smallest' | 'webp' | 'avif'; delivery?: 'picture' | 'htaccess' }) { await page.goto('/wp-admin/options-general.php?page=tinify'); if (settings.convert) { diff --git a/test/unit/TinySettingsAjaxTest.php b/test/unit/TinySettingsAjaxTest.php index 40fb44b..1e05c81 100644 --- a/test/unit/TinySettingsAjaxTest.php +++ b/test/unit/TinySettingsAjaxTest.php @@ -4,6 +4,7 @@ class Tiny_Settings_Ajax_Test extends Tiny_TestCase { protected $notices; + private $settings; public function set_up() { parent::set_up(); @@ -12,7 +13,8 @@ public function set_up() { public function test_settings_ajax_init() { $tiny_settings = new Tiny_Settings(); - $tiny_settings->ajax_init(); + $this->settings = $tiny_settings; + $this->settings->ajax_init(); WordPressStubs::assertHook('wp_ajax_tiny_image_sizes_notice', array( $tiny_settings, 'image_sizes_notice' )); WordPressStubs::assertHook('wp_ajax_tiny_account_status', array( $tiny_settings, 'account_status' )); @@ -21,7 +23,7 @@ public function test_settings_ajax_init() { } public function test_notices_ajax_init() { - $tiny_notices = new Tiny_Notices(); + $tiny_notices = new Tiny_Notices( $this->settings ); $tiny_notices->ajax_init(); WordPressStubs::assertHook('wp_ajax_tiny_dismiss_notice', array( $tiny_notices, 'dismiss' )); diff --git a/test/unit/Tiny_Notices_Test.php b/test/unit/Tiny_Notices_Test.php new file mode 100644 index 0000000..bccbc99 --- /dev/null +++ b/test/unit/Tiny_Notices_Test.php @@ -0,0 +1,86 @@ +wp->addMethod( 'get_user_meta' ); + $this->wp->addMethod( 'update_user_meta' ); + $this->wp->addMethod( 'get_current_user_id' ); + + $this->settings = $this->createMock( Tiny_Settings::class ); + $this->settings->method( 'get_compression_count' )->willReturn( 0 ); + + $this->wp->stub( 'current_user_can', function () { + return true; + } ); + + $this->subject = new Tiny_Notices( $this->settings ); + } + + /** + * Verifies that when the current user has the manage_options capability, + * calling admin_init() registers at least one admin_notices action. + */ + public function test_registers_notices_when_user_can_manage_options() { + $this->subject->add( 'test', 'Test notice message' ); + + $this->subject->admin_init(); + + WordPressStubs::assertHook( 'admin_notices' ); + } + + /** + * Verifies that feedback_notice_show is hooked to admin_notices when the + * feedback notice has not been dismissed + */ + public function test_registers_feedback_notice_when_not_dismissed() { + $this->settings = $this->createMock( Tiny_Settings::class ); + $this->settings->method( 'get_compression_count' )->willReturn( 20 ); + $this->subject = new Tiny_Notices( $this->settings ); + + $this->subject->show_notices(); + + WordPressStubs::assertHook( 'admin_notices', array( $this->subject, 'feedback_notice_show' ) ); + } + + /** + * Verifies that feedback_notice_show is hooked to admin_notices when the + * compression count is just above compressions_for_feedback + */ + public function test_registers_feedback_notice_when_compressioncount_reached() { + $this->settings = $this->createMock( Tiny_Settings::class ); + $this->settings->method( 'get_compression_count' )->willReturn( 11 ); + $this->subject = new Tiny_Notices( $this->settings ); + + $this->subject->show_notices(); + + WordPressStubs::assertHook( 'admin_notices', array( $this->subject, 'feedback_notice_show' ) ); + } + + + /** + * Verifies that feedback_notice_show is NOT hooked to admin_notices when + * the feedback notice has been dismissed by the user. + */ + public function test_will_not_show_feedback_notice_when_dismissed() { + $this->settings = $this->createMock( Tiny_Settings::class ); + $this->subject = new Tiny_Notices( $this->settings ); + + $this->wp->stub( 'get_user_meta', function () { + return array( 'feedback' => true ); + } ); + + $this->subject->admin_init(); + + WordPressStubs::assertNotHook( 'admin_notices', array( $this->subject, 'feedback_notice_show' ) ); + } + +}