يا جماعة الخير، مسّاكم الله بالرضا… اسمحولي أرجع بالذاكرة ليلة ما بنساها. كنا قاعدين، أنا وفريق التطوير، الساعة وحدة بالليل، بنحتفل بإطلاق تحديث كبير على واحد من أكبر مشاريعنا. الأدرينالين عالي، والقهوة شغّالة، وكل الاختبارات الوظيفية (Unit, Integration, E2E) لونها أخضر وبتضوي. ضغطنا على زر النشر واحنا واثقين من شغلنا… “خلص، يعطينا العافية، المهمة تمت”.
ما هي إلا ساعة زمن، وإلا تلفون من العميل… وصوته مش عاجبني بالمرة. “أبو عمر، شو هاد؟ كل الموقع خربان!”. قلبي وقع بين رجليّ. فتحت الموقع من جهازي، كل شي تمام. فتحته من تلفوني، برضه تمام. بعد نص ساعة من الشد والجذب، اكتشفنا المصيبة: تغيير بسيط في ملف CSS عام، كان المفروض يصلّح شغلة صغيرة في صفحة اتصل بنا، تسبب في “زحف” زر الشراء الرئيسي في صفحة المنتج على أجهزة الآيباد بالعرض (Landscape mode) وصار شبه مخفي!
كل اختباراتنا الأوتوماتيكية نجحت لأن الزر كان “موجوداً” في الصفحة و”قابلاً للنقر” (functionally correct)، لكن بصرياً (visually) كان كارثة. هداك اليوم كان نقطة تحول إلنا. قررنا إنه لازم نلاقي حل لهاي المشاكل اللي ما بتلقطها الاختبارات التقليدية. وهون بدأت رحلتنا مع ما يسمى بـ “اختبار التراجع البصري” أو Visual Regression Testing.
شو قصة اختبار التراجع البصري (Visual Regression Testing)؟
بكل بساطة، تخيل إنك بتلعب لعبة “أوجد الفروقات السبعة” بين صورتين. هاد هو المبدأ بالزبط. اختبار التراجع البصري هو عملية أوتوماتيكية لأخذ “لقطات شاشة” (Screenshots) لواجهة المستخدم في تطبيقك، ومقارنتها بنسخ مرجعية “سليمة” تم اعتمادها سابقاً.
إذا كان في أي اختلاف، حتى لو كان بكسل واحد، النظام بنبهك. وقتها أنت كمطور بتقرر: هل هاد التغيير مقصود (مثلاً، غيرنا لون زر)؟ أو هو خطأ برمجي (Regression bug) لازم يتصلح؟
باختصار: الاختبارات التقليدية تسأل “هل الزر يعمل؟”، أما اختبار التراجع البصري فيسأل “هل الزر يبدو كما يجب أن يكون وفي مكانه الصحيح؟”.
ليش الاختبارات العادية ما بتكفي؟
زي ما صار معنا في قصتنا، الاختبارات التقليدية ممتازة للتأكد من منطق العمل (Business Logic)، لكنها عمياء تماماً عن الجانب البصري. ممكن اختبار End-to-End ينجح في النقر على زر حتى لو كان:
- لونه أبيض على خلفية بيضاء (غير مرئي للمستخدم).
- نصّه طالع برّا حدوده.
- مغطي عليه عنصر ثاني (z-index issue).
- حجمه صغير جداً على شاشات الموبايل.
كل هاي “كوارث” من ناحية تجربة المستخدم، لكنها “حالات ناجحة” في نظر الاختبارات التقليدية. اختبار التراجع البصري هو شبكة الأمان اللي بتصيد هاي المشاكل.
يلا نشتغل عملي: كيف نبدأ؟
العملية بتمر بخطوات واضحة:
- إنشاء خط الأساس (Baseline): أول مرة بتشغّل الاختبارات على نسخة مستقرة من تطبيقك، بتقوم الأداة بأخذ لقطات شاشة لكل الصفحات والمكونات اللي حددتها، وبتحفظها كـ “النسخة المرجعية” أو الـ Baseline.
- التشغيل والمقارنة: بعد أي تعديل على الكود، بتشغّل الاختبارات مرة ثانية. الأداة بتاخد لقطات شاشة جديدة.
- تحليل الفروقات: الأداة بتقارن بين الصور الجديدة والصور المرجعية بكسل ببكسل.
- إصدار التقرير: لو في أي اختلاف، الاختبار “بفشل” وبتطلعلك صورة ثالثة بتوضح الفروقات باللون الأحمر عشان تشوفها بسهولة.
- المراجعة واتخاذ القرار: هون بيجي دورك. بتشوف الفرق:
- إذا كان خطأ: بتروح تصلحه. الحمد لله إنه انصاد قبل ما يوصل للمستخدم.
- إذا كان تغيير مقصود: بتكبس زر “Approve”، وهيك الصورة الجديدة بتصير هي الـ Baseline للمستقبل.
أشهر الأدوات في الساحة: Playwright و Cypress
في أدوات كتير، لكن حالياً الأضواء مسلطة على عملاقين في عالم اختبارات الواجهات الأمامية، وكل واحد فيهم عنده طريقة للتعامل مع الاختبار البصري.
h3>1. Playwright: القوة المدمجة من مايكروسوفت
Playwright أداة رائعة من مايكروسوفت، وأجمل ما فيها إن ميزة اختبار لقطات الشاشة مدمجة فيها من الأساس (Built-in)، ما بتحتاج إضافات خارجية. سهلة وقوية جداً.
مثال كود باستخدام Playwright:
تخيل عنا اختبار بسيط لصفحة الهبوط (Landing Page):
// tests/landing-page.spec.ts
import { test, expect } from '@playwright/test';
test('صفحة الهبوط الرئيسية يجب أن تبدو سليمة', async ({ page }) => {
// 1. اذهب إلى الصفحة
await page.goto('https://my-awesome-app.com');
// 2. انتظر حتى يتم تحميل عنصر معين لضمان استقرار الصفحة
await page.waitForSelector('h1');
// 3. خذ لقطة شاشة وقارنها بالـ baseline
// أول مرة، سيتم إنشاء ملف 'landing-page.png'
// في المرات التالية، سيتم مقارنة الشاشة الحالية بهذا الملف
await expect(page).toHaveScreenshot('landing-page.png', {
maxDiffPixels: 100 // السماح بـ 100 بكسل مختلف كحد أقصى (مفيد لتجنب الفشل بسبب اختلافات طفيفة في العرض)
});
});
test('زر "ابدأ الآن" يجب أن يبدو سليماً', async ({ page }) => {
await page.goto('https://my-awesome-app.com');
// يمكن أخذ لقطة شاشة لعنصر محدد فقط
const ctaButton = page.locator('#cta-button');
await expect(ctaButton).toHaveScreenshot('cta-button.png');
});
h3>2. Cypress: المرونة وقوة المجتمع
Cypress أداة محبوبة جداً لسهولتها وتجربة المطور الممتازة اللي بتقدمها. هي ما بتيجي مع اختبار بصري مدمج، لكن مجتمعها النشط وفر إضافات (plugins) قوية جداً بتأدي الغرض وأكثر.
أحد أشهر الإضافات هي @cypress/snapshot أو حلول طرف ثالث مثل cypress-image-snapshot أو خدمات سحابية مثل Applitools أو Percy.
مثال كود باستخدام Cypress مع إضافة (مثل cypress-image-snapshot):
بعد تثبيت وتهيئة الإضافة، يصبح الاختبار شبيهاً بهذا:
// cypress/e2e/landing-page.cy.ts
describe('اختبارات الواجهة البصرية لصفحة الهبوط', () => {
it('يجب أن تطابق صفحة الهبوط لقطة الشاشة المرجعية', () => {
cy.visit('https://my-awesome-app.com');
// ببساطة، أضف هذا الأمر
// سيقوم بأخذ لقطة ومقارنتها
cy.matchImageSnapshot('landing-page');
});
it('يجب أن يطابق زر "ابدأ الآن" لقطة الشاشة المرجعية', () => {
cy.visit('https://my-awesome-app.com');
// يمكن تحديد عنصر معين لأخذ لقطة له
cy.get('#cta-button').matchImageSnapshot('cta-button');
});
});
نصائح من خبرة أبو عمر العملية 🤓
بعد ما تبنينا هاي الثقافة، تعلمنا كم شغلة “من الكيس” زي ما بحكوها، وحابب أشارككم إياها:
- ابدأ بالصفحات الحرجة: ما في داعي تغطي 100% من التطبيق من أول يوم. ابدأ بالصفحات اللي بتجيب فلوس أو اللي بشوفها المستخدم أول شي: الصفحة الرئيسية، صفحة المنتج، سلة الشراء، صفحة الدفع.
- تعامل مع المحتوى المتغير (Dynamic Content): أكبر عدو للاختبار البصري هو المحتوى المتغير زي الإعلانات، التواريخ، عدادات الزوار، أو “أحدث التغريدات”. لازم تتعلم كيف “تخفي” هاي العناصر من لقطة الشاشة أو تستخدم بيانات وهمية (Mock Data) ثابتة أثناء الاختبار. معظم الأدوات بتسمحلك تعمل هيك.
- لا تكن متزمتاً جداً في المقارنة: مقارنة 100% بكسل ببكسل ممكن تفشل بسبب اختلافات طفيفة جداً في عرض الخطوط بين الأجهزة (anti-aliasing). استخدم خاصية “عتبة الاختلاف” (threshold) للسماح بنسبة صغيرة جداً من الاختلاف (مثلاً 0.1%) لتجنب الإنذارات الكاذبة.
- اجعلها جزءاً من الـ CI/CD Pipeline: القوة الحقيقية بتظهر لما تدمج هاي الاختبارات في عملية الدمج المستمر. مع كل Pull Request جديد، بتشتغل الاختبارات أوتوماتيكياً، ولو في أي تغيير بصري غير متوقع، بيظهرلك مباشرة في تقرير الـ PR قبل ما تدمج الكود. هذا هو الأمان الحقيقي.
- اختبر على عدة متصفحات وأحجام شاشة: المشكلة اللي صارت معنا كانت خاصة بالآيباد. لا تنسَ أن المشاكل البصرية غالباً ما تكون مرتبطة بأحجام شاشات معينة. أدوات مثل Playwright تجعل اختبار المتصفحات المختلفة (Chrome, Firefox, Safari) سهلاً للغاية.
الخلاصة: استثمار يستحق كل قرش (وكل سطر كود)
تبني اختبارات التراجع البصري كان نقلة نوعية في جودة عملنا وثقتنا بأنفسنا. نعم، هو يحتاج لجهد في البداية لإنشاء الـ Baseline وصيانة الاختبارات، لكن هذا الجهد لا يقارن أبداً بالوقت والسمعة اللي ممكن تضيعها في إصلاح أخطاء بصرية محرجة بعد وصولها للإنتاج.
لم نعد نخاف من كبسة “النشر” في آخر الليل. صرنا ننام قريري العين، عارفين إنه في “حارس” آلي بيراقب تصميمنا وبيحمينا من أنفسنا أحياناً. هو ليس بديلاً عن الاختبار اليدوي أو عن عين المصمم الخبيرة، ولكنه خط دفاع أول قوي جداً يجعلك تنام مرتاح البال. ✅
نصيحتي لكل فريق تطوير واجهات أمامية: ابدأوا اليوم، ولو بخطوة صغيرة. مستقبلكم (وعملائكم) سيشكرونكم. يلا شدوا حيلكم! 🚀