خليني أحكيلكم قصة صارت معي قبل كم سنة. كنا شغالين على مشروع كبير لأحد العملاء المهمين، موقع تجارة إلكترونية ضخم. في يوم من الأيام، طلب العميل تعديلاً بسيطاً جداً: “أبو عمر، بدنا نغير لون زر ‘أضف إلى السلة’ من الأزرق للأخضر، عشان نحسن نسبة النقر عليه”.
قلت في نفسي، “بسيطة، هاي شغلة خمس دقايق”. فتحت ملف الـ CSS، غيرت سطر واحد، عملت `git push`، ورفعت التحديث على السيرفر. شعرت بالرضا عن سرعة الإنجاز. بعد حوالي نصف ساعة، رن تلفوني. على الطرف الثاني كان العميل وصوته فيه نبرة هلع: “أبو عمر، شو عملتوا؟ صفحة الدفع خربانة كلها! الحقول فوق بعض والأزرار مختفية!”.
يا ساتر! نزلت عليّ الكلمات زي الصاعقة. فتحت الموقع بسرعة وأنا مش مصدق. فعلاً، صفحة الدفع، أهم صفحة في الموقع كله، كانت في حالة فوضى عارمة. تبين إنه التغيير “البسيط” اللي عملته على كلاس الزر العام، بسبب قواعد الـ Specificity المعقدة في CSS، أثر على ستايل ثاني في صفحة الدفع ما كان يخطر على بال حدا. يومها “ولعت الدنيا” وقضينا ساعات طويلة في إصلاح المشكلة واستعادة ثقة العميل. هذه الحادثة كانت نقطة تحول، وبداية رحلتنا للبحث عن حل جذري لكابوس “هل كسرنا شيئاً؟”.
الجحيم الصغير لكل تعديل CSS
إذا كنت مطور واجهات أمامية (Frontend Developer)، فأنت تعرف هذا الشعور جيداً. لغة CSS، على الرغم من قوتها وجمالها، يمكن أن تكون خادعة. طبيعتها العالمية والمتشعبة (Cascading) تعني أن أي تغيير، مهما كان صغيراً، يمكن أن يسبب آثاراً جانبية غير متوقعة في أماكن لم تفكر حتى في التحقق منها.
قبل أن نكتشف الحل، كانت عملية ضمان الجودة لدينا كالتالي:
- يقوم المطور بإجراء التعديل على CSS.
- يقوم بفتح 5-10 صفحات رئيسية في الموقع يدوياً للتحقق من أن كل شيء يبدو على ما يرام.
- يطلب من زميل آخر (أو من فاحص الجودة QA) أن يلقي نظرة سريعة.
- نتأمل جميعاً ونقول “إن شاء الله خير” ونقوم بالنشر.
هذه العملية كانت مرهقة، غير موثوقة، وتعتمد كلياً على الصدفة والذاكرة البشرية المحدودة. كنا نعيش في خوف دائم من أن يفلت منا خطأ بصري صغير يتسبب في مشكلة كبيرة للعميل أو المستخدم النهائي.
الحل السحري: الاختبار البصري التراجعي (Visual Regression Testing)
بعد الحادثة الشهيرة، بدأنا البحث بجدية عن طريقة آلية تمنع تكرار مثل هذه الكوارث. وهنا تعرفنا على عالم “الاختبار البصري التراجعي”.
ما قصة هذا الاختبار؟
ببساطة شديدة، الاختبار البصري التراجعي هو عملية مؤتمتة تقارن كيف تبدو واجهات المستخدم الخاصة بك بمرور الوقت. الفكرة عبقرية في بساطتها:
- اللقطة المرجعية (Baseline): في المرة الأولى التي تجري فيها الاختبار على صفحة أو مكون، تقوم الأداة بأخذ لقطة شاشة (Screenshot) وحفظها كـ “الصورة المرجعية” أو “النسخة الذهبية” التي تمثل الحالة الصحيحة والسليمة.
- إجراء التغييرات: تقوم أنت كمطور بإجراء تعديلاتك على الكود كالمعتاد (مثلاً، تغيير لون الزر).
- المقارنة: عند تشغيل الاختبارات مرة أخرى، تأخذ الأداة لقطة شاشة جديدة لنفس الصفحة أو المكون.
- كشف الاختلافات: تقوم الأداة بمقارنة الصورة الجديدة بالصورة المرجعية بكسل ببكسل. إذا لم يكن هناك أي اختلاف، ينجح الاختبار. إذا كان هناك اختلاف، يفشل الاختبار وتقوم الأداة بإنشاء “صورة فرق” (Diff image) تبرز لك بالضبط الأماكن التي حدث فيها التغيير.
- المراجعة والاعتماد: أنت الآن تراجع هذه الاختلافات.
- إذا كان التغيير مقصوداً (مثل لون الزر الجديد)، فأنت “توافق” على التغيير، وتصبح اللقطة الجديدة هي اللقطة المرجعية للمستقبل.
- إذا كان التغيير غير مقصود (مثل تخريب صفحة الدفع)، فهذا يعني أنك اكتشفت خطأً! تقوم بإصلاح الكود وتشغيل الاختبار مرة أخرى حتى يمر.
نصيحة من أبو عمر: الاختبار البصري لا يحل محل اختبارات الوحدات (Unit tests) أو اختبارات التكامل (Integration tests). إنه يكملها. تلك الاختبارات تتأكد من “منطق” التطبيق، بينما الاختبار البصري يتأكد من “مظهر” التطبيق. الاثنان معاً يشكلان شبكة أمان قوية جداً.
يلا نطبق: أدوات ونصائح من الميدان
الكلام النظري جميل، لكن دعونا نرى كيف يمكن تطبيق هذا عملياً. هناك العديد من الأدوات الرائعة في هذا المجال، بعضها مدفوع مثل Percy و Applitools، وبعضها مفتوح المصدر مثل BackstopJS. ولكن الأداة التي أصبحت المفضلة لدينا مؤخراً هي Playwright، مكتبة الأتمتة الرائعة من مايكروسوفت، لأنها تأتي مع دعم مدمج للاختبار البصري.
مثال عملي باستخدام Playwright
لنفترض أن لدينا صفحة بسيطة ونريد التأكد من أن العنوان الرئيسي لا يتغير عن طريق الخطأ. لنبدأ بتثبيت Playwright:
npm init playwright@latest
بعد ذلك، سنكتب اختباراً بسيطاً. لنفترض أن لدينا ملف اختبار اسمه `homepage.spec.ts`:
import { test, expect } from '@playwright/test';
test('الصفحة الرئيسية يجب أن تبدو سليمة', async ({ page }) => {
// 1. اذهب إلى الصفحة المطلوبة
await page.goto('http://localhost:3000');
// 2. انتظر حتى يتم تحميل العناصر الهامة (اختياري لكنه مهم)
await page.waitForSelector('h1');
// 3. خذ لقطة شاشة وقارنها باللقطة المرجعية
// هذا هو السطر السحري!
await expect(page).toHaveScreenshot('homepage.png');
});
عند تشغيل الاختبار لأول مرة:
سيقوم Playwright بالبحث عن لقطة مرجعية اسمها `homepage.png`. بما أنه لن يجدها، سيفشل الاختبار ويخبرك: “Error: A snapshot doesn’t exist… writing actual.” وسيقوم بإنشاء الصورة المرجعية الأولى لك. كل ما عليك فعله هو تشغيل الاختبار مرة أخرى، وهذه المرة سينجح.
عند إجراء تغيير:
لنفترض أنك غيرت حجم خط العنوان الرئيسي `h1` عن طريق الخطأ. الآن، عندما تشغل الاختبار:
npx playwright test
سيفشل الاختبار وستحصل على تقرير مفصل. سيُظهر لك Playwright ثلاث صور:
- `homepage-expected.png`: الصورة المرجعية القديمة.
- `homepage-actual.png`: الصورة الجديدة بعد التغيير.
- `homepage-diff.png`: صورة تسلط الضوء باللون الأحمر على الفروقات الدقيقة بين الصورتين.
هنا تكمن القوة! أنت الآن ترى بعينيك بالضبط ما الذي “كسرته” دون الحاجة إلى التخمين أو البحث اليدوي.
نصائح من خبير مجرّب (من أبو عمر)
بعد سنوات من استخدام هذه التقنية، تعلمت بعض الدروس التي أود مشاركتها معكم:
- بلّش حبة حبة (ابدأ صغيراً): لا تحاول تغطية كل شبر في موقعك من اليوم الأول. ابدأ بالصفحات أو المكونات الأكثر أهمية وحساسية: الصفحة الرئيسية، صفحة المنتج، سلة التسوق، المكونات الأساسية في مكتبة التصميم (Design System) الخاصة بك.
- تعامل مع المحتوى الديناميكي: أكبر تحدٍ في الاختبار البصري هو المحتوى المتغير (تواريخ، أسماء مستخدمين، إعلانات، عدادات). معظم الأدوات تسمح لك بإخفاء عناصر معينة قبل التقاط الصورة. في Playwright، يمكنك فعل ذلك بسهولة:
// يخفي العنصر الذي يحتوي على تاريخ متغير قبل أخذ اللقطة await expect(page).toHaveScreenshot('article.png', { mask: [page.locator('.dynamic-date')] }); - اربطه بالـ CI/CD: القوة الحقيقية تظهر عندما تجعل هذه الاختبارات تعمل تلقائياً مع كل Pull Request على GitHub أو GitLab. هذا يمنع أي تغيير بصري غير مرغوب فيه من الوصول إلى الكود الرئيسي من الأساس.
- لا تخف من الـ `threshold`: أحياناً، قد تظهر اختلافات طفيفة جداً (بكسل واحد أو اثنين) بسبب طريقة عرض الخطوط (anti-aliasing) بين بيئات التشغيل المختلفة. معظم الأدوات تسمح بتعيين “هامش خطأ” أو `threshold` لتجاهل هذه الفروقات الطفيفة وغير المهمة. استخدمه بحكمة.
الخلاصة: نم مرتاح البال 😴
تبني الاختبار البصري التراجعي كان واحداً من أفضل القرارات التقنية التي اتخذناها كفريق. لقد حول عملية تعديل الـ CSS من مهمة محفوفة بالمخاطر والقلق إلى عملية واثقة ومنظمة.
لم نعد نسأل أنفسنا “هل كسرنا شيئاً؟”، بل أصبحنا نقول بثقة “هذه هي التغييرات البصرية المقصودة، وتلك هي الأخطاء التي اكتشفناها قبل أن يراها أي مستخدم”. إنها شبكة الأمان التي تسمح لنا بالتحرك بسرعة وثقة، والنوم ليلاً براحة بال.
نصيحتي الأخيرة لك: لا تنتظر حتى تحدث الكارثة. ابدأ اليوم، ولو بمشروع صغير. استثمر قليلاً من الوقت في إعداد هذه الاختبارات، وستوفر على نفسك وفريقك ساعات لا تحصى من تصحيح الأخطاء والقلق في المستقبل. وداعاً لأيام الـ “يا رب استر!” مع كل `git push`. 😉