From 0b98be80f533e1979e7cd5e27246eed18717cb52 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:06:28 +0100 Subject: [PATCH 01/38] Don't block PSR-4 style filenames for classes --- phpcs.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 18e8384a..3752d709 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,6 @@ - Accessibility Checker Wordpress Coding Standards + Accessibility Checker WordPress Coding Standards . @@ -147,4 +147,12 @@ + + + + + + + + From 183b8648e75b6b501d3e5065fec7584faddd356d Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:06:53 +0100 Subject: [PATCH 02/38] Add a PSR-4 style namespace for autoloader --- composer.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 331cb205..3bc6ef34 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,10 @@ "admin/", "includes/classes/", "includes/deprecated/" - ] + ], + "Psr-4": { + "EqualizeDigital\\AccessibilityChecker\\": "includes/classes/" + } }, "autoload-dev": { "classmap": [] @@ -75,4 +78,4 @@ "@php ./vendor/phpunit/phpunit/phpunit --testdox" ] } -} \ No newline at end of file +} From 3b486bda05e33bc20eb2ff0fe47d35bf987d7a62 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:07:53 +0100 Subject: [PATCH 03/38] Add WP-Cli base setup and commands for get-stats and delete-stats --- includes/classes/WPCLI/BootstrapCLI.php | 81 ++++++++++ .../WPCLI/Command/CLICommandInterface.php | 40 +++++ .../classes/WPCLI/Command/DeleteStats.php | 81 ++++++++++ includes/classes/WPCLI/Command/GetStats.php | 151 ++++++++++++++++++ 4 files changed, 353 insertions(+) create mode 100644 includes/classes/WPCLI/BootstrapCLI.php create mode 100644 includes/classes/WPCLI/Command/CLICommandInterface.php create mode 100644 includes/classes/WPCLI/Command/DeleteStats.php create mode 100644 includes/classes/WPCLI/Command/GetStats.php diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php new file mode 100644 index 00000000..658dacf5 --- /dev/null +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -0,0 +1,81 @@ +commands ); + + foreach ( $commands as $command ) { + // All commands must follow the interface. + if ( ! ( $command instanceof CLICommandInterface ) ) { + continue; + } + + try { + WP_CLI::add_command( + $command::get_name(), + $command, + $command::get_args() + ); + } catch ( Exception $e ) { + WP_CLI::warning( sprintf( + // translators: 1: a php classname, 2: an error message that was thrown about why this failed to register. + __( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), + get_class( $command ), + $e->getMessage() + ) ); + } + } + } +} diff --git a/includes/classes/WPCLI/Command/CLICommandInterface.php b/includes/classes/WPCLI/Command/CLICommandInterface.php new file mode 100644 index 00000000..814d7d01 --- /dev/null +++ b/includes/classes/WPCLI/Command/CLICommandInterface.php @@ -0,0 +1,40 @@ + [ + [ + 'type' => 'positional', + 'name' => 'post_id', + 'description' => 'The ID of the post to delete stats for.', + 'optional' => true, + 'default' => 0, + 'repeating' => false, + ], + ], + ]; + } + + /** + * Run the command to delete stats for a given post id. + * + * @param array $options This is the positional argument, the post ID in this case. + * @param array $arguments This is the associative argument, not used in this command but kept for consistency with cli commands using this pattern. + * + * @return void + * @throws ExitException If the post ID is not provided, does not exist, or the class we need isn't available. + */ + public function __invoke( array $options = [], array $arguments = [] ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $post_id = $options[0] ?? null; + + if ( 0 === $post_id ) { + WP_CLI::error( "No Post ID provided, getting all stats not implemented yet.\n" ); + } + + $post_exists = (bool) get_post( $post_id ); + + if ( ! $post_exists ) { + WP_CLI::error( "Post ID {$post_id} does not exist.\n" ); + } + + if ( class_exists( 'EDAC\Admin\Purge_Post_Data' ) === false ) { + WP_CLI::error( "Purge_Post_Data class not found, is Accessibility Checker installed and activated?\n" ); + } + + Purge_Post_Data::delete_post( $post_id ); + WP_CLI::success( "Stats of {$post_id} deleted! \n" ); + } +} diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php new file mode 100644 index 00000000..a99d7ba3 --- /dev/null +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -0,0 +1,151 @@ + [ + [ + 'type' => 'positional', + 'name' => 'post_id', + 'description' => 'The ID of the post to get stats for.', + 'optional' => true, + 'default' => 0, + 'repeating' => false, + ], + [ + 'type' => 'assoc', + 'name' => 'stat', + 'description' => 'Keys to show in the results. Defaults to all keys. "passed_tests", "errors", "warnings", "ignored", "contrast_errors", "content_grade", "readability", "simplified_summary"', + 'optional' => true, + 'default' => null, + 'repeating' => true, + ], + ], + ]; + } + + /** + * Run the command that gets the stats for a post ID or the stats for the whole site. + * + * @since 1.15.0 + * + * @param array $options The positional argument, the post ID in this case. + * @param array $arguments The associative argument, the stat key in this case. + * + * @return void + * @throws ExitException If the post ID does not exist, or the class we need isn't available. + */ + public function __invoke( array $options = [], array $arguments = [] ) { + $post_id = $options[0] ?? null; + + if ( 0 === $post_id ) { + $all_stats_json = $this->get_all_stats(); + + WP_CLI::success( $all_stats_json ); + } + + $post_exists = (bool) get_post( $post_id ); + + if ( ! $post_exists ) { + WP_CLI::error( "Post ID {$post_id} does not exist.\n" ); + } + + if ( class_exists( 'EDAC\Inc\Summary_Generator' ) === false ) { + WP_CLI::error( "Summary_Generator class not found, is Accessibility Checker installed and activated?.\n" ); + } + + $stats = ( new Summary_Generator( $post_id ) )->generate_summary(); + + if ( empty( $stats ) ) { + WP_CLI::error( "No stats found for post ID {$post_id}.\n" ); + } + + $value = $arguments['stat'] && in_array( $arguments['stat'], $this->valid_stats, true ) + ? [ $arguments['stat'] => $stats[ $arguments['stat'] ] ] + : $stats; + + WP_CLI::success( wp_json_encode( $value ) . "\n" ); + } + + /** + * Gets the sites from the entire site. + * + * A limitation is this can only stats for scanned pages, if some pages are not scanned they are not reflected in the stats. + * + * @since 1.15.0 + * + * @throws ExitException If ScanStats class is not found or no stats are found. + */ + private function get_all_stats() { + + if ( class_exists( 'EDAC\Admin\Scans_Stats' ) === false ) { + WP_CLI::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); + } + + $stats = ( new Scans_Stats() )->summary(); + + if ( empty( $stats ) ) { + WP_CLI::error( "No stats found.\n" ); + } + + return wp_json_encode( $stats ); + } +} From 0dc00e84b160bad0401179f6d456502bd61cde7a Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:08:26 +0100 Subject: [PATCH 04/38] Add WP-Cli base setup and commands for get-stats and delete-stats --- includes/classes/class-plugin.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/includes/classes/class-plugin.php b/includes/classes/class-plugin.php index c0170b93..0f4cc3b3 100644 --- a/includes/classes/class-plugin.php +++ b/includes/classes/class-plugin.php @@ -9,6 +9,7 @@ use EDAC\Admin\Admin; use EDAC\Admin\Meta_Boxes; +use EqualizeDigital\AccessibilityChecker\WPCLI\BootstrapCLI; /** * Main plugin functionality class. @@ -52,5 +53,10 @@ private function init() { $frontend_validate = new Frontend_Validate(); $frontend_validate->init_hooks(); + + if ( defined( 'WP_CLI' ) && WP_CLI ) { + $cli = new BootstrapCLI(); + $cli->register(); + } } } From 775007d5310d70149f25415822c8ac5ed95ab59f Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:19:52 +0100 Subject: [PATCH 05/38] Use is_subclass_of rather than instance of since we have a classname string and not an object --- includes/classes/WPCLI/BootstrapCLI.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index 658dacf5..344385ac 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -58,7 +58,8 @@ public function register() { foreach ( $commands as $command ) { // All commands must follow the interface. - if ( ! ( $command instanceof CLICommandInterface ) ) { + if ( ! is_subclass_of( $command, CLICommandInterface::class, true ) ) { + error_log('not a cli'); continue; } From 86fb9333f546df181a9bb3af7bb123722cb26043 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:20:13 +0100 Subject: [PATCH 06/38] Use is_subclass_of rather than instance of since we have a classname string and not an object --- includes/classes/WPCLI/BootstrapCLI.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index 344385ac..373991f4 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -59,7 +59,6 @@ public function register() { foreach ( $commands as $command ) { // All commands must follow the interface. if ( ! is_subclass_of( $command, CLICommandInterface::class, true ) ) { - error_log('not a cli'); continue; } From 64bdc004a05c784d2c4fa28980ccf901ba99d84b Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:20:56 +0100 Subject: [PATCH 07/38] Register the cli commands when bootstrapping the plugin --- includes/classes/class-plugin.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/includes/classes/class-plugin.php b/includes/classes/class-plugin.php index 0f4cc3b3..6bb0f558 100644 --- a/includes/classes/class-plugin.php +++ b/includes/classes/class-plugin.php @@ -31,6 +31,12 @@ public function __construct() { // The REST api must load if admin or not. $rest_api = new REST_Api(); $rest_api->init_hooks(); + + // When WP CLI is enabled, load the CLI commands. + if ( defined( 'WP_CLI' ) && WP_CLI ) { + $cli = new BootstrapCLI(); + $cli->register(); + } } /** @@ -53,10 +59,5 @@ private function init() { $frontend_validate = new Frontend_Validate(); $frontend_validate->init_hooks(); - - if ( defined( 'WP_CLI' ) && WP_CLI ) { - $cli = new BootstrapCLI(); - $cli->register(); - } } } From 9d3bff3127574622ab78ca5ccc37226f8e3e128c Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 21:31:01 +0100 Subject: [PATCH 08/38] Lowercase autoload key --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3bc6ef34..6aefb539 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "includes/classes/", "includes/deprecated/" ], - "Psr-4": { + "psr-4": { "EqualizeDigital\\AccessibilityChecker\\": "includes/classes/" } }, From dae548494a2c727799a12adf8a0c8f4dcfd99dd6 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 22:17:42 +0100 Subject: [PATCH 09/38] PHPCS: fix multi-line call by breaking to own lines --- includes/classes/WPCLI/BootstrapCLI.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index 373991f4..d16003be 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -69,12 +69,14 @@ public function register() { $command::get_args() ); } catch ( Exception $e ) { - WP_CLI::warning( sprintf( - // translators: 1: a php classname, 2: an error message that was thrown about why this failed to register. - __( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), - get_class( $command ), - $e->getMessage() - ) ); + WP_CLI::warning( + sprintf( + // translators: 1: a php classname, 2: an error message that was thrown about why this failed to register. + __( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), + get_class( $command ), + $e->getMessage() + ) + ); } } } From f7d2296d943a2536d0de9a9e0619f37d40844607 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Mon, 24 Jun 2024 22:19:53 +0100 Subject: [PATCH 10/38] Use spaces in composer.json instead of tabs --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 6aefb539..8690bc95 100644 --- a/composer.json +++ b/composer.json @@ -57,9 +57,9 @@ "includes/classes/", "includes/deprecated/" ], - "psr-4": { - "EqualizeDigital\\AccessibilityChecker\\": "includes/classes/" - } + "psr-4": { + "EqualizeDigital\\AccessibilityChecker\\": "includes/classes/" + } }, "autoload-dev": { "classmap": [] From 26b8d65ee0a7049116a156a3b6946db29dd75360 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 27 Jun 2024 21:08:33 +0100 Subject: [PATCH 11/38] Remove handling for full site stats in get-stats command --- includes/classes/WPCLI/Command/GetStats.php | 30 --------------------- 1 file changed, 30 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index a99d7ba3..247ef409 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -96,12 +96,6 @@ public static function get_args(): array { public function __invoke( array $options = [], array $arguments = [] ) { $post_id = $options[0] ?? null; - if ( 0 === $post_id ) { - $all_stats_json = $this->get_all_stats(); - - WP_CLI::success( $all_stats_json ); - } - $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { @@ -124,28 +118,4 @@ public function __invoke( array $options = [], array $arguments = [] ) { WP_CLI::success( wp_json_encode( $value ) . "\n" ); } - - /** - * Gets the sites from the entire site. - * - * A limitation is this can only stats for scanned pages, if some pages are not scanned they are not reflected in the stats. - * - * @since 1.15.0 - * - * @throws ExitException If ScanStats class is not found or no stats are found. - */ - private function get_all_stats() { - - if ( class_exists( 'EDAC\Admin\Scans_Stats' ) === false ) { - WP_CLI::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); - } - - $stats = ( new Scans_Stats() )->summary(); - - if ( empty( $stats ) ) { - WP_CLI::error( "No stats found.\n" ); - } - - return wp_json_encode( $stats ); - } } From 4795cdd4bbaf0392263fabaf5e56eb7de66a915e Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 27 Jun 2024 22:38:42 +0100 Subject: [PATCH 12/38] Update get-stats command to not support getting entire site stats and to handle csv items in the --stat arg --- includes/classes/WPCLI/Command/GetStats.php | 35 ++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index 247ef409..d5755c9f 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -73,7 +73,7 @@ public static function get_args(): array { [ 'type' => 'assoc', 'name' => 'stat', - 'description' => 'Keys to show in the results. Defaults to all keys. "passed_tests", "errors", "warnings", "ignored", "contrast_errors", "content_grade", "readability", "simplified_summary"', + 'description' => 'Keys to show in the results. Defaults to all keys. Pass items in as a comma separated list if you want multiple. Valid keys are: ' . implode( ', ', ( new self() )->valid_stats ) . '.', 'optional' => true, 'default' => null, 'repeating' => true, @@ -99,7 +99,7 @@ public function __invoke( array $options = [], array $arguments = [] ) { $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { - WP_CLI::error( "Post ID {$post_id} does not exist.\n" ); + WP_CLI::error( "Post ID {$post_id} does not exist." ); } if ( class_exists( 'EDAC\Inc\Summary_Generator' ) === false ) { @@ -109,13 +109,34 @@ public function __invoke( array $options = [], array $arguments = [] ) { $stats = ( new Summary_Generator( $post_id ) )->generate_summary(); if ( empty( $stats ) ) { - WP_CLI::error( "No stats found for post ID {$post_id}.\n" ); + WP_CLI::error( "No stats found for post ID {$post_id}." ); } - $value = $arguments['stat'] && in_array( $arguments['stat'], $this->valid_stats, true ) - ? [ $arguments['stat'] => $stats[ $arguments['stat'] ] ] - : $stats; + if ( 100 === (int) $stats['passed_tests'] && 0 === (int) $stats['ignored'] ) { + WP_CLI::success( "Either the post is not yet scanned or all tests passed for post ID {$post_id}." ); + return; + } + + if ( ! empty( $arguments['stat'] ) ) { + $items_to_return = []; + $requested_stats = explode( ',', $arguments['stat'] ); + foreach ( $requested_stats as $key ) { + $stats_key = trim( $key ); + if ( ! in_array( $stats_key, $this->valid_stats, true ) ) { + WP_CLI::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', $this->valid_stats ) . '.' ); + } + if ( ! isset( $stats[ $stats_key ] ) ) { + WP_CLI::error( "Stat key: {$stats_key} not found in stats." ); + } + $items_to_return[ $stats_key ] = $stats[ $stats_key ]; + } + + if ( $items_to_return ) { + WP_CLI::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); + return; + } + } - WP_CLI::success( wp_json_encode( $value ) . "\n" ); + WP_CLI::success( wp_json_encode( $stats, JSON_PRETTY_PRINT ) ); } } From 1935f636b9b57b80e6703aff3c5f9bec6e76b258 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 27 Jun 2024 22:40:36 +0100 Subject: [PATCH 13/38] Add a get-site-stats command to get the entire site stats for all scanned pages --- includes/classes/WPCLI/BootstrapCLI.php | 4 +- .../classes/WPCLI/Command/GetSiteStats.php | 118 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 includes/classes/WPCLI/Command/GetSiteStats.php diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index d16003be..1a975c5b 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -11,6 +11,7 @@ use EqualizeDigital\AccessibilityChecker\WPCLI\Command\CLICommandInterface; use EqualizeDigital\AccessibilityChecker\WPCLI\Command\DeleteStats; +use EqualizeDigital\AccessibilityChecker\WPCLI\Command\GetSiteStats; use EqualizeDigital\AccessibilityChecker\WPCLI\Command\GetStats; use Exception; use WP_CLI; @@ -30,8 +31,9 @@ class BootstrapCLI { * @var CLICommandInterface[] */ protected array $commands = [ - GetStats::class, DeleteStats::class, + GetSiteStats::class, + GetStats::class, ]; /** diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php new file mode 100644 index 00000000..951a844e --- /dev/null +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -0,0 +1,118 @@ + [ + [ + 'type' => 'assoc', + 'name' => 'stat', + 'description' => 'Keys to show in the results. Defaults to all keys.', + 'optional' => true, + 'default' => null, + 'repeating' => true, + ], + ], + ]; + } + + /** + * Run the command that gets the stats for the whole site. + * + * @since 1.15.0 + * + * @param array $options The positional argument, none is this case. + * @param array $arguments The associative arguments, the stat keys in this case. + * + * @return void + * @throws ExitException If the post ID does not exist, or the class we need isn't available. + */ + public function __invoke( array $options = [], array $arguments = [] ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $all_stats = $this->get_all_stats(); + + if ( ! empty( $arguments['stat'] ) ) { + $items_to_return = []; + $requested_stats = explode( ',', $arguments['stat'] ); + foreach ( $requested_stats as $key ) { + $stats_key = trim( $key ); + if ( ! isset( $all_stats[ $stats_key ] ) ) { + WP_CLI::error( "Stat key: {$stats_key} not found in stats." ); + } + $items_to_return[ $stats_key ] = $all_stats[ $stats_key ]; + } + } + + if ( $items_to_return ) { + WP_CLI::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); + return; + } + + WP_CLI::success( wp_json_encode( $all_stats, JSON_PRETTY_PRINT ) ); + } + + /** + * Gets the sites from the entire site. + * + * A limitation is this can only provide the stats for scanned pages, if + * some pages are not scanned they are not reflected in the stats. Use the + * 'scannable_posts_count' and the 'posts_scanned' values to determine if + * the whole site is reflected or not. + * + * @since 1.15.0 + * + * @throws ExitException If ScanStats class is not found or no stats are found. + */ + private function get_all_stats(): array { + + if ( class_exists( 'EDAC\Admin\Scans_Stats' ) === false ) { + WP_CLI::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); + } + + $stats = ( new Scans_Stats() )->summary(); + + if ( empty( $stats ) ) { + WP_CLI::error( "No stats found.\n" ); + } + + return $stats; + } +} From 941db722423bcc803ee554ea7b19ca503a17973d Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:13:09 +0100 Subject: [PATCH 14/38] Add a Mock_WP_CLI class for tests --- tests/phpunit/Mocks/Mock_WP_CLI.php | 124 ++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 tests/phpunit/Mocks/Mock_WP_CLI.php diff --git a/tests/phpunit/Mocks/Mock_WP_CLI.php b/tests/phpunit/Mocks/Mock_WP_CLI.php new file mode 100644 index 00000000..827d7bd7 --- /dev/null +++ b/tests/phpunit/Mocks/Mock_WP_CLI.php @@ -0,0 +1,124 @@ + Date: Wed, 10 Jul 2024 14:13:22 +0100 Subject: [PATCH 15/38] Update the BootstrapCLI class to get WP_CLI with dependency injection --- includes/classes/WPCLI/BootstrapCLI.php | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index 1a975c5b..ef5abd67 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -23,6 +23,17 @@ */ class BootstrapCLI { + /** + * The WP-CLI instance. + * + * This allows injecting a mock WP-CLI instance for testing. + * + * @since 1.15.0 + * + * @var WP_CLI + */ + private $wp_cli; + /** * The boot method on this class will use this array to register custom WP-CLI commands. * @@ -36,6 +47,17 @@ class BootstrapCLI { GetStats::class, ]; + /** + * Set up the internal wp_cli property. + * + * @since 1.15.0 + * + * @param WP_CLI|null $wp_cli The WP-CLI instance. + */ + public function __construct( $wp_cli = null ) { + $this->wp_cli = $wp_cli ? $wp_cli : new WP_CLI(); + } + /** * Register the WP-CLI commands by looping through the commands array and adding each command. * @@ -65,17 +87,17 @@ public function register() { } try { - WP_CLI::add_command( + $this->wp_cli::add_command( $command::get_name(), $command, $command::get_args() ); } catch ( Exception $e ) { - WP_CLI::warning( + $this->wp_cli::warning( sprintf( // translators: 1: a php classname, 2: an error message that was thrown about why this failed to register. __( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), - get_class( $command ), + $command, $e->getMessage() ) ); From 11a903606fe4e5508f69efd24035a97848ef3024 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:13:41 +0100 Subject: [PATCH 16/38] Add tests for the BootstrapCLI class --- .../classes/WPCLI/BootstrapCLITest.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php diff --git a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php new file mode 100644 index 00000000..f7704795 --- /dev/null +++ b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php @@ -0,0 +1,64 @@ +get_subcommands() ); + + $bootstrap_cli = new BootstrapCLI( new WP_CLI() ); + $bootstrap_cli->register(); + + $commands = WP_CLI::get_root_command(); + $command_count_after = count( $commands->get_subcommands() ); + + // check if the number of commands has increased after register is called. + $this->assertGreaterThan( $command_count, $command_count_after ); + } + + /** + * Test the bootstrap CLI command with a mock that throws an exception when + * adding commands. + */ + public function test_bootstrap_cli_command_with_exception() { + WP_CLI::set_add_command_should_throw( true ); + + $bootstrap_cli = new BootstrapCLI( new WP_CLI() ); + + ob_start(); + $bootstrap_cli->register(); + $output = ob_get_clean(); + + // check if the output contains the expected exception message. + $this->assertStringStartsWith( 'Warning: Failed to register command', $output ); + } +} From ac55f1dd9860630ebcd13f48eef4467815975e20 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:17:44 +0100 Subject: [PATCH 17/38] Update the GetStats class to get WP_CLI with dependency injection --- includes/classes/WPCLI/Command/GetStats.php | 46 +++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index d5755c9f..768d7ba4 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -9,7 +9,6 @@ namespace EqualizeDigital\AccessibilityChecker\WPCLI\Command; -use EDAC\Admin\Scans_Stats; use EDAC\Inc\Summary_Generator; use WP_CLI; use WP_CLI\ExitException; @@ -23,6 +22,15 @@ */ class GetStats implements CLICommandInterface { + /** + * The WP-CLI instance. + * + * This lets a mock be passed in for testing. + * + * @var mixed|WP_CLI + */ + private $wp_cli; + /** * An array of valid stats keys. * @@ -30,7 +38,7 @@ class GetStats implements CLICommandInterface { * * @var array|string[] */ - private array $valid_stats = [ + private static array $valid_stats = [ 'passed_tests', 'errors', 'warnings', @@ -41,6 +49,15 @@ class GetStats implements CLICommandInterface { 'simplified_summary', ]; + /** + * GetStats constructor. + * + * @param mixed|WP_CLI $wp_cli The WP-CLI instance. + */ + public function __construct( $wp_cli = null ) { + $this->wp_cli = $wp_cli ?? new WP_CLI(); + } + /** * Get the name of the command. * @@ -73,7 +90,7 @@ public static function get_args(): array { [ 'type' => 'assoc', 'name' => 'stat', - 'description' => 'Keys to show in the results. Defaults to all keys. Pass items in as a comma separated list if you want multiple. Valid keys are: ' . implode( ', ', ( new self() )->valid_stats ) . '.', + 'description' => 'Keys to show in the results. Defaults to all keys. Pass items in as a comma separated list if you want multiple. Valid keys are: ' . implode( ', ', self::$valid_stats ) . '.', 'optional' => true, 'default' => null, 'repeating' => true, @@ -99,21 +116,24 @@ public function __invoke( array $options = [], array $arguments = [] ) { $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { - WP_CLI::error( "Post ID {$post_id} does not exist." ); + $this->wp_cli::error( "Post ID {$post_id} does not exist." ); + return; } if ( class_exists( 'EDAC\Inc\Summary_Generator' ) === false ) { - WP_CLI::error( "Summary_Generator class not found, is Accessibility Checker installed and activated?.\n" ); + $this->wp_cli::error( "Summary_Generator class not found, is Accessibility Checker installed and activated?.\n" ); + return; } $stats = ( new Summary_Generator( $post_id ) )->generate_summary(); if ( empty( $stats ) ) { - WP_CLI::error( "No stats found for post ID {$post_id}." ); + $this->wp_cli::error( "No stats found for post ID {$post_id}." ); + return; } if ( 100 === (int) $stats['passed_tests'] && 0 === (int) $stats['ignored'] ) { - WP_CLI::success( "Either the post is not yet scanned or all tests passed for post ID {$post_id}." ); + $this->wp_cli::success( "Either the post is not yet scanned or all tests passed for post ID {$post_id}." ); return; } @@ -122,21 +142,23 @@ public function __invoke( array $options = [], array $arguments = [] ) { $requested_stats = explode( ',', $arguments['stat'] ); foreach ( $requested_stats as $key ) { $stats_key = trim( $key ); - if ( ! in_array( $stats_key, $this->valid_stats, true ) ) { - WP_CLI::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', $this->valid_stats ) . '.' ); + if ( ! in_array( $stats_key, self::$valid_stats, true ) ) { + $this->wp_cli::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', $this->valid_stats ) . '.' ); + return; } if ( ! isset( $stats[ $stats_key ] ) ) { - WP_CLI::error( "Stat key: {$stats_key} not found in stats." ); + $this->wp_cli::error( "Stat key: {$stats_key} not found in stats." ); + return; } $items_to_return[ $stats_key ] = $stats[ $stats_key ]; } if ( $items_to_return ) { - WP_CLI::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); + $this->wp_cli::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); return; } } - WP_CLI::success( wp_json_encode( $stats, JSON_PRETTY_PRINT ) ); + $this->wp_cli::success( wp_json_encode( $stats, JSON_PRETTY_PRINT ) ); } } From 43335846226f662b4f6a92b3268db3615d37cde7 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:17:58 +0100 Subject: [PATCH 18/38] Add tests for GetStats class --- .../classes/WPCLI/Commands/GetStatsTest.php | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php new file mode 100644 index 00000000..47a56069 --- /dev/null +++ b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php @@ -0,0 +1,158 @@ +register(); + $this->get_stats = new GetStats( $wp_cli ); + + // create database tables if they don't exist. + ( new Update_Database() )->edac_update_database(); + + parent::setUp(); + } + + /** + * Drop the table to clean up after tests. + * + * @return void + */ + protected function tearDown(): void { + $this->drop_table(); + + parent::tearDown(); + } + + /** + * Test the get stats command errors if the post doesn't exist/ + */ + public function test_get_stats_command_errors_when_post_does_not_exist() { + + $non_existent_id = 132456789; + + ob_start(); + $this->get_stats->__invoke( [ $non_existent_id ], [] ); + $stats = ob_get_clean(); + + // check we have the expected error. + $this->assertEquals( 'Error: Post ID ' . $non_existent_id . ' does not exist.', $stats ); + } + + /** + * Test the get stats command errors if it can't get the stats like if the + * database is broken. + */ + public function test_get_stats_command_errors_when_issues_query_cant_complete() { + + $post_id = $this->factory()->post->create(); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [] ); + $stats = ob_get_clean(); + + $this->drop_table(); + + // check we have the expected error. + $this->assertEquals( 'Error: Post ID ' . $post_id . ' does not exist.', $stats ); + } + + /** + * Test the get stats command can complete when no stats exist for a post. + */ + public function test_get_stats_command_completes_when_no_stats_exist_for_post() { + + $post_id = $this->factory()->post->create(); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [] ); + $stats = ob_get_clean(); + + $this->assertStringStartsWith( 'Success: Either the post is not yet scanned or all tests passed', $stats ); + } + + /** + * Test the get stats command can get stats for a post can get the stats. + */ + public function test_get_stats_command_returns_results_when_stats_exist_for_post() { + $post_id = $this->factory()->post->create(); + $post = get_post( $post_id ); + + $this->insert_issue_to_db( $post ); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [] ); + $stats = ob_get_clean(); + + $this->assertStringStartsWith( 'Success: {', $stats ); + } + + /** + * Insert a record to the database for a given post. + * + * @param WP_Post $post The post to insert the record for. + * + * @return void + */ + private function insert_issue_to_db( WP_Post $post ): void { + + global $wpdb; + $table_name = $wpdb->prefix . 'accessibility_checker'; + $wpdb->insert( // phpcs:ignore WordPress.DB -- using direct query for testing. + $table_name, + [ + 'postid' => $post->ID, + 'siteid' => get_current_blog_id(), + 'type' => $post->post_type, + 'rule' => 'empty_paragraph_tag', + 'ruletype' => 'warning', + 'object' => '

', + 'recordcheck' => 1, + 'user' => get_current_user_id(), + 'ignre' => 0, + 'ignre_user' => null, + 'ignre_date' => null, + 'ignre_comment' => null, + 'ignre_global' => 0, + ] + ); + } + + /** + * Drops the table for the plugin if it exists. + * + * Used for cleanup after tests. + * + * @return void + */ + private function drop_table() { + global $wpdb; + $table_name = $wpdb->prefix . 'accessibility_checker'; + $wpdb->query( 'DROP TABLE IF EXISTS ' . $table_name ); // phpcs:ignore WordPress.DB -- query for a unit test. + } +} From 8af1dbdb4851d9b110851c883abffc222d0fddd1 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:21:01 +0100 Subject: [PATCH 19/38] Reset the should_throw flag in teardown of BootstrapCLI tests --- .../includes/classes/WPCLI/BootstrapCLITest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php index f7704795..a45489c2 100644 --- a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php +++ b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php @@ -28,6 +28,16 @@ protected function setUp(): void { parent::setUp(); } + /** + * Set the should_throw flag back to false after each test. + * + * @return void + */ + protected function tearDown(): void { + WP_CLI::set_add_command_should_throw( false ); + parent::tearDown(); + } + /** * Test the bootstrap CLI command. */ From 35cbc2c76591047b60b4cb65b5066afaa308ae09 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 14:23:46 +0100 Subject: [PATCH 20/38] Catch empty stats in another condition Testing indicates this can't easily happen so no need for a dedicated condition to catch it --- includes/classes/WPCLI/Command/GetStats.php | 10 ++++------ .../classes/WPCLI/BootstrapCLITest.php | 2 +- .../classes/WPCLI/Commands/GetStatsTest.php | 18 ------------------ 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index 768d7ba4..4d70c6c9 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -127,12 +127,10 @@ public function __invoke( array $options = [], array $arguments = [] ) { $stats = ( new Summary_Generator( $post_id ) )->generate_summary(); - if ( empty( $stats ) ) { - $this->wp_cli::error( "No stats found for post ID {$post_id}." ); - return; - } - - if ( 100 === (int) $stats['passed_tests'] && 0 === (int) $stats['ignored'] ) { + if ( + empty( $stats ) || + ( 100 === (int) $stats['passed_tests'] && 0 === (int) $stats['ignored'] ) + ) { $this->wp_cli::success( "Either the post is not yet scanned or all tests passed for post ID {$post_id}." ); return; } diff --git a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php index a45489c2..e2813e93 100644 --- a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php +++ b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php @@ -30,7 +30,7 @@ protected function setUp(): void { /** * Set the should_throw flag back to false after each test. - * + * * @return void */ protected function tearDown(): void { diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php index 47a56069..c208435a 100644 --- a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php +++ b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php @@ -64,24 +64,6 @@ public function test_get_stats_command_errors_when_post_does_not_exist() { $this->assertEquals( 'Error: Post ID ' . $non_existent_id . ' does not exist.', $stats ); } - /** - * Test the get stats command errors if it can't get the stats like if the - * database is broken. - */ - public function test_get_stats_command_errors_when_issues_query_cant_complete() { - - $post_id = $this->factory()->post->create(); - - ob_start(); - $this->get_stats->__invoke( [ $post_id ], [] ); - $stats = ob_get_clean(); - - $this->drop_table(); - - // check we have the expected error. - $this->assertEquals( 'Error: Post ID ' . $post_id . ' does not exist.', $stats ); - } - /** * Test the get stats command can complete when no stats exist for a post. */ From 793e691fa33abee8cfc0223a870572c6bf4b6910 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 15:37:21 +0100 Subject: [PATCH 21/38] Add tests for filtered stat request --- .../classes/WPCLI/Commands/GetStatsTest.php | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php index c208435a..2d531ff6 100644 --- a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php +++ b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php @@ -94,6 +94,72 @@ public function test_get_stats_command_returns_results_when_stats_exist_for_post $this->assertStringStartsWith( 'Success: {', $stats ); } + /** + * Test that the get stats command can get just the requested stats keys. + * + * @return void + */ + public function test_get_stats_can_get_filtered_stats_when_requested() { + $post_id = $this->factory()->post->create(); + $post = get_post( $post_id ); + + $this->insert_issue_to_db( $post ); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [ 'stat' => 'passed_tests' ] ); + $stats = ob_get_clean(); + + // check the output is still a success message. + $this->assertStringStartsWith( 'Success: {', $stats ); + + $stats_array = json_decode( + html_entity_decode( + str_replace( 'Success: ', '', $stats ) + ), + true + ); + + // is only one key long and is the key we requested. + $this->assertCount( 1, $stats_array ); + $this->assertArrayHasKey( 'passed_tests', $stats_array ); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [ 'stat' => 'passed_tests, errors, warnings' ] ); + $stats = ob_get_clean(); + + // check the output is still a success message. + $this->assertStringStartsWith( 'Success: {', $stats ); + + $stats_array = json_decode( + html_entity_decode( + str_replace( 'Success: ', '', $stats ) + ), + true + ); + + // is 3 keys. + $this->assertCount( 3, $stats_array ); + $this->assertArrayHasKey( 'passed_tests', $stats_array ); + $this->assertArrayHasKey( 'errors', $stats_array ); + $this->assertArrayHasKey( 'warnings', $stats_array ); + } + + /** + * Test the get stats command errors when filtered stats are requested for non-existent keys. + */ + public function test_get_stats_errors_when_filtered_stats_are_requested_for_non_existent_keys() { + $post_id = $this->factory()->post->create(); + $post = get_post( $post_id ); + + $this->insert_issue_to_db( $post ); + + ob_start(); + $this->get_stats->__invoke( [ $post_id ], [ 'stat' => 'a_non_existant_stat' ] ); + $stats = ob_get_clean(); + + $this->assertStringStartsWith( 'Error: Invalid stat key', $stats ); + } + /** * Insert a record to the database for a given post. * From 98af8613e549561ec9c17484773bed7fda353cae Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 15:38:00 +0100 Subject: [PATCH 22/38] No need for a dedicated catch block for stats not found, groping it with invalid keys --- includes/classes/WPCLI/Command/GetStats.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index 4d70c6c9..10a76801 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -140,12 +140,8 @@ public function __invoke( array $options = [], array $arguments = [] ) { $requested_stats = explode( ',', $arguments['stat'] ); foreach ( $requested_stats as $key ) { $stats_key = trim( $key ); - if ( ! in_array( $stats_key, self::$valid_stats, true ) ) { - $this->wp_cli::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', $this->valid_stats ) . '.' ); - return; - } - if ( ! isset( $stats[ $stats_key ] ) ) { - $this->wp_cli::error( "Stat key: {$stats_key} not found in stats." ); + if ( ! in_array( $stats_key, self::$valid_stats, true ) || ! isset( $stats[ $stats_key ] ) ) { + $this->wp_cli::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', self::$valid_stats ) . '.' ); return; } $items_to_return[ $stats_key ] = $stats[ $stats_key ]; From e4fcc872ce58355a1e0ac9c083fdc0ca6482a735 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 15:39:33 +0100 Subject: [PATCH 23/38] Remove bail for non-existent class. It couldn't be reached in testing --- includes/classes/WPCLI/Command/GetStats.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index 10a76801..bea3e2e6 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -120,11 +120,6 @@ public function __invoke( array $options = [], array $arguments = [] ) { return; } - if ( class_exists( 'EDAC\Inc\Summary_Generator' ) === false ) { - $this->wp_cli::error( "Summary_Generator class not found, is Accessibility Checker installed and activated?.\n" ); - return; - } - $stats = ( new Summary_Generator( $post_id ) )->generate_summary(); if ( From f238b860093c2a2cbaa95b468fb747a5db16cef5 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 17:26:53 +0100 Subject: [PATCH 24/38] Move mocks and database helpers to dedicated directory in tests folder and use autoload for theme --- composer.json | 5 +- tests/phpunit/TestHelpers/DatabaseHelpers.php | 71 +++++++++++++++++++ .../{ => TestHelpers}/Mocks/Mock_WP_CLI.php | 2 +- .../classes/WPCLI/BootstrapCLITest.php | 4 +- .../classes/WPCLI/Commands/GetStatsTest.php | 58 ++------------- 5 files changed, 84 insertions(+), 56 deletions(-) create mode 100644 tests/phpunit/TestHelpers/DatabaseHelpers.php rename tests/phpunit/{ => TestHelpers}/Mocks/Mock_WP_CLI.php (97%) diff --git a/composer.json b/composer.json index 8690bc95..18b184ff 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,10 @@ } }, "autoload-dev": { - "classmap": [] + "classmap": [], + "psr-4": { + "EqualizeDigital\\AccessibilityChecker\\Tests\\": "tests/phpunit/" + } }, "scripts": { "lint": [ diff --git a/tests/phpunit/TestHelpers/DatabaseHelpers.php b/tests/phpunit/TestHelpers/DatabaseHelpers.php new file mode 100644 index 00000000..221e1a18 --- /dev/null +++ b/tests/phpunit/TestHelpers/DatabaseHelpers.php @@ -0,0 +1,71 @@ +edac_update_database(); + } + /** + * Insert a record to the database for a given post. + * + * @param WP_Post $post The post to insert the record for. + * + * @return void + */ + public static function insert_test_issue_to_db( WP_Post $post ): void { + + global $wpdb; + $table_name = $wpdb->prefix . 'accessibility_checker'; + $wpdb->insert( // phpcs:ignore WordPress.DB -- using direct query for testing. + $table_name, + [ + 'postid' => $post->ID, + 'siteid' => get_current_blog_id(), + 'type' => $post->post_type, + 'rule' => 'empty_paragraph_tag', + 'ruletype' => 'warning', + 'object' => '

', + 'recordcheck' => 1, + 'user' => get_current_user_id(), + 'ignre' => 0, + 'ignre_user' => null, + 'ignre_date' => null, + 'ignre_comment' => null, + 'ignre_global' => 0, + ] + ); + } + + /** + * Drops the table for the plugin if it exists. + * + * Used for cleanup after tests. + * + * @return void + */ + public static function drop_table() { + global $wpdb; + $table_name = $wpdb->prefix . 'accessibility_checker'; + $wpdb->query( 'DROP TABLE IF EXISTS ' . $table_name ); // phpcs:ignore WordPress.DB -- query for a unit test. + } +} diff --git a/tests/phpunit/Mocks/Mock_WP_CLI.php b/tests/phpunit/TestHelpers/Mocks/Mock_WP_CLI.php similarity index 97% rename from tests/phpunit/Mocks/Mock_WP_CLI.php rename to tests/phpunit/TestHelpers/Mocks/Mock_WP_CLI.php index 827d7bd7..728a713b 100644 --- a/tests/phpunit/Mocks/Mock_WP_CLI.php +++ b/tests/phpunit/TestHelpers/Mocks/Mock_WP_CLI.php @@ -5,7 +5,7 @@ * @package Accessibility_Checker */ -namespace EqualizeDigital\AccessibilityChecker\Tests\Mocks; +namespace EqualizeDigital\AccessibilityChecker\Tests\TestHelpers\Mocks; use Exception; use stdClass; diff --git a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php index e2813e93..97ae21c6 100644 --- a/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php +++ b/tests/phpunit/includes/classes/WPCLI/BootstrapCLITest.php @@ -5,7 +5,7 @@ * @package Accessibility_Checker */ -use EqualizeDigital\AccessibilityChecker\Tests\Mocks\Mock_WP_CLI as WP_CLI; +use EqualizeDigital\AccessibilityChecker\Tests\TestHelpers\Mocks\Mock_WP_CLI as WP_CLI; use EqualizeDigital\AccessibilityChecker\WPCLI\BootstrapCLI; /** @@ -20,8 +20,6 @@ class BootstrapCLITest extends WP_UnitTestCase { * Makes the mock available and sets the constant like WP_CLI would in a real environment. */ protected function setUp(): void { - require_once dirname( __DIR__, 3 ) . '/Mocks/Mock_WP_CLI.php'; - // since this is a synthetic run on WP-CLI, we need to define WP_CLI. if ( ! defined( 'WP_CLI' ) ) { define( 'WP_CLI', true ); } diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php index 2d531ff6..fd146694 100644 --- a/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php +++ b/tests/phpunit/includes/classes/WPCLI/Commands/GetStatsTest.php @@ -6,7 +6,8 @@ */ use EDAC\Admin\Update_Database; -use EqualizeDigital\AccessibilityChecker\Tests\Mocks\Mock_WP_CLI; +use EqualizeDigital\AccessibilityChecker\Tests\TestHelpers\DatabaseHelpers; +use EqualizeDigital\AccessibilityChecker\Tests\TestHelpers\Mocks\Mock_WP_CLI; use EqualizeDigital\AccessibilityChecker\WPCLI\BootstrapCLI; use EqualizeDigital\AccessibilityChecker\WPCLI\Command\GetStats; @@ -21,7 +22,6 @@ class GetStatsTest extends WP_UnitTestCase { * @return void */ protected function setUp(): void { - require_once dirname( __DIR__, 4 ) . '/Mocks/Mock_WP_CLI.php'; // since this is a synthetic run on WP-CLI, we need to define WP_CLI. if ( ! defined( 'WP_CLI' ) ) { define( 'WP_CLI', true ); @@ -33,7 +33,7 @@ protected function setUp(): void { $this->get_stats = new GetStats( $wp_cli ); // create database tables if they don't exist. - ( new Update_Database() )->edac_update_database(); + DatabaseHelpers::create_table(); parent::setUp(); } @@ -44,7 +44,7 @@ protected function setUp(): void { * @return void */ protected function tearDown(): void { - $this->drop_table(); + DatabaseHelpers::drop_table(); parent::tearDown(); } @@ -85,7 +85,7 @@ public function test_get_stats_command_returns_results_when_stats_exist_for_post $post_id = $this->factory()->post->create(); $post = get_post( $post_id ); - $this->insert_issue_to_db( $post ); + DatabaseHelpers::insert_test_issue_to_db( $post ); ob_start(); $this->get_stats->__invoke( [ $post_id ], [] ); @@ -103,7 +103,7 @@ public function test_get_stats_can_get_filtered_stats_when_requested() { $post_id = $this->factory()->post->create(); $post = get_post( $post_id ); - $this->insert_issue_to_db( $post ); + DatabaseHelpers::insert_test_issue_to_db( $post ); ob_start(); $this->get_stats->__invoke( [ $post_id ], [ 'stat' => 'passed_tests' ] ); @@ -151,7 +151,7 @@ public function test_get_stats_errors_when_filtered_stats_are_requested_for_non_ $post_id = $this->factory()->post->create(); $post = get_post( $post_id ); - $this->insert_issue_to_db( $post ); + DatabaseHelpers::insert_test_issue_to_db( $post ); ob_start(); $this->get_stats->__invoke( [ $post_id ], [ 'stat' => 'a_non_existant_stat' ] ); @@ -159,48 +159,4 @@ public function test_get_stats_errors_when_filtered_stats_are_requested_for_non_ $this->assertStringStartsWith( 'Error: Invalid stat key', $stats ); } - - /** - * Insert a record to the database for a given post. - * - * @param WP_Post $post The post to insert the record for. - * - * @return void - */ - private function insert_issue_to_db( WP_Post $post ): void { - - global $wpdb; - $table_name = $wpdb->prefix . 'accessibility_checker'; - $wpdb->insert( // phpcs:ignore WordPress.DB -- using direct query for testing. - $table_name, - [ - 'postid' => $post->ID, - 'siteid' => get_current_blog_id(), - 'type' => $post->post_type, - 'rule' => 'empty_paragraph_tag', - 'ruletype' => 'warning', - 'object' => '

', - 'recordcheck' => 1, - 'user' => get_current_user_id(), - 'ignre' => 0, - 'ignre_user' => null, - 'ignre_date' => null, - 'ignre_comment' => null, - 'ignre_global' => 0, - ] - ); - } - - /** - * Drops the table for the plugin if it exists. - * - * Used for cleanup after tests. - * - * @return void - */ - private function drop_table() { - global $wpdb; - $table_name = $wpdb->prefix . 'accessibility_checker'; - $wpdb->query( 'DROP TABLE IF EXISTS ' . $table_name ); // phpcs:ignore WordPress.DB -- query for a unit test. - } } From 0e9306d1bccf1cb71b61a40d85e663eec9dc933f Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 17:27:39 +0100 Subject: [PATCH 25/38] Allow injecting a WP_CLI-like object to DeleteStats and GetSiteStats --- .../classes/WPCLI/Command/DeleteStats.php | 31 ++++++++++++++----- .../classes/WPCLI/Command/GetSiteStats.php | 26 +++++++++++++--- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/includes/classes/WPCLI/Command/DeleteStats.php b/includes/classes/WPCLI/Command/DeleteStats.php index ec13d0d8..3ba939ae 100644 --- a/includes/classes/WPCLI/Command/DeleteStats.php +++ b/includes/classes/WPCLI/Command/DeleteStats.php @@ -20,6 +20,24 @@ */ class DeleteStats implements CLICommandInterface { + /** + * The WP-CLI instance. + * + * This lets a mock be passed in for testing. + * + * @var mixed|WP_CLI + */ + private $wp_cli; + + /** + * GetStats constructor. + * + * @param mixed|WP_CLI $wp_cli The WP-CLI instance. + */ + public function __construct( $wp_cli = null ) { + $this->wp_cli = $wp_cli ?? new WP_CLI(); + } + /** * Get the name of the command * @@ -59,23 +77,20 @@ public static function get_args(): array { * @throws ExitException If the post ID is not provided, does not exist, or the class we need isn't available. */ public function __invoke( array $options = [], array $arguments = [] ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $post_id = $options[0] ?? null; + $post_id = $options[0] ?? 0; if ( 0 === $post_id ) { - WP_CLI::error( "No Post ID provided, getting all stats not implemented yet.\n" ); + $this->wp_cli::error( "No Post ID provided.\n" ); } $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { - WP_CLI::error( "Post ID {$post_id} does not exist.\n" ); - } - - if ( class_exists( 'EDAC\Admin\Purge_Post_Data' ) === false ) { - WP_CLI::error( "Purge_Post_Data class not found, is Accessibility Checker installed and activated?\n" ); + $this->wp_cli::error( "Post ID {$post_id} does not exist.\n" ); + return; } Purge_Post_Data::delete_post( $post_id ); - WP_CLI::success( "Stats of {$post_id} deleted! \n" ); + $this->wp_cli::success( "Stats of {$post_id} deleted! \n" ); } } diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 951a844e..643e9600 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -22,6 +22,24 @@ */ class GetSiteStats implements CLICommandInterface { + /** + * The WP-CLI instance. + * + * This lets a mock be passed in for testing. + * + * @var mixed|WP_CLI + */ + private $wp_cli; + + /** + * GetStats constructor. + * + * @param mixed|WP_CLI $wp_cli The WP-CLI instance. + */ + public function __construct( $wp_cli = null ) { + $this->wp_cli = $wp_cli ?? new WP_CLI(); + } + /** * Get the name of the command. * @@ -82,11 +100,11 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc } if ( $items_to_return ) { - WP_CLI::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); + $this->wp_cli::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); return; } - WP_CLI::success( wp_json_encode( $all_stats, JSON_PRETTY_PRINT ) ); + $this->wp_cli::success( wp_json_encode( $all_stats, JSON_PRETTY_PRINT ) ); } /** @@ -104,13 +122,13 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc private function get_all_stats(): array { if ( class_exists( 'EDAC\Admin\Scans_Stats' ) === false ) { - WP_CLI::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); + $this->wp_cli::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); } $stats = ( new Scans_Stats() )->summary(); if ( empty( $stats ) ) { - WP_CLI::error( "No stats found.\n" ); + $this->wp_cli::error( "No stats found.\n" ); } return $stats; From 096c65c5b447c1e2e744ec048e7ff06c1c0b7f02 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 17:31:05 +0100 Subject: [PATCH 26/38] Add tests to cover the delete-stats command --- .../WPCLI/Commands/DeleteStatsTest.php | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php new file mode 100644 index 00000000..e9897e63 --- /dev/null +++ b/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php @@ -0,0 +1,80 @@ +delete_stats = new DeleteStats( new Mock_WP_CLI() ); + DatabaseHelpers::create_table(); + parent::setUp(); + } + + /** + * Drop the table to clean up after tests. + */ + protected function tearDown(): void { + DatabaseHelpers::drop_table(); + parent::tearDown(); + } + + /** + * Test the delete stats command errors if the post doesn't exist. + */ + public function test_delete_stats_command_errors_when_post_does_not_exist() { + $non_existent_id = 132456789; + + ob_start(); + $this->delete_stats->__invoke( [ $non_existent_id ], [] ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'Error: Post ID ' . $non_existent_id . ' does not exist', $output ); + } + + /** + * Test the delete stats command errors if no post ID is passed. + */ + public function test_delete_stats_command_errors_when_no_id_is_passed() { + ob_start(); + $this->delete_stats->__invoke( [], [] ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'Error: No Post ID provided.', $output ); + } + + /** + * Test the delete stats command deletes the stats. + */ + public function test_delete_stats_command_deletes_stats() { + $post_id = $this->factory()->post->create(); + DatabaseHelpers::insert_test_issue_to_db( get_post( $post_id ) ); + + global $wpdb; + $table_name = $wpdb->prefix . 'accessibility_checker'; + $stats_before = $wpdb->get_results( "SELECT * FROM $table_name WHERE postid = $post_id" ); // phpcs:ignore WordPress.DB -- Querying for testing purposes. + $this->assertEquals( 1, count( $stats_before ) ); + + ob_start(); + $this->delete_stats->__invoke( [ $post_id ], [] ); + $output = ob_get_clean(); + + $this->assertStringStartsWith( 'Success: Stats of ' . $post_id . ' deleted!', $output ); + + // make sure the issue is actually deleted from the database. + $stats_after = $wpdb->get_results( "SELECT * FROM $table_name WHERE postid = $post_id" ); // phpcs:ignore WordPress.DB -- Querying for testing purposes. + $this->assertEmpty( $stats_after ); + } +} From 883063390a0770a5351e73c1775a690e2a2e2f06 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 17:31:47 +0100 Subject: [PATCH 27/38] Use a nested namespace for test helpers so that test classes don't notify of incorrect PSR-4 namespace --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 18b184ff..d53f17cd 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "autoload-dev": { "classmap": [], "psr-4": { - "EqualizeDigital\\AccessibilityChecker\\Tests\\": "tests/phpunit/" + "EqualizeDigital\\AccessibilityChecker\\Tests\\TestHelpers\\": "tests/phpunit/TestHelpers/" } }, "scripts": { From 8a409074272bf2547c00f4fc0e98cf2c548c8e54 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:03:28 +0100 Subject: [PATCH 28/38] Use isset to check items_to_return --- includes/classes/WPCLI/Command/GetSiteStats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 643e9600..5b57a883 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -99,7 +99,7 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc } } - if ( $items_to_return ) { + if ( isset( $items_to_return ) ) { $this->wp_cli::success( wp_json_encode( $items_to_return, JSON_PRETTY_PRINT ) ); return; } From 951473a0f4e18673329f5e558bba873e90af68e0 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:03:49 +0100 Subject: [PATCH 29/38] Add cache clear support to the command --- includes/classes/WPCLI/Command/GetSiteStats.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 5b57a883..372ecc59 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -69,6 +69,14 @@ public static function get_args(): array { 'default' => null, 'repeating' => true, ], + [ + 'type' => 'assoc', + 'name' => 'clear-cache', + 'description' => 'Clear the cache before retrieving the stats (can be intensive).', + 'optional' => true, + 'default' => true, + 'repeating' => false, + ], ], ]; } @@ -85,6 +93,11 @@ public static function get_args(): array { * @throws ExitException If the post ID does not exist, or the class we need isn't available. */ public function __invoke( array $options = [], array $arguments = [] ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + if ( ! empty( $arguments['clear-cache'] ) ) { + // Clear the cache. + ( new Scans_Stats() )->clear_cache(); + } + $all_stats = $this->get_all_stats(); if ( ! empty( $arguments['stat'] ) ) { From 8c9d1e2ed0f97610c497bb26545983ab1c5bc5bc Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:04:04 +0100 Subject: [PATCH 30/38] Fix missed WP_CLI dependency use --- includes/classes/WPCLI/Command/GetSiteStats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 372ecc59..96483130 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -106,7 +106,7 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc foreach ( $requested_stats as $key ) { $stats_key = trim( $key ); if ( ! isset( $all_stats[ $stats_key ] ) ) { - WP_CLI::error( "Stat key: {$stats_key} not found in stats." ); + $this->wp_cli::error( "Stat key: {$stats_key} not found in stats." ); } $items_to_return[ $stats_key ] = $all_stats[ $stats_key ]; } From cbfc1c1db71dd31a0ae11a128a76454bdd1b710e Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:08:32 +0100 Subject: [PATCH 31/38] Add tests for get-site-stats command class --- .../WPCLI/Commands/GetSiteStatsTest.php | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tests/phpunit/includes/classes/WPCLI/Commands/GetSiteStatsTest.php diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/GetSiteStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/GetSiteStatsTest.php new file mode 100644 index 00000000..82fc2929 --- /dev/null +++ b/tests/phpunit/includes/classes/WPCLI/Commands/GetSiteStatsTest.php @@ -0,0 +1,120 @@ +get_site_stats = new GetSiteStats( new Mock_WP_CLI() ); + + DatabaseHelpers::create_table(); + + parent::setUp(); + } + + /** + * Drop the table to clean up after tests. + */ + protected function tearDown(): void { + DatabaseHelpers::drop_table(); + parent::tearDown(); + } + + /** + * Test the get site stats command can complete when run. + */ + public function test_get_site_stats_command_can_complete() { + ob_start(); + $this->get_site_stats->__invoke( [], [] ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'Success: {', $output ); + } + + /** + * Test the get site stats command can return filtered stats for one key or multiple keys. + */ + public function test_get_site_stats_command_can_return_filtered_stats() { + + ob_start(); + $this->get_site_stats->__invoke( [], [ 'stat' => 'rule_count' ] ); + $stats = ob_get_clean(); + + $this->assertStringContainsString( 'Success: {', $stats ); + + $this->assertStringContainsString( 'Success: {', $stats ); + + $stats_array = json_decode( + html_entity_decode( + str_replace( 'Success: ', '', $stats ) + ), + true + ); + + $this->assertEquals( 1, count( $stats_array ) ); + $this->assertArrayHasKey( 'rule_count', $stats_array ); + + ob_start(); + $this->get_site_stats->__invoke( [], [ 'stat' => 'rule_count,tests_count' ] ); + $stats = ob_get_clean(); + + $this->assertStringContainsString( 'Success: {', $stats ); + + $stats_array = json_decode( + html_entity_decode( + str_replace( 'Success: ', '', $stats ) + ), + true + ); + + $this->assertEquals( 2, count( $stats_array ) ); + $this->assertArrayHasKey( 'rule_count', $stats_array ); + $this->assertArrayHasKey( 'tests_count', $stats_array ); + } + + /** + * Test the get site stats command errors if the requested stat key doesn't exist. + */ + public function test_get_site_stats_command_errors_when_stat_key_does_not_exist() { + ob_start(); + $this->get_site_stats->__invoke( [], [ 'stat' => 'non_existent_key' ] ); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'Error: Stat key: non_existent_key not found in stats.', $output ); + } + + /** + * Test the get site stats command can clear the cache. + */ + public function test_get_site_stats_command_can_clear_cache() { + ob_start(); + $this->get_site_stats->__invoke( [], [] ); + $stats_initial = ob_get_clean(); + + DatabaseHelpers::insert_test_issue_to_db( get_post( $this->factory()->post->create() ) ); + + ob_start(); + $this->get_site_stats->__invoke( [], [ 'clear-cache' => true ] ); + $stats_after_clear = ob_get_clean(); + + $this->assertNotEquals( $stats_initial, $stats_after_clear ); + } +} From a62ee9ee1a5578066a0f734c1a9c658d71d2cf5f Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:09:22 +0100 Subject: [PATCH 32/38] Remove unneeded function for getting stats - call the method directly instead --- .../classes/WPCLI/Command/GetSiteStats.php | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 96483130..db859af5 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -98,7 +98,7 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc ( new Scans_Stats() )->clear_cache(); } - $all_stats = $this->get_all_stats(); + $all_stats = ( new Scans_Stats() )->summary(); if ( ! empty( $arguments['stat'] ) ) { $items_to_return = []; @@ -119,31 +119,4 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc $this->wp_cli::success( wp_json_encode( $all_stats, JSON_PRETTY_PRINT ) ); } - - /** - * Gets the sites from the entire site. - * - * A limitation is this can only provide the stats for scanned pages, if - * some pages are not scanned they are not reflected in the stats. Use the - * 'scannable_posts_count' and the 'posts_scanned' values to determine if - * the whole site is reflected or not. - * - * @since 1.15.0 - * - * @throws ExitException If ScanStats class is not found or no stats are found. - */ - private function get_all_stats(): array { - - if ( class_exists( 'EDAC\Admin\Scans_Stats' ) === false ) { - $this->wp_cli::error( "Scans_Stats class not found, is Accessibility Checker installed and activated?.\n" ); - } - - $stats = ( new Scans_Stats() )->summary(); - - if ( empty( $stats ) ) { - $this->wp_cli::error( "No stats found.\n" ); - } - - return $stats; - } } From 38b6068d7fba6396d4c23767dc098a2c4f5c38e5 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 18:09:40 +0100 Subject: [PATCH 33/38] Return early when stats key not found --- includes/classes/WPCLI/Command/GetSiteStats.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index db859af5..d123bdc7 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -107,6 +107,7 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc $stats_key = trim( $key ); if ( ! isset( $all_stats[ $stats_key ] ) ) { $this->wp_cli::error( "Stat key: {$stats_key} not found in stats." ); + return; } $items_to_return[ $stats_key ] = $all_stats[ $stats_key ]; } From 188c4cb402df905f3accc9e42a124a22a7532e93 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Wed, 10 Jul 2024 19:37:02 +0100 Subject: [PATCH 34/38] Wrap strings in commands translation functions --- includes/classes/WPCLI/BootstrapCLI.php | 2 +- .../classes/WPCLI/Command/DeleteStats.php | 20 ++++++++--- .../classes/WPCLI/Command/GetSiteStats.php | 12 +++++-- includes/classes/WPCLI/Command/GetStats.php | 34 ++++++++++++++++--- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/includes/classes/WPCLI/BootstrapCLI.php b/includes/classes/WPCLI/BootstrapCLI.php index ef5abd67..35e9f11b 100644 --- a/includes/classes/WPCLI/BootstrapCLI.php +++ b/includes/classes/WPCLI/BootstrapCLI.php @@ -96,7 +96,7 @@ public function register() { $this->wp_cli::warning( sprintf( // translators: 1: a php classname, 2: an error message that was thrown about why this failed to register. - __( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), + esc_html__( 'Failed to register command %1$s because %2$s', 'accessibility-checker' ), $command, $e->getMessage() ) diff --git a/includes/classes/WPCLI/Command/DeleteStats.php b/includes/classes/WPCLI/Command/DeleteStats.php index 3ba939ae..c4a42c48 100644 --- a/includes/classes/WPCLI/Command/DeleteStats.php +++ b/includes/classes/WPCLI/Command/DeleteStats.php @@ -58,7 +58,7 @@ public static function get_args(): array { [ 'type' => 'positional', 'name' => 'post_id', - 'description' => 'The ID of the post to delete stats for.', + 'description' => esc_html__( 'The ID of the post to delete stats for.', 'accessibility-checker' ), 'optional' => true, 'default' => 0, 'repeating' => false, @@ -80,17 +80,29 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc $post_id = $options[0] ?? 0; if ( 0 === $post_id ) { - $this->wp_cli::error( "No Post ID provided.\n" ); + $this->wp_cli::error( esc_html__( 'No Post ID provided.', 'accessibility-checker' ) ); } $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { - $this->wp_cli::error( "Post ID {$post_id} does not exist.\n" ); + $this->wp_cli::error( + sprintf( + // translators: 1: a post ID. + esc_html__( 'Post ID %1$s does not exist.', 'accessibility-checker' ), + $post_id + ) + ); return; } Purge_Post_Data::delete_post( $post_id ); - $this->wp_cli::success( "Stats of {$post_id} deleted! \n" ); + $this->wp_cli::success( + sprintf( + // translators: 1: a post ID. + esc_html__( 'Stats of %1$s deleted.', 'accessibility-checker' ), + $post_id + ) + ); } } diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index d123bdc7..831d0d5c 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -64,7 +64,7 @@ public static function get_args(): array { [ 'type' => 'assoc', 'name' => 'stat', - 'description' => 'Keys to show in the results. Defaults to all keys.', + 'description' => esc_html__( 'Keys to show in the results. Defaults to all keys.', 'accessibility-checker' ), 'optional' => true, 'default' => null, 'repeating' => true, @@ -72,7 +72,7 @@ public static function get_args(): array { [ 'type' => 'assoc', 'name' => 'clear-cache', - 'description' => 'Clear the cache before retrieving the stats (can be intensive).', + 'description' => esc_html__( 'Clear the cache before retrieving the stats (can be intensive).', 'accessibility-checker' ), 'optional' => true, 'default' => true, 'repeating' => false, @@ -106,7 +106,13 @@ public function __invoke( array $options = [], array $arguments = [] ) { // phpc foreach ( $requested_stats as $key ) { $stats_key = trim( $key ); if ( ! isset( $all_stats[ $stats_key ] ) ) { - $this->wp_cli::error( "Stat key: {$stats_key} not found in stats." ); + $this->wp_cli::error( + sprintf( + // translators: 1: a stat key that was requested but not found. + esc_html__( 'Stat key: %1$s not found in stats.', 'accessibility-checker' ), + $stats_key + ) + ); return; } $items_to_return[ $stats_key ] = $all_stats[ $stats_key ]; diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index bea3e2e6..67f9fb7e 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -82,7 +82,7 @@ public static function get_args(): array { [ 'type' => 'positional', 'name' => 'post_id', - 'description' => 'The ID of the post to get stats for.', + 'description' => esc_html__( 'The ID of the post to get stats for.', 'accessibility-checker' ), 'optional' => true, 'default' => 0, 'repeating' => false, @@ -90,7 +90,11 @@ public static function get_args(): array { [ 'type' => 'assoc', 'name' => 'stat', - 'description' => 'Keys to show in the results. Defaults to all keys. Pass items in as a comma separated list if you want multiple. Valid keys are: ' . implode( ', ', self::$valid_stats ) . '.', + 'description' => sprintf( + // translators: 1: a comma separated list of valid stats keys that should not be translated. + 'Keys to show in the results. Defaults to all keys. Pass items in as a comma separated list if you want multiple. Valid keys are: %1$s.', + implode( ', ', self::$valid_stats ) + ), 'optional' => true, 'default' => null, 'repeating' => true, @@ -116,7 +120,13 @@ public function __invoke( array $options = [], array $arguments = [] ) { $post_exists = (bool) get_post( $post_id ); if ( ! $post_exists ) { - $this->wp_cli::error( "Post ID {$post_id} does not exist." ); + $this->wp_cli::error( + sprintf( + // translators: 1: a post ID. + esc_html__( 'Post ID %1$d does not exist.', 'accessibility-checker' ), + $post_id + ) + ); return; } @@ -126,7 +136,13 @@ public function __invoke( array $options = [], array $arguments = [] ) { empty( $stats ) || ( 100 === (int) $stats['passed_tests'] && 0 === (int) $stats['ignored'] ) ) { - $this->wp_cli::success( "Either the post is not yet scanned or all tests passed for post ID {$post_id}." ); + $this->wp_cli::success( + sprintf( + // translators: 1: a post ID. + esc_html__( 'Either the post is not yet scanned or all tests passed for post ID %1$d.', 'accessibility-checker' ), + $post_id + ) + ); return; } @@ -136,7 +152,15 @@ public function __invoke( array $options = [], array $arguments = [] ) { foreach ( $requested_stats as $key ) { $stats_key = trim( $key ); if ( ! in_array( $stats_key, self::$valid_stats, true ) || ! isset( $stats[ $stats_key ] ) ) { - $this->wp_cli::error( "Invalid stat key: {$stats_key}. Valid keys are: " . implode( ', ', self::$valid_stats ) . '.' ); + $this->wp_cli::error( + sprintf( + // translators: 1: a stat key, 2: a comma separated list of valid stats keys. + 'Invalid stat key: %1$s. Valid keys are: %2$s.', + $stats_key, + implode( ', ', self::$valid_stats ) + ) + ); + return; } $items_to_return[ $stats_key ] = $stats[ $stats_key ]; From 447852a65d35a0b6d20d6eb4f5d900062ef0b0cc Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Fri, 12 Jul 2024 15:51:33 +0100 Subject: [PATCH 35/38] Make the help text for the commands more useful --- includes/classes/WPCLI/Command/DeleteStats.php | 2 +- includes/classes/WPCLI/Command/GetSiteStats.php | 2 +- includes/classes/WPCLI/Command/GetStats.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/classes/WPCLI/Command/DeleteStats.php b/includes/classes/WPCLI/Command/DeleteStats.php index c4a42c48..24d058d9 100644 --- a/includes/classes/WPCLI/Command/DeleteStats.php +++ b/includes/classes/WPCLI/Command/DeleteStats.php @@ -68,7 +68,7 @@ public static function get_args(): array { } /** - * Run the command to delete stats for a given post id. + * Delete the accessibility-checker stats for a given post ID. * * @param array $options This is the positional argument, the post ID in this case. * @param array $arguments This is the associative argument, not used in this command but kept for consistency with cli commands using this pattern. diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 831d0d5c..fd1ff8c6 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -82,7 +82,7 @@ public static function get_args(): array { } /** - * Run the command that gets the stats for the whole site. + * Gets the accessibility-checker stats for the whole site. Use the --clear-cache flag to clear the cache before retrieving the stats. * * @since 1.15.0 * diff --git a/includes/classes/WPCLI/Command/GetStats.php b/includes/classes/WPCLI/Command/GetStats.php index 67f9fb7e..4cd45a5f 100644 --- a/includes/classes/WPCLI/Command/GetStats.php +++ b/includes/classes/WPCLI/Command/GetStats.php @@ -104,7 +104,7 @@ public static function get_args(): array { } /** - * Run the command that gets the stats for a post ID or the stats for the whole site. + * Gets the accessibility-checker stats for a given post ID. * * @since 1.15.0 * From 8076a56baf3456e42a2f4ce5f9341fdf4574e447 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Fri, 12 Jul 2024 15:51:58 +0100 Subject: [PATCH 36/38] Make --clear-cache a true flag and not an associative param --- includes/classes/WPCLI/Command/GetSiteStats.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index fd1ff8c6..758eba0d 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -70,11 +70,9 @@ public static function get_args(): array { 'repeating' => true, ], [ - 'type' => 'assoc', + 'type' => 'flag', 'name' => 'clear-cache', 'description' => esc_html__( 'Clear the cache before retrieving the stats (can be intensive).', 'accessibility-checker' ), - 'optional' => true, - 'default' => true, 'repeating' => false, ], ], From 7ff5681a26043c5966936ee674eb465d6836e89a Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Fri, 12 Jul 2024 15:56:07 +0100 Subject: [PATCH 37/38] Update the test to check the correct string after updating translations --- .../phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php b/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php index e9897e63..dc25055e 100644 --- a/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php +++ b/tests/phpunit/includes/classes/WPCLI/Commands/DeleteStatsTest.php @@ -71,7 +71,7 @@ public function test_delete_stats_command_deletes_stats() { $this->delete_stats->__invoke( [ $post_id ], [] ); $output = ob_get_clean(); - $this->assertStringStartsWith( 'Success: Stats of ' . $post_id . ' deleted!', $output ); + $this->assertStringStartsWith( 'Success: Stats of ' . $post_id . ' deleted', $output ); // make sure the issue is actually deleted from the database. $stats_after = $wpdb->get_results( "SELECT * FROM $table_name WHERE postid = $post_id" ); // phpcs:ignore WordPress.DB -- Querying for testing purposes. From 1c305b1a59ba921a1da222ab419772ef4da73f1e Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Fri, 12 Jul 2024 17:15:51 +0100 Subject: [PATCH 38/38] Flags need to be set as `'optional' => true` in CLI synopsys blocks --- includes/classes/WPCLI/Command/GetSiteStats.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/classes/WPCLI/Command/GetSiteStats.php b/includes/classes/WPCLI/Command/GetSiteStats.php index 758eba0d..6a0314b8 100644 --- a/includes/classes/WPCLI/Command/GetSiteStats.php +++ b/includes/classes/WPCLI/Command/GetSiteStats.php @@ -74,6 +74,7 @@ public static function get_args(): array { 'name' => 'clear-cache', 'description' => esc_html__( 'Clear the cache before retrieving the stats (can be intensive).', 'accessibility-checker' ), 'repeating' => false, + 'optional' => true, ], ], ];