diff --git a/assets/js/recommendations/litespeed-cache-browser-cache.js b/assets/js/recommendations/litespeed-cache-browser-cache.js new file mode 100644 index 000000000..55dd1340e --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-browser-cache.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable browser cache. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-browser-cache', + popoverId: 'prpl-popover-litespeed-cache-browser-cache', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-browser-cache', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/assets/js/recommendations/litespeed-cache-css-minification.js b/assets/js/recommendations/litespeed-cache-css-minification.js new file mode 100644 index 000000000..59dc868fa --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-css-minification.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable CSS minification. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-css-minification', + popoverId: 'prpl-popover-litespeed-cache-css-minification', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-css-minification', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/assets/js/recommendations/litespeed-cache-guest-mode.js b/assets/js/recommendations/litespeed-cache-guest-mode.js new file mode 100644 index 000000000..638e16c41 --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-guest-mode.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable guest mode. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-guest-mode', + popoverId: 'prpl-popover-litespeed-cache-guest-mode', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-guest-mode', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/assets/js/recommendations/litespeed-cache-image-lazy-load.js b/assets/js/recommendations/litespeed-cache-image-lazy-load.js new file mode 100644 index 000000000..d91fdbb94 --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-image-lazy-load.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable image lazy loading. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-image-lazy-load', + popoverId: 'prpl-popover-litespeed-cache-image-lazy-load', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-image-lazy-load', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/assets/js/recommendations/litespeed-cache-js-minification.js b/assets/js/recommendations/litespeed-cache-js-minification.js new file mode 100644 index 000000000..50952dae1 --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-js-minification.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable JS minification. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-js-minification', + popoverId: 'prpl-popover-litespeed-cache-js-minification', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-js-minification', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/assets/js/recommendations/litespeed-cache-page-cache.js b/assets/js/recommendations/litespeed-cache-page-cache.js new file mode 100644 index 000000000..da345b1f5 --- /dev/null +++ b/assets/js/recommendations/litespeed-cache-page-cache.js @@ -0,0 +1,32 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * LiteSpeed Cache: enable page cache. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'litespeed-cache-page-cache', + popoverId: 'prpl-popover-litespeed-cache-page-cache', + callback: () => { + return new Promise( ( resolve, reject ) => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_litespeed-cache-page-cache', + nonce: progressPlanner.nonce, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index 64b2aefb5..f0947a0f1 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -25,6 +25,7 @@ use Progress_Planner\Suggested_Tasks\Tasks_Interface; use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers; use Progress_Planner\Suggested_Tasks\Providers\Integrations\AIOSEO\Add_AIOSEO_Providers; +use Progress_Planner\Suggested_Tasks\Providers\Integrations\Litespeed_Cache\Add_Litespeed_Cache_Providers; use Progress_Planner\Suggested_Tasks\Providers\User as User_Tasks; use Progress_Planner\Suggested_Tasks\Providers\Email_Sending; use Progress_Planner\Suggested_Tasks\Providers\Set_Valuable_Post_Types; @@ -119,6 +120,9 @@ public function add_plugin_integration() { // All in One SEO integration. new Add_AIOSEO_Providers(); + + // LiteSpeed Cache integration. + new Add_Litespeed_Cache_Providers(); } /** diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-add-litespeed-cache-providers.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-add-litespeed-cache-providers.php new file mode 100644 index 000000000..8f86fafa6 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-add-litespeed-cache-providers.php @@ -0,0 +1,52 @@ +providers = [ + new Page_Cache(), + new Browser_Cache(), + new Css_Minification(), + new Js_Minification(), + new Image_Lazy_Load(), + new Guest_Mode(), + ]; + + return \array_merge( + $providers, + $this->providers + ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-browser-cache.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-browser-cache.php new file mode 100644 index 000000000..4db049fc1 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-browser-cache.php @@ -0,0 +1,126 @@ +get_litespeed_option( 'cache-browser' ); + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo '
'; + \esc_html_e( 'Browser caching tells visitors\' browsers to store static files (images, CSS, JavaScript) locally. When they return to your site, these files load from their device instead of being downloaded again, making repeat visits significantly faster.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable browser cache', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'cache-browser', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-css-minification.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-css-minification.php new file mode 100644 index 000000000..c9346566e --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-css-minification.php @@ -0,0 +1,126 @@ +get_litespeed_option( 'optm-css_min' ); + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo ''; + \esc_html_e( 'CSS minification removes unnecessary whitespace, comments, and formatting from your CSS files, reducing their size by 10-30%. This is a low-risk optimization that helps pages load faster by reducing the amount of data browsers need to download.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable CSS minification', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'optm-css_min', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-guest-mode.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-guest-mode.php new file mode 100644 index 000000000..6289759ca --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-guest-mode.php @@ -0,0 +1,126 @@ +get_litespeed_option( 'guest' ); + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo ''; + \esc_html_e( 'Guest mode serves a cached version of your pages to first-time visitors before determining their specific cache vary. This improves the cache hit ratio and ensures most anonymous visitors get the fastest possible page loads.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable guest mode', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'guest', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-image-lazy-load.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-image-lazy-load.php new file mode 100644 index 000000000..e64622807 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-image-lazy-load.php @@ -0,0 +1,126 @@ +get_litespeed_option( 'media-lazy' ); + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo ''; + \esc_html_e( 'Image lazy loading defers loading of images that are below the visible area of the page. Images are only loaded when the visitor scrolls near them. This significantly reduces initial page load time, especially on image-heavy pages.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable image lazy loading', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'media-lazy', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-js-minification.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-js-minification.php new file mode 100644 index 000000000..9861c9add --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-js-minification.php @@ -0,0 +1,126 @@ +get_litespeed_option( 'optm-js_min' ); + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo ''; + \esc_html_e( 'JavaScript minification strips unnecessary characters from your JS files without changing their functionality. This reduces file sizes and helps your pages load faster, especially on slower connections.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable JS minification', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'optm-js_min', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-litespeed-cache-interactive-provider.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-litespeed-cache-interactive-provider.php new file mode 100644 index 000000000..f29d2ea2b --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-litespeed-cache-interactive-provider.php @@ -0,0 +1,84 @@ +update_confs( [ $option_key => $value ] ); + + // update_confs() returns nothing, so report on the stored value. + return (int) $this->get_litespeed_option( $option_key ) === (int) $value; + } + } + + return \update_option( 'litespeed.conf.' . $option_key, $value ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/litespeed-cache/class-page-cache.php b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-page-cache.php new file mode 100644 index 000000000..73b8dee17 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/litespeed-cache/class-page-cache.php @@ -0,0 +1,127 @@ +get_litespeed_option( 'cache' ) !== 1; + } + + /** + * Get the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo ''; + \esc_html_e( 'Page caching is the foundation of LiteSpeed Cache. It stores pre-built versions of your pages so they load instantly for visitors instead of being generated fresh each time. This is the single most impactful performance setting you can enable.', 'progress-planner' ); + echo '
'; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $this->print_submit_button( \__( 'Enable page cache', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + $this->verify_litespeed_cache_ajax_security(); + + $this->update_litespeed_option( 'cache', 1 ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + return $this->add_popover_action( $actions, \__( 'Enable', 'progress-planner' ) ); + } +} diff --git a/classes/suggested-tasks/providers/traits/class-ajax-security-litespeed-cache.php b/classes/suggested-tasks/providers/traits/class-ajax-security-litespeed-cache.php new file mode 100644 index 000000000..daed8c7be --- /dev/null +++ b/classes/suggested-tasks/providers/traits/class-ajax-security-litespeed-cache.php @@ -0,0 +1,53 @@ + \esc_html__( 'LiteSpeed Cache is not active.', 'progress-planner' ) ] ); + } + } + + /** + * Perform complete LiteSpeed Cache AJAX security checks. + * + * Runs LiteSpeed Cache active check, capability check, and nonce verification. + * This is a convenience method for LiteSpeed Cache interactive tasks. + * + * @param string $capability The capability to require (default: 'manage_options'). + * @param string $action The nonce action to verify (default: 'progress_planner'). + * @param string $field The POST field containing the nonce (default: 'nonce'). + * + * @return void Exits with wp_send_json_error() if any check fails. + */ + protected function verify_litespeed_cache_ajax_security( $capability = 'manage_options', $action = 'progress_planner', $field = 'nonce' ) { + $this->verify_litespeed_cache_active_or_fail(); + $this->verify_ajax_security( $capability, $action, $field ); + } +} diff --git a/tests/phpunit/stubs/class-litespeed-conf-stub.php b/tests/phpunit/stubs/class-litespeed-conf-stub.php new file mode 100644 index 000000000..0d59d1398 --- /dev/null +++ b/tests/phpunit/stubs/class-litespeed-conf-stub.php @@ -0,0 +1,94 @@ + value pairs. + * + * @return void + */ + public function update_confs( $the_matrix = [] ) { + self::$calls[] = $the_matrix; + + if ( ! self::$should_store ) { + return; + } + + foreach ( $the_matrix as $key => $value ) { + \update_option( 'litespeed.conf.' . $key, $value ); + } + } + + /** + * Get the recorded calls. + * + * @return array[] + */ + public static function get_calls() { + return self::$calls; + } + + /** + * Forget the recorded calls. + * + * @return void + */ + public static function reset_calls() { + self::$calls = []; + } + + /** + * Set whether the stub stores values. + * + * @param bool $should_store Whether to store. + * + * @return void + */ + public static function set_should_store( $should_store ) { + self::$should_store = (bool) $should_store; + } + } +} diff --git a/tests/phpunit/test-class-litespeed-cache.php b/tests/phpunit/test-class-litespeed-cache.php new file mode 100644 index 000000000..bead3e7c7 --- /dev/null +++ b/tests/phpunit/test-class-litespeed-cache.php @@ -0,0 +1,158 @@ +getMethod( 'update_litespeed_option' ); + $method->setAccessible( true ); + + return $method->invoke( $provider, $option_key, $value ); + } + + /** + * With LiteSpeed present, the update goes through its own Conf class. + * + * Writing the option row directly would leave LiteSpeed's caches unpurged + * and its in-memory config stale -- for `guest` it also skips the crawler + * disabled-list reset that Conf::update_confs() performs. + * + * @return void + */ + public function test_update_routes_through_litespeed_conf() { + require_once __DIR__ . '/stubs/class-litespeed-conf-stub.php'; + + $result = $this->update_option_via( new Guest_Mode(), 'guest', 1 ); + + $this->assertSame( + [ [ 'guest' => 1 ] ], + \LiteSpeed\Conf::get_calls(), + 'The value should have been handed to Conf::update_confs().' + ); + $this->assertTrue( $result, 'The update should report success.' ); + } + + /** + * The provider reports failure when LiteSpeed did not store the value. + * + * Conf::update_confs() returns nothing, so success is read back from the + * stored option rather than assumed. + * + * @return void + */ + public function test_update_reports_failure_when_value_is_not_stored() { + require_once __DIR__ . '/stubs/class-litespeed-conf-stub.php'; + + \LiteSpeed\Conf::set_should_store( false ); + + $result = $this->update_option_via( new Guest_Mode(), 'guest', 1 ); + + $this->assertFalse( $result, 'A value LiteSpeed refused to store is not a success.' ); + + \LiteSpeed\Conf::set_should_store( true ); + } + + /** + * Each provider hands LiteSpeed its own option key. + * + * @return void + */ + public function test_each_provider_updates_its_own_option() { + require_once __DIR__ . '/stubs/class-litespeed-conf-stub.php'; + + $this->update_option_via( new Page_Cache(), 'cache', 1 ); + + $this->assertSame( [ [ 'cache' => 1 ] ], \LiteSpeed\Conf::get_calls() ); + } + + /** + * The task is offered while the setting is off, and not once it is on. + * + * @return void + */ + public function test_should_add_task_follows_the_setting() { + if ( ! \defined( 'LSCWP_V' ) ) { + \define( 'LSCWP_V', '7.9' ); + } + + $provider = new Guest_Mode(); + + \update_option( 'litespeed.conf.guest', 0 ); + $this->assertTrue( $provider->should_add_task(), 'A disabled setting should be suggested.' ); + + \update_option( 'litespeed.conf.guest', 1 ); + $this->assertFalse( $provider->should_add_task(), 'An enabled setting should not be suggested.' ); + } + + /** + * A string "1" counts as enabled. + * + * LiteSpeed casts values on save, so the stored option is not necessarily + * the integer the provider wrote. + * + * @return void + */ + public function test_should_add_task_treats_string_one_as_enabled() { + if ( ! \defined( 'LSCWP_V' ) ) { + \define( 'LSCWP_V', '7.9' ); + } + + \update_option( 'litespeed.conf.guest', '1' ); + + $this->assertFalse( + ( new Guest_Mode() )->should_add_task(), + 'A string "1" is still enabled.' + ); + } +}