diff --git a/src/Reporting/HtmlDashboardReporter.php b/src/Reporting/HtmlDashboardReporter.php index 8c717e5..1806c1d 100644 --- a/src/Reporting/HtmlDashboardReporter.php +++ b/src/Reporting/HtmlDashboardReporter.php @@ -13,10 +13,54 @@ class HtmlDashboardReporter { $count = count($violations); $date = date('Y-m-d H:i:s'); - - $rows = ''; + + // Calculate severity and standard counts + $criticalCount = 0; + $errorCount = 0; + $warningCount = 0; + $infoCount = 0; + + $standardA = 0; + $standardAA = 0; + $standardAAA = 0; + foreach ($violations as $v) { + switch ($v->severity) { + case \YakNet\AccessibilityConsole\Core\Severity::CRITICAL: + $criticalCount++; + break; + case \YakNet\AccessibilityConsole\Core\Severity::ERROR: + $errorCount++; + break; + case \YakNet\AccessibilityConsole\Core\Severity::WARNING: + $warningCount++; + break; + case \YakNet\AccessibilityConsole\Core\Severity::INFO: + $infoCount++; + break; + } + + switch ($v->standard) { + case \YakNet\AccessibilityConsole\Core\WCAGStandard::A: + $standardA++; + break; + case \YakNet\AccessibilityConsole\Core\WCAGStandard::AA: + $standardAA++; + break; + case \YakNet\AccessibilityConsole\Core\WCAGStandard::AAA: + $standardAAA++; + break; + } + } + + // Calculate health score (0-100) + $deductions = ($criticalCount * 15) + ($errorCount * 10) + ($warningCount * 4) + ($infoCount * 1); + $score = max(0, min(100, 100 - $deductions)); + + $cards = ''; + foreach ($violations as $index => $v) { $severityClass = strtolower($v->severity->value); + $standardVal = $v->standard->value; $aiSuggestion = $v->fixSuggestion; $explanation = ''; $fixedCode = ''; @@ -25,80 +69,1110 @@ class HtmlDashboardReporter preg_match('/EXPLANATION:(.*)FIX:(.*)/s', $aiSuggestion, $matches); $explanation = trim($matches[1] ?? ''); $fixedCode = trim($matches[2] ?? ''); - // Clean up potential markdown $fixedCode = preg_replace('/^```html\s*|\s*```$/i', '', $fixedCode); } else { $fixedCode = $aiSuggestion; } - $rows .= " - - {$v->ruleId} - {$v->message} -
" . htmlspecialchars($v->htmlSnippet) . "
- - " . ($explanation ? "
Öneri: {$explanation}
" : "") . " - " . ($fixedCode ? "
" . htmlspecialchars($fixedCode) . "
" : "Manual fix required") . " - - " . ($v->location ? "{$v->location['file']}:{$v->location['line']}" : 'N/A') . " - "; + $escapedSnippet = htmlspecialchars($v->htmlSnippet, ENT_QUOTES, 'UTF-8'); + $escapedFixed = $fixedCode ? htmlspecialchars($fixedCode, ENT_QUOTES, 'UTF-8') : ''; + $escapedExplanation = $explanation ? htmlspecialchars($explanation, ENT_QUOTES, 'UTF-8') : ''; + $locationStr = $v->location ? htmlspecialchars($v->location['file'] . ':' . $v->location['line'], ENT_QUOTES, 'UTF-8') : 'N/A'; + $escapedRuleId = htmlspecialchars($v->ruleId, ENT_QUOTES, 'UTF-8'); + $escapedMessage = htmlspecialchars($v->message, ENT_QUOTES, 'UTF-8'); + + $hasAiFix = !empty($fixedCode); + $aiTabButton = $hasAiFix ? "" : ""; + $aiTabPanel = $hasAiFix ? " + " : ""; + + $cards .= " +
+
+
+ {$escapedRuleId} + WCAG {$standardVal} + + + " . ucfirst($severityClass) . " + +
+
+ + {$locationStr} +
+
+
+

{$escapedMessage}

+ +
+
+ + {$aiTabButton} +
+
+
{$escapedSnippet}
+
+ {$aiTabPanel} +
+
+
"; } return << - + - A11y Report - {$target} + + + YakNet Erişilebilirlik Raporu + + + -
-

Accessibility Report

-

Target: {$target} | Generated: {$date}

- -
-
-

{$count}

-

Violations Found

-
-
-

WCAG 2.1

-

Standard Level

-
-
+ +
- - - - - - - - - - - - {$rows} - -
Rule IDIssueCode SnippetAI RecommendationSource Location
+
+ +
+
+ +
+

YakNet Accessibility Dashboard

+

Web Standartları ve WCAG Uyum Analiz Raporu

+
+
+
+
Hedef: {$target}
+
Tarih: {$date}
+ +
+
+ + +
+ +
+
+ + + + +
0
+
+
+

Erişilebilirlik Skoru

+

Sayfanın genel standartlara uygunluk derecesi.

+
+
+ + +
+
+
+ + Critical + + {$criticalCount} +
+
+ + Error + + {$errorCount} +
+
+ + Warning + + {$warningCount} +
+
+ + Info + + {$infoCount} +
+
+
+ + +
+
+
+ WCAG A + {$standardA} +
+
+ WCAG AA + {$standardAA} +
+
+ WCAG AAA + {$standardAAA} +
+
+ Toplam İhlal + {$count} +
+
+
+
+ + +
+
+ + + + +
+ +
+ + + + + +
+
+ + +
+ {$cards} + + + +
+ + + HTML; diff --git a/test_report.html b/test_report.html index 053e61d..641f1d6 100644 --- a/test_report.html +++ b/test_report.html @@ -1,62 +1,832 @@ - + - A11y Report - demo/broken.html + + + YakNet Erişilebilirlik Raporu + + + -
-

Accessibility Report

-

Target: demo/broken.html | Generated: 2026-05-13 09:46:04

- -
-
-

4

-

Violations Found

-
-
-

WCAG 2.1

-

Standard Level

-
-
+ +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Rule IDIssueCode SnippetAI RecommendationSource Location
WCAG_3_1_1_LANGThe html element must have a lang attribute.
<html>
+    
+ +
+
+ +
+

YakNet Accessibility Dashboard

+

Web Standartları ve WCAG Uyum Analiz Raporu

+
+
+
+
Hedef: demo/broken.html
+
Tarih: 2026-06-06 07:20:38
+ +
+
+ + +
+ +
+
+ + + + +
0
+
+
+

Erişilebilirlik Skoru

+

Sayfanın genel standartlara uygunluk derecesi.

+
+
+ + +
+
+
+ + Critical + + 0 +
+
+ + Error + + 13 +
+
+ + Warning + + 7 +
+
+ + Info + + 0 +
+
+
+ + +
+
+
+ WCAG A + 19 +
+
+ WCAG AA + 1 +
+
+ WCAG AAA + 0 +
+
+ Toplam İhlal + 20 +
+
+
+
+ + +
+
+ + + + +
+ +
+ + + + + +
+
+ + +
+ +
+
+
+ WCAG_3_1_1_LANG + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

The html element must have a lang attribute.

+ +
+
+ + +
+
+
<html>
 <head>
-    <title>Broken Page</title>
+    <title></title>
 </head>
 <body>
-    <h1>Welcome to a broken page</h1>
+    <h1 id="main-content">Welcome to a broken page</h1>
     
     <!-- Missing alt attribute -->
     <img src="logo.png">
@@ -67,35 +837,1056 @@
     <!-- Empty button -->
     <button type="submit"></button>
     
+    <!-- Positive tabindex -->
+    <a href="/keyboard" tabindex="5">Keyboard test</a>
+
+    <!-- Duplicate ID -->
+    <div id="main-content">This is a duplicate main-content ID div!</div>
+
+    <!-- Low Contrast -->
+    <p style="color: #888888; background-color: #999999;">This text has extremely low contrast ratio!</p>
+
+    <!-- Invalid ARIA role and missing controls target -->
+    <div role="cool-widget" aria-controls="non-existent-popup">WAI-ARIA elements</div>
+
+    <!-- Missing Iframe Title -->
+    <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>
+
+    <!-- New WCAG Rule Violations for testing -->
+    
+    <!-- LinkTextGeneric: Generic text without context -->
+    <a href="/read-more">Click here</a>
+    <a href="/devam-oku">devamını oku</a>
+    
+    <!-- PlaceholderAsLabel: Input relying only on placeholder -->
+    <input type="text" placeholder="Search website...">
+
+    <!-- ImageAltRedundant: Redundant words in alt text -->
+    <img src="logo2.png" alt="logo of our company">
+    <img src="profile.jpg" alt="profile photo of john doe">
+
+    <!-- AriaHiddenFocusable: Focusable element hidden with aria-hidden="true" -->
+    <div aria-hidden="true">
+        <button type="button">Hidden click</button>
+    </div>
+    <a href="#" aria-hidden="true">Hidden link</a>
+
     <p>This page has multiple accessibility issues.</p>
 </body>
-</html>
Hata: GEMINI_API_KEY bulunamadı.
N/A
WCAG_1_1_1_ALTImages must have an alt attribute.
<img src="logo.png">
Hata: GEMINI_API_KEY bulunamadı.
N/A
WCAG_2_4_4_LINK_TEXTLinks must have discernible text.
<a href="https://google.com"></a>
Hata: GEMINI_API_KEY bulunamadı.
N/A
WCAG_4_1_2_BUTTONButtons must have discernible text.
<button type="submit"></button>
Hata: GEMINI_API_KEY bulunamadı.
N/A
+</html> +
+ + +
+ + +
+
+
+ WCAG_1_1_1_ALT + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Images must have an alt attribute.

+ +
+
+ + +
+
+
<img src="logo.png">
+
+ + +
+
+
+
+
+
+ WCAG_2_4_4_LINK_TEXT + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Links must have discernible text.

+ +
+
+ + +
+
+
<a href="https://google.com"></a>
+
+ + +
+
+
+
+
+
+ WCAG_4_1_2_BUTTON + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Buttons must have discernible text.

+ +
+
+ + +
+
+
<button type="submit"></button>
+
+ + +
+
+
+
+
+
+ WCAG_1_3_1_LABEL + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Form inputs must have an associated label.

+ +
+
+ + +
+
+
<input type="text" placeholder="Search website...">
+
+ + +
+
+
+
+
+
+ WCAG_2_4_3_TABINDEX + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Avoid positive tabindex values (tabindex="5") to preserve the natural tab order.

+ +
+
+ + +
+
+
<a href="/keyboard" tabindex="5">Keyboard test</a>
+
+ + +
+
+
+
+
+
+ WCAG_1_4_3_CONTRAST + WCAG AA + + + Warning + +
+
+ + N/A +
+
+
+

Insufficient contrast ratio of 1.24:1. Minimum required is 4.5:1.

+ +
+
+ + +
+
+
<p style="color: #888888; background-color: #999999;">This text has extremely low contrast ratio!</p>
+
+ + +
+
+
+
+
+
+ DuplicateId + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Duplicate ID "main-content" found. Element ID attribute must be unique across the document.

+ +
+
+ + +
+
+
<h1 id="main-content">Welcome to a broken page</h1>
+
+ +
+
+
+
+
+
+ DuplicateId + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Duplicate ID "main-content" found. Element ID attribute must be unique across the document.

+ +
+
+ + +
+
+
<div id="main-content">This is a duplicate main-content ID div!</div>
+
+ +
+
+
+
+
+
+ AriaRole + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Invalid ARIA role "cool-widget" declared on element. Must use standard WAI-ARIA roles.

+ +
+
+ + +
+
+
<div role="cool-widget" aria-controls="non-existent-popup">WAI-ARIA elements</div>
+
+ +
+
+
+
+
+
+ AriaRole + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Attribute aria-controls="non-existent-popup" targets an element that does not exist in the document.

+ +
+
+ + +
+
+
<div role="cool-widget" aria-controls="non-existent-popup">WAI-ARIA elements</div>
+
+ +
+
+
+
+
+
+ WCAG_4_1_2_IFRAME_TITLE + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Iframe element is missing a descriptive title attribute.

+ +
+
+ + +
+
+
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>
+
+ + +
+
+
+
+
+
+ WCAG_2_4_4_LINK_GENERIC + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Links must have descriptive text. Generic text like "click here" or "read more" is not accessible.

+ +
+
+ + +
+
+
<a href="/read-more">Click here</a>
+
+ + +
+
+
+
+
+
+ WCAG_2_4_4_LINK_GENERIC + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Links must have descriptive text. Generic text like "click here" or "read more" is not accessible.

+ +
+
+ + +
+
+
<a href="/devam-oku">devamını oku</a>
+
+ + +
+
+
+
+
+
+ WCAG_3_3_2_PLACEHOLDER_LABEL + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Inputs should not use placeholder text as their only label.

+ +
+
+ + +
+
+
<input type="text" placeholder="Search website...">
+
+ + +
+
+
+
+
+
+ WCAG_1_1_1_ALT_REDUNDANT + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Image alt text should not contain redundant words like "image" or "photo".

+ +
+
+ + +
+
+
<img src="logo2.png" alt="logo of our company">
+
+ + +
+
+
+
+
+
+ WCAG_1_1_1_ALT_REDUNDANT + WCAG A + + + Warning + +
+
+ + N/A +
+
+
+

Image alt text should not contain redundant words like "image" or "photo".

+ +
+
+ + +
+
+
<img src="profile.jpg" alt="profile photo of john doe">
+
+ + +
+
+
+
+
+
+ WCAG_4_1_2_ARIA_HIDDEN_FOCUS + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Focusable elements must not be hidden with aria-hidden="true".

+ +
+
+ + +
+
+
<div aria-hidden="true">
+        <button type="button">Hidden click</button>
+    </div>
+
+ + +
+
+
+
+
+
+ WCAG_4_1_2_ARIA_HIDDEN_FOCUS + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Focusable elements must not be hidden with aria-hidden="true".

+ +
+
+ + +
+
+
<a href="#" aria-hidden="true">Hidden link</a>
+
+ + +
+
+
+
+
+
+ WCAG_2_4_2_PAGE_TITLE + WCAG A + + + Error + +
+
+ + N/A +
+
+
+

Web pages must have a non-empty title element in the head.

+ +
+
+ + +
+
+
<head>
+    <title></title>
+</head>
+
+ + +
+
+
+ + + + + + + \ No newline at end of file