الوحدة 5 · الاختبار وثقافة إمكانية الوصول
الاختبار الآلي باستخدام axe
16 دقراءة 4 داكتشف الخطأ
الدرس في لمحة
شغّل axe في المتصفّح وفي اختبارات Playwright لتلتقط مبكرًا الإخفاقات الشائعة التي تستطيع الآلة رصدها، واعرف بالضبط أيّ المشكلات لن تجدها الأتمتة أبدًا.
- 1axe وعائلته
- 2ما تلتقطه الأتمتة، وما لا تستطيعه
- 3axe في اختبارات Playwright
- 4الـ Linting يلتقط المشكلات أبكر
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
لسمير مقولة: «الروبوت يجد الأخطاء الإملائية، والإنسان يجد الثغرات في الحبكة». أدوات إمكانية الوصول الآلية سريعة ورخيصة ولا تتعب، وتجد نسبة كبيرة من المشكلات في الصفحة المعتادة خلال ثوانٍ. لكنها عاجزة تمامًا عن معرفة إن كان النص البديل صادقًا، أو إن كان ترتيب التركيز منطقيًا، أو إن كانت نافذة الحوار قابلة للاستخدام. يوضّح هذا الدرس كيف تحصل منها على أقصى فائدة، وأين تتوقّف.
axe وعائلته
axe-core، الذي تصونه شركة Deque، مكتبة JavaScript مفتوحة المصدر لقواعد إمكانية الوصول. تعمل داخل الصفحة، وتفحص الـ DOM والأنماط المحسوبة، وتبلّغ عن المخالفات مع معيار WCAG المعنيّ والعنصر المخالف واقتراح للإصلاح. وهي حريصة على نحو غير معتاد على تجنّب الإنذارات الكاذبة: حين لا تكون متأكدة، تبلّغ عن العنصر على أنه “needs review” (يحتاج إلى مراجعة) لا على أنه مخالفة.
ستقابلها في عدة أماكن:
- axe DevTools، إضافة للمتصفّح، للفحص عند الطلب أثناء التطوير.
- فئة إمكانية الوصول في Lighthouse داخل أدوات المطوّر في Chrome، التي تشغّل جزءًا من قواعد axe وتعطي درجة.
- تكاملات الاختبار:
@axe-core/playwrightللاختبارات الشاملة (end-to-end)، وأغلفة مثلvitest-axeلاختبارات الـ components.
وإضافة WAVE من WebAIM وAccessibility Insights من Microsoft بدائل أو رفاق جيدون؛ وتضيف Accessibility Insights أيضًا فحوصًا يدوية موجَّهة.
ما تلتقطه الأتمتة، وما لا تستطيعه
القواعد الآلية جيدة في الحقائق الموضوعية البنيوية:
| تلتقطه الأتمتة بشكل موثوق | يحتاج إلى إنسان |
|---|---|
| غياب خاصية alt | هل النص البديل دقيق ومفيد |
| حقول وأزرار وروابط بلا اسم | هل للاسم معنى، وهل يطابق التسمية المرئية |
| تباين نصي أقل من 4.5:1 أو 3:1 | التباين فوق الصور والتدرّجات اللونية |
غياب lang، وتكرار الـ ids، وقيم ARIA غير الصالحة |
هل اللغة صحيحة لكل جزء |
aria-hidden على عناصر قابلة للتركيز |
ترتيب التركيز، ومصائد لوحة المفاتيح، والتركيز بعد إغلاق نافذة حوار |
| بعض مشكلات العناوين والـ landmarks | هل العناوين تصف المحتوى |
| هل تُعلَن التحديثات الحيّة، وهل التعليمات واضحة |
كم يمثّل ذلك إجمالًا؟ الدراسات تختلف لأنها تحسب بطرق مختلفة. حين زرعت دائرة الخدمات الرقمية الحكومية في المملكة المتحدة (GDS) عام 2017 ما مجموعه 142 عائقًا معروفًا في صفحة اختبار، وجدت أفضل أداة 40% منها، ووجد بعضها أقل من ذلك بكثير. أما Deque، التي تحصي المشكلات في آلاف عمليات التدقيق الحقيقية، فتقول إن الاختبارات الآلية التقطت نحو 57% من المشكلات من حيث العدد، ويعود ذلك أساسًا إلى أن بضع مشكلات قابلة للأتمتة، وخاصةً التباين، تتكرّر كثيرًا. الخلاصة المنصفة: الأتمتة تجد المشكلات الأكثر تكرارًا، لكنها لا تغطّي إلا أقلية مما تطلبه WCAG.
axe في اختبارات Playwright
الفحص في الـ CI يحوّل إمكانية الوصول إلى اختبار تراجع (regression test): بمجرد أن تصبح الصفحة نظيفة، يُفشل أي label مفقود جديد الـ build. مع Playwright و@axe-core/playwright:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
const WCAG_AA = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'];
test('event page has no detectable WCAG A/AA violations', async ({ page }) => {
await page.goto('/events/friday');
const results = await new AxeBuilder({ page }).withTags(WCAG_AA).analyze();
expect(results.violations).toEqual([]);
});
test('booking dialog has no detectable violations when open', async ({ page }) => {
await page.goto('/events/friday');
await page.getByRole('button', { name: 'Book now Friday pass' }).click();
await expect(page.getByRole('dialog', { name: 'Book Friday pass' })).toBeVisible();
const results = await new AxeBuilder({ page })
.include('#booking')
.withTags(WCAG_AA)
.analyze();
expect(results.violations).toEqual([]);
});الاختبار الثاني لا يقلّ أهمية عن الأول. لا يرى axe إلا ما هو معروض لحظة الفحص، فنوافذ الحوار المغلقة والقوائم المطويّة وحالات الخطأ والسمة الداكنة تبقى غير مختبَرة ما لم يضع اختبارك الصفحة في تلك الحالات أولًا.
لاحظ أن الاختبار يجد العناصر أيضًا عبر getByRole واسم متاح. تحديد العناصر بالدور والاسم هو الأسلوب الذي يوصي به Playwright أصلًا، ويمنحك فحصًا صغيرًا لإمكانية الوصول مجانًا: إن فقدت نافذة الحوار اسمها، يتوقّف الـ locator عن المطابقة.
الـ Linting يلتقط المشكلات أبكر
في مشاريع React، ينبّه eslint-plugin-jsx-a11y إلى المشكلات وأنت تكتب: عنصر img بلا alt، أو div عليه onClick بلا دور أو معالج مفاتيح، أو خاصية aria-* غير موجودة. يلتقط أقل مما يلتقطه axe لأنه لا يرى إلا الكود المصدري لا الصفحة المعروضة، لكنه يلتقطه بعد دقائق من الخطأ بدلًا من أيام.
هكذا يبدو الإعداد متعدّد الطبقات: الـ linter في المحرّر، وaxe في اختبارات الـ components والاختبارات الشاملة، وإنسان بلوحة مفاتيح وقارئ شاشة قبل كل إصدار. والدرس التالي هو ذلك الإنسان.
أهم النقاط
- axe-core هو محرّك القواعد مفتوح المصدر الذي يقف خلف axe DevTools وتدقيق إمكانية الوصول في Lighthouse وكثير من تكاملات الاختبار.
- تلتقط الأدوات الآلية بشكل موثوق غياب alt والـ labels والأسماء، وإخفاقات التباين، وقيم ARIA غير الصالحة، وغياب لغة الصفحة.
- لا تستطيع هذه الأدوات الحكم على المعنى، فجودة alt وترتيب التركيز وسلوك لوحة المفاتيح والإعلانات الحيّة وعدم تطابق التسمية مع الاسم تحتاج إلى إنسان.
- شغّل axe في الـ CI على الصفحات والحالات الأساسية، مثل نافذة حوار مفتوحة، لتُفشل التراجعاتُ الـ build، واعتبر الفحص النظيف الحدّ الأدنى لا خطّ النهاية.