-
Notifications
You must be signed in to change notification settings - Fork 15
Add scan-to-scan accessibility regression detection and sidebar trend messaging #1588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,8 +58,9 @@ public function __construct( $post_id ) { | |
| public function generate_summary() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = edac_get_valid_table_name( $wpdb->prefix . 'accessibility_checker' ); | ||
| $summary = []; | ||
| $table_name = edac_get_valid_table_name( $wpdb->prefix . 'accessibility_checker' ); | ||
| $summary = []; | ||
| $previous_summary = get_post_meta( $this->post_id, '_edac_summary', true ); | ||
|
|
||
| if ( ! $table_name ) { | ||
| return $summary; | ||
|
|
@@ -80,12 +81,96 @@ public function generate_summary() { | |
| $summary['content_grade'] = $this->calculate_content_grade(); | ||
| $summary['readability'] = $this->get_readability( $summary ); | ||
| $summary['simplified_summary'] = (bool) ( get_post_meta( $this->post_id, '_edac_simplified_summary', true ) ); | ||
|
|
||
| // If issue counts haven't changed since the last save, preserve existing regression | ||
| // data to avoid a second generate_summary() call overwriting deltas with zeros. | ||
| if ( $this->issue_counts_match( $previous_summary, $summary ) && ! empty( $previous_summary['regression'] ) ) { | ||
| $summary['regression'] = $previous_summary['regression']; | ||
| } else { | ||
| $summary['regression'] = $this->build_regression_data( $previous_summary, $summary ); | ||
| } | ||
|
|
||
| $this->update_issue_density( $summary ); | ||
| $this->save_summary_meta_data( $summary ); | ||
|
|
||
| return $summary; | ||
| } | ||
|
|
||
| /** | ||
| * Checks whether issue-related counts in the current summary match a previously saved summary. | ||
| * | ||
| * @param mixed $previous_summary The previously saved summary meta. | ||
| * @param array $current_summary The current summary data. | ||
| * | ||
| * @return bool True when all issue counts are identical. | ||
| */ | ||
| private function issue_counts_match( $previous_summary, array $current_summary ): bool { | ||
| if ( ! is_array( $previous_summary ) ) { | ||
| return false; | ||
| } | ||
|
|
||
| $metrics = [ 'errors', 'warnings', 'contrast_errors', 'passed_tests', 'content_grade' ]; | ||
|
|
||
| foreach ( $metrics as $metric ) { | ||
| if ( absint( $previous_summary[ $metric ] ?? 0 ) !== absint( $current_summary[ $metric ] ?? 0 ) ) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Build regression/trend data by comparing the current summary with the previous scan summary. | ||
| * | ||
| * @param mixed $previous_summary The previously saved summary meta. | ||
| * @param array $current_summary The current summary data. | ||
| * | ||
| * @return array | ||
| */ | ||
| private function build_regression_data( $previous_summary, array $current_summary ) { | ||
| $metrics = [ | ||
| 'errors', | ||
| 'warnings', | ||
| 'contrast_errors', | ||
| 'passed_tests', | ||
| 'content_grade', | ||
| ]; | ||
|
|
||
| $deltas = []; | ||
| foreach ( $metrics as $metric ) { | ||
| $current_value = absint( $current_summary[ $metric ] ?? 0 ); | ||
| $previous_value = is_array( $previous_summary ) ? absint( $previous_summary[ $metric ] ?? 0 ) : 0; | ||
| $deltas[ $metric ] = $current_value - $previous_value; | ||
| } | ||
|
|
||
| $regression_score = max( 0, $deltas['errors'] ) + max( 0, $deltas['warnings'] ) + max( 0, $deltas['contrast_errors'] ); | ||
|
|
||
| $status = 'stable'; | ||
| if ( $regression_score >= 5 || $deltas['passed_tests'] <= -10 ) { | ||
| $status = 'declining'; | ||
| } elseif ( $regression_score > 0 || $deltas['passed_tests'] < 0 ) { | ||
| $status = 'watch'; | ||
| } elseif ( $deltas['errors'] < 0 || $deltas['warnings'] < 0 || $deltas['contrast_errors'] < 0 || $deltas['passed_tests'] > 0 ) { | ||
| $status = 'improving'; | ||
| } | ||
|
|
||
| $has_baseline = is_array( $previous_summary ) && ! empty( $previous_summary ); | ||
|
|
||
| return [ | ||
| 'has_baseline' => (bool) $has_baseline, | ||
| 'status' => sanitize_key( $status ), | ||
| 'delta' => [ | ||
| 'errors' => intval( $deltas['errors'] ), | ||
| 'warnings' => intval( $deltas['warnings'] ), | ||
| 'contrast_errors' => intval( $deltas['contrast_errors'] ), | ||
| 'passed_tests' => intval( $deltas['passed_tests'] ), | ||
| 'content_grade' => intval( $deltas['content_grade'] ), | ||
| ], | ||
| 'scanned_at' => current_time( 'mysql', true ), | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * Calculates the percentage of passed tests based on the provided rules. | ||
| * This method queries the database to find which rules have not been violated (passed) for | ||
|
|
@@ -330,6 +415,51 @@ private function save_summary_meta_data( $summary ) { | |
| update_post_meta( $this->post_id, '_edac_summary_warnings', absint( $summary['warnings'] ) ); | ||
| update_post_meta( $this->post_id, '_edac_summary_ignored', absint( $summary['ignored'] ) ); | ||
| update_post_meta( $this->post_id, '_edac_summary_contrast_errors', absint( $summary['contrast_errors'] ) ); | ||
| $this->update_summary_history( $summary ); | ||
| } | ||
|
|
||
| /** | ||
| * Stores compact summary scan history used for trend/regression messaging. | ||
| * | ||
| * @param array $summary The latest summary. | ||
| * | ||
| * @return void | ||
| */ | ||
| private function update_summary_history( array $summary ) { | ||
| $history = get_post_meta( $this->post_id, '_edac_summary_history', true ); | ||
| $history = is_array( $history ) ? $history : []; | ||
| $timestamp = current_time( 'mysql', true ); | ||
|
|
||
| $new_entry = [ | ||
| 'scanned_at' => $timestamp, | ||
| 'errors' => absint( $summary['errors'] ?? 0 ), | ||
| 'warnings' => absint( $summary['warnings'] ?? 0 ), | ||
| 'contrast_errors' => absint( $summary['contrast_errors'] ?? 0 ), | ||
| 'passed_tests' => absint( $summary['passed_tests'] ?? 0 ), | ||
| 'content_grade' => absint( $summary['content_grade'] ?? 0 ), | ||
| ]; | ||
|
|
||
| // Skip duplicate entries when generate_summary() is called multiple times per scan. | ||
| if ( ! empty( $history ) ) { | ||
| $last = end( $history ); | ||
| if ( | ||
| absint( $last['errors'] ?? -1 ) === $new_entry['errors'] && | ||
| absint( $last['warnings'] ?? -1 ) === $new_entry['warnings'] && | ||
| absint( $last['contrast_errors'] ?? -1 ) === $new_entry['contrast_errors'] && | ||
| absint( $last['passed_tests'] ?? -1 ) === $new_entry['passed_tests'] && | ||
| absint( $last['content_grade'] ?? -1 ) === $new_entry['content_grade'] | ||
| ) { | ||
| return; | ||
| } | ||
|
Comment on lines
+443
to
+453
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default value with Using Consider using a direct comparison without the 🛡️ Suggested fix if (
- absint( $last['errors'] ?? -1 ) === $new_entry['errors'] &&
- absint( $last['warnings'] ?? -1 ) === $new_entry['warnings'] &&
- absint( $last['contrast_errors'] ?? -1 ) === $new_entry['contrast_errors'] &&
- absint( $last['passed_tests'] ?? -1 ) === $new_entry['passed_tests'] &&
- absint( $last['content_grade'] ?? -1 ) === $new_entry['content_grade']
+ isset( $last['errors'], $last['warnings'], $last['contrast_errors'], $last['passed_tests'], $last['content_grade'] ) &&
+ absint( $last['errors'] ) === $new_entry['errors'] &&
+ absint( $last['warnings'] ) === $new_entry['warnings'] &&
+ absint( $last['contrast_errors'] ) === $new_entry['contrast_errors'] &&
+ absint( $last['passed_tests'] ) === $new_entry['passed_tests'] &&
+ absint( $last['content_grade'] ) === $new_entry['content_grade']
) {🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| $history[] = $new_entry; | ||
|
|
||
| if ( count( $history ) > 10 ) { | ||
| $history = array_slice( $history, -10 ); | ||
| } | ||
|
|
||
| update_post_meta( $this->post_id, '_edac_summary_history', $history ); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -351,6 +481,18 @@ private function sanitize_summary_meta_data( array $summary ): array { | |
| 'content_grade' => absint( $summary['content_grade'] ?? 0 ), | ||
| 'readability' => sanitize_text_field( $summary['readability'] ?? '' ), | ||
| 'simplified_summary' => filter_var( $summary['simplified_summary'] ?? false, FILTER_VALIDATE_BOOLEAN ), | ||
| 'regression' => [ | ||
| 'has_baseline' => filter_var( $summary['regression']['has_baseline'] ?? false, FILTER_VALIDATE_BOOLEAN ), | ||
| 'status' => sanitize_key( $summary['regression']['status'] ?? 'stable' ), | ||
| 'delta' => [ | ||
| 'errors' => intval( $summary['regression']['delta']['errors'] ?? 0 ), | ||
| 'warnings' => intval( $summary['regression']['delta']['warnings'] ?? 0 ), | ||
| 'contrast_errors' => intval( $summary['regression']['delta']['contrast_errors'] ?? 0 ), | ||
| 'passed_tests' => intval( $summary['regression']['delta']['passed_tests'] ?? 0 ), | ||
| 'content_grade' => intval( $summary['regression']['delta']['content_grade'] ?? 0 ), | ||
| ], | ||
| 'scanned_at' => sanitize_text_field( $summary['regression']['scanned_at'] ?? '' ), | ||
| ], | ||
| ]; | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This classification runs even when there is no prior summary, so a first-ever scan with existing errors/warnings is labeled
watch/decliningdespitehas_baselinebeing false. The sidebar then uses that status for retention messaging, which can incorrectly tell users accessibility is declining before any scan-to-scan comparison exists. Please short-circuit tostable(or equivalent neutral status) until a baseline is present.Useful? React with 👍 / 👎.