يا أهلاً وسهلاً فيكم جميعاً، معكم أخوكم أبو عمر.
قبل كم سنة، كنت شغال على مشروع كبير ومهم لأحد العملاء، عبارة عن منصة تحليل بيانات معقدة بواجهات ورسوم بيانية كثيرة. كنت فخور جداً بشغلي، والمشروع كان مستقر والحمد لله. في يوم من الأيام، طلبوا مني تعديل بسيط جداً: تحديث إحدى المكتبات البرمجية الصغيرة (dependency) عشان نحل مشكلة أمان بسيطة.
قلت في نفسي “شغلة خمس دقايق”. عملت التحديث، شغّلت كل الاختبارات الآلية اللي عندي – اختبارات الوحدات (Unit Tests) واختبارات التكامل (Integration Tests) – وكله تمام التمام، مية المية. ما في أي خطأ. رفعت التحديثات وأنا مرتاح البال، وقلت للعميل “الأمور طيبة”.
بعدها بيومين، بيجيني اتصال من مدير المشروع عند العميل، صوته معصّب شوي وبيقولي: “أبو عمر، شو هاد؟ كل الرسوم البيانية في لوحة التحكم الرئيسية مخربطة! الأرقام طالعة فوق بعضها، والألوان ضايعة!”.
أنا بصراحة انصدمت. كيف صار هيك وكل الاختبارات نجحت؟ فتحت الموقع من عندي، ويا ريتني ما فتحت. المنظر كان كارثي. تحديث المكتبة البرمجية البسيط هذا، بالرغم من إنه ما كسر أي “وظيفة” في الموقع، إلا إنه عمل تعارض في ملفات الـ CSS، وضرب كل تنسيق الرسوم البيانية. المشكلة إن اختباراتي كانت “عمياء”، بتفحص الكود والمنطق، بس ما بتشوف الشاشة زي ما بشوفها المستخدم.
هذا الموقف المحرج علّمني درس قاسي: الاختبارات الوظيفية وحدها لا تكفي. من يومها، صار “الاختبار البصري التراجعي” جزء أساسي من أي مشروع بشتغل عليه. خلوني أحكيلكم كيف هالتنقنية أنقذتني مرات ومرات من كوارث مشابهة.
ما هو “الكسر الصامت” للواجهة؟ ولماذا هو خطير؟
المشكلة اللي واجهتها اسمها “Visual Regression” أو “التراجع البصري”. هو نوع من الأخطاء البرمجية اللي ما بتكتشفها الاختبارات التقليدية. هاي الاختبارات بتتأكد إنه لو ضغطت على زر “شراء”، عملية الشراء بتتم بنجاح. لكنها ما بتهتم إذا كان زر “الشراء” نفسه نصه جاي على اليمين بدل النص، أو لونه تغير للون ثاني، أو حتى اختفى من الشاشة بسبب خطأ في الـ CSS.
هذه الأخطاء “الصامتة” خطيرة جداً للأسباب التالية:
- تدمير تجربة المستخدم (UX): واجهة مكسورة أو مشوهة بتعطي انطباع سيء جداً عن المنتج وبتخلي المستخدم يفقد الثقة فيه.
- صعوبة الاكتشاف: ممكن الخطأ يكون موجود فقط على شاشة بحجم معين (مثلاً على آيباد بالعرض)، أو على متصفح معين. اكتشافها يدوياً عملية مرهقة ومملة جداً.
- الإحراج أمام العميل: أسوأ طريقة تكتشف فيها خطأ في شغلك هي لما العميل أو المستخدم هو اللي يخبرك عنه.
الحل المنقذ: الاختبار البصري التراجعي (Visual Regression Testing)
ببساطة شديدة، الاختبار البصري التراجعي هو عملية آلية بتلعب لعبة “اوجد الفروقات” بين نسختين من واجهة المستخدم تبعتك. الفكرة عبقرية وبسيطة:
- التقاط صورة أساسية (Baseline): في المرة الأولى، بنشغّل الاختبار على الواجهة السليمة والمكتملة، والأداة بتقوم بأخذ لقطات شاشة (screenshots) لكل صفحة أو مكون مهم وبتحفظها كـ “صور أساسية” أو “النسخة المرجعية”.
- التقاط صورة جديدة: بعد ما نعمل أي تعديل على الكود (مثلاً نغير لون زر، أو نحدث مكتبة CSS)، بنرجع نشغّل نفس الاختبار مرة ثانية.
- المقارنة وكشف الفروقات: الأداة بتقارن بشكل آلي بين الصورة الجديدة والصورة الأساسية المحفوظة عندها، بكسل ببكسل.
- إصدار تقرير: إذا كان في أي اختلاف، حتى لو كان بكسل واحد، الاختبار بيفشل وبيطلعلك تقرير يوضحلك الصورتين (القديمة والجديدة) وصورة ثالثة بتحدد مكان الاختلاف بالضبط.
وقتها، أنت كمطور بتقرر: هل هذا التغيير مقصود (مثلاً غيرت لون الزر عن قصد)؟ إذا نعم، بتكبس زر “Approve” وبتصير الصورة الجديدة هي الصورة الأساسية. أما إذا كان التغيير غير مقصود (زي ما صار معي في قصة الرسوم البيانية)، فهذا يعني إنك اكتشفت “Bug” ولازم تصلحه قبل ما يوصل للمستخدم. راحة بال ما إلها ثمن!
كيف تبدأ؟ الأدوات وأمثلة عملية
في أدوات كثيرة وقوية في السوق، منها المجاني ومنها المدفوع. من أشهرها Playwright, Cypress, Percy, و Applitools. أنا شخصياً بحب أستخدم Playwright لأنه من مايكروسوفت، مجاني، قوي جداً، ومدمج معه دعم للاختبار البصري بشكل مباشر بدون إضافات معقدة.
مثال عملي باستخدام Playwright
خلينا نفترض إنه عنا صفحة رئيسية وبدنا نتأكد إنه شكلها ما بتغير فجأة. الموضوع أبسط مما بتتخيل.
أولاً، بعد تنصيب Playwright في مشروعك، بتكتب ملف اختبار بسيط زي هاد (مكتوب بلغة TypeScript):
import { test, expect } from '@playwright/test';
test('Homepage should look the same as before', async ({ page }) => {
// 1. اذهب إلى الصفحة المطلوبة
await page.goto('https://my-awesome-app.com');
// 2. خذ لقطة شاشة وقارنها بالنسخة الأساسية
// اسم اللقطة 'homepage.png' هو مجرد اسم من اختيارك
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true, // لالتقاط صورة للصفحة كاملة
maxDiffPixels: 100 // السماح بـ 100 بكسل اختلاف كحد أقصى (مفيد لتجاهل اختلافات بسيطة جداً)
});
});
test('Submit button on contact form should not change', async ({ page }) => {
// يمكنك أيضاً فحص مكون واحد فقط بدلاً من الصفحة كلها
await page.goto('https://my-awesome-app.com/contact');
// حدد الزر الذي تريد فحصه
const submitButton = page.locator('#submit-contact-form');
// 2. خذ لقطة شاشة للزر فقط وقارنها
await expect(submitButton).toHaveScreenshot('contact-submit-button.png');
});
كيف يعمل هذا الكود؟
- أول مرة تشغّل الاختبار: بما إنه ما في صورة أساسية اسمها
homepage.png، بلاي رايت رح ينشئها ويحفظها في مجلد خاص. الاختبار سينجح. - المرات التالية: كل مرة بتشغل الاختبار، بلاي رايت رح ياخد لقطة جديدة ويقارنها باللقطة المحفوظة. إذا تطابقوا، ينجح الاختبار. إذا اختلفوا، يفشل الاختبار ويولد تقرير بالفرق.
نصيحة من أبو عمر: التعامل مع المحتوى الديناميكي
أحد أكبر التحديات في الاختبار البصري هو المحتوى المتغير: التواريخ، أسماء المستخدمين، الإعلانات، أو أي بيانات بتتغير مع كل زيارة. هاي الشغلات رح تخلي اختباراتك تفشل دايماً حتى لو ما في خطأ حقيقي.
الحل هو استخدام خاصية “الإخفاء” أو “Masking”. بتقدر تخبر أداة الاختبار إنها تتجاهل أجزاء معينة من الشاشة أثناء المقارنة. في Playwright، الموضوع سهل جداً:
test('User profile card should look correct', async ({ page }) => {
await page.goto('/profile/abu-omar');
// نريد فحص بطاقة المستخدم، لكن تاريخ "آخر ظهور" يتغير دائماً
const profileCard = page.locator('.profile-card');
const lastSeenElement = page.locator('.last-seen-timestamp');
await expect(profileCard).toHaveScreenshot('profile-card.png', {
// أخبر الأداة أن تضع مربع أسود فوق هذا العنصر قبل المقارنة
mask: [lastSeenElement]
});
});
بهذه الطريقة، أنت بتفحص التصميم العام للبطاقة بدون ما يتأثر الاختبار بالمحتوى الديناميكي اللي بداخلها. ذكاء!
نصائح عملية من خبرتي (خلاصة سنين من التجارب)
بعد استخدامي لهالتقنية لسنوات، جمعتلكم كم نصيحة ذهبية رح توفر عليكم وقت وجهد كبير:
- ابدأ صغيراً: لا تحاول تعمل اختبارات بصرية لكل شبر في موقعك من أول يوم. ابدأ بالأجزاء الحرجة: الهيدر والفوتر، الصفحة الرئيسية، عملية الدفع، وأهم المكونات اللي بتتكرر في كل مكان (مثل الأزرار والنماذج).
- الأتمتة هي المفتاح (CI/CD): القوة الحقيقية للاختبار البصري بتظهر لما تربطه بنظام الـ CI/CD تبعك (مثل GitHub Actions أو Jenkins). خلي الاختبارات تشتغل آلياً مع كل Pull Request. هيك، ما في أي تغيير بوصل للكود الرئيسي إلا وهو مفحوص بصرياً.
- افحص المكونات بشكل منفصل: يا جماعة، افحصوا القطع الصغيرة قبل ما تركّبوا السيارة كلها. استخدم أدوات مثل Storybook لعرض مكونات الواجهة (الأزرار، القوائم، إلخ) بشكل منعزل، وشغّل الاختبارات البصرية عليها. هذا أسرع وأكثر استقراراً من فحص الصفحات الكاملة.
- مراجعة الفروقات هي جزء من العملية: فشل الاختبار لا يعني دائماً وجود خطأ. أحياناً يكون التغيير مقصوداً. خصص وقتاً لمراجعة التقارير. إذا كان التغيير مقصوداً، قم بتحديث الصورة الأساسية (Baseline). إذا لم يكن مقصوداً، فهنيئاً لك، لقد أمسكت بـ “Bug” قبل أن يراه أي شخص آخر!
الخلاصة يا جماعة الخير ✅
في عالم تطوير البرمجيات السريع، من السهل جداً إنك تركز على الميزات الجديدة وتنسى جودة الواجهة البصرية. الاختبار البصري التراجعي ما عاد رفاهية، بل هو شبكة أمان ضرورية لكل مطور ومطورة واجهات أمامية (Frontend Developer) ولكل فريق ضمان جودة (QA).
صحيح، الإعداد الأولي بياخد شوية وقت، لكن راحة البال والثقة اللي بيعطيك إياها عند إطلاق أي تحديث جديد لا تقدر بثمن. من يوم ما تبنيت هذه المنهجية، صرت أنام وأنا مرتاح البال، وعارف إنه ما في “كارثة صامتة” بتستناني في اليوم التالي.
نصيحتي الأخيرة: ما تخلي تحديث بسيط يخرب سمعة شغلك وتعبه. استثمر شوية وقت في أتمتة فحص الواجهة، ورح تكسب ثقة عملائك وراحة بالك.
أتمنى تكون هالمقالة مفيدة، وإذا عندكم أي سؤال أو تجربة حابين تشاركوها، أنا موجود. بالتوفيق في مشاريعكم!