واجهاتنا كانت تتغير خلسة: كيف أنقذنا ‘الاختبار البصري التراجعي’ من جحيم الأخطاء المرئية

يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.

قبل كم سنة، كنا شغالين على مشروع كبير ومهم، وكان فريقنا “طاير” في الإنجاز. الأكواد تنكتب، الميزات تتسلم، والكل مبسوط. قبل موعد الإطلاق بأسبوع، عملنا اجتماع أخير لنستعرض المنتج بشكله النهائي. فتحنا الموقع، والأمور تبدو تمام… للوهلة الأولى.

فجأة، واحد من فريق التسويق بيحكي: “لحظة، ليش زر ‘اشتر الآن’ في صفحة المنتجات لونه باهت وطالع فوق الوصف؟ مبارح كان تمام!”.

صار الكل يطلع على الشاشة، وفعلاً، الزر كان “سابح” في مكان غلط ولونه متغير. بلشنا رحلة البحث عن السبب. فريق الواجهة الأمامية (Frontend) أقسموا إنهم ما لمسوا هاي الصفحة من أسبوع. فريق الواجهة الخلفية (Backend) أكدوا إن البيانات سليمة. قضينا ساعات طويلة، والله يا جماعة ساعات، ونحنا بننبش في الكود، بنراجع آخر التغييرات (commits)، والتوتر بيزيد مع كل دقيقة بتمر.

بالآخر، وبعد ما قربنا نستسلم، اكتشفنا الكارثة. واحد من المطورين كان بيشتغل على صفحة “اتصل بنا” (صفحة مختلفة تماماً)، وغير تنسيق (style) عام لأحد العناصر في ملف CSS مشترك. التغيير كان بسيط، سطر واحد، لكنه أثّر بشكل غير متوقع على الزر في صفحة المنتجات اللي بتستخدم نفس التنسيق. كان خطأ خفي، بصري بحت، ما بيظهر في أي اختبار منطقي أو وحدة (Unit Test).

هذاك اليوم، أدركت إنه اعتمادنا على عيوننا بس لمراجعة كل شبر في التطبيق مع كل تغيير هو وصفة لكارثة محققة. ومن هنا، بلشت رحلتي مع عالم “الاختبار البصري التراجعي” أو الـ Visual Regression Testing.

ما هو هذا “الشبح” الذي يغير واجهاتنا؟

المشكلة اللي واجهناها الها اسم: الأخطاء البصرية التراجعية (Visual Regressions). هاي الأخطاء هي تغييرات غير مقصودة في واجهة المستخدم (UI) بتصير نتيجة تعديلات في الكود. ممكن تكون:

  • عناصر متداخلة فوق بعضها.
  • ألوان أو خطوط خاطئة.
  • اختفاء أجزاء من الصفحة.
  • تغير في التخطيط (Layout) على شاشات معينة.

المشكلة الأكبر إن أدوات الاختبار التقليدية (مثل اختبارات الوحدة أو التكامل) عمياء عن هاي الأخطاء. هي بتتأكد إن الدالة بترجع القيمة الصحيحة، أو إن الزر بيعمل الأكشن المطلوب، لكنها ما بتعرف “شكل” الزر كيف صار.

الاختبار البصري التراجعي: حارس المرمى لواجهة المستخدم

ببساطة شديدة، الاختبار البصري التراجعي هو عملية مؤتمتة بتقارن “صور” لواجهة المستخدم قبل وبعد التغيير، وبتنبهك لو في أي اختلاف. فكر فيها كأنك بتوظف روبوت عنده ذاكرة فوتوغرافية عشان يلعب معك لعبة “أوجد الفروقات” على كل صفحة في تطبيقك.

الآلية تبعته بتمر بخطوات بسيطة ومنطقية:

  1. الصورة المرجعية (Baseline): في المرة الأولى، بتاخذ “لقطة شاشة” (screenshot) للواجهة في حالتها السليمة والمثالية. هاي بنسميها الصورة المرجعية أو الأساس.
  2. التغيير والتصوير: بعد ما أنت أو أي حدا في الفريق يعمل تغييرات على الكود، النظام بيقوم بأخذ لقطة شاشة جديدة لنفس الواجهة.
  3. المقارنة: يتم مقارنة الصورة الجديدة بالصورة المرجعية بكسل ببكسل.
  4. التقرير: إذا ما في أي اختلاف، الاختبار بينجح. أما إذا وُجد اختلاف، الاختبار بيفشل وبيولد صورة ثالثة بتوضح لك الفروقات باللون الأحمر عشان تشوفها بوضوح.
  5. القرار البشري: هنا يأتي دورك. بتراجع الاختلافات، وبتقرر:
    • إذا كان التغيير مقصودًا: بتقبل التغيير وبتحدّث الصورة المرجعية لتصير هي الأساس الجديد.
    • إذا كان التغيير خطأ (Bug): بترفض التغيير وبترجع الكود للمطور ليصلحه.

كيف نبدأ مع الاختبار البصري؟ الأدوات والخطوات العملية

الحكي النظري حلو، بس خلينا نشوف كيف بنطبق هالكلام على أرض الواقع. اليوم السوق مليان أدوات، منها المجاني ومنها المدفوع. من أشهرها: Playwright, Cypress (مع إضافات), Storybook, Percy, و Applitools.

شخصيًا، أنا بميل لاستخدام Playwright لأنه أداة متكاملة من مايكروسوفت بتدعم هذا النوع من الاختبارات بشكل أساسي وبدون إضافات معقدة.

الخطوة الأولى: إعداد بيئة Playwright

إذا ما عندك Playwright، إعداده بسيط جداً. افتح الطرفية (Terminal) في مشروعك واكتب:

npm init playwright@latest

هذا الأمر رح ينزل كل شي بتحتاجه ويهيئ لك ملفات الإعدادات الأساسية.

الخطوة الثانية: كتابة أول اختبار بصري

لنفترض عنا صفحة رئيسية وبدنا نتأكد إنها ما بتتغير بشكل غير متوقع. بنعمل ملف اختبار جديد، مثلاً tests/homepage.spec.js، وبنكتب فيه الكود التالي:


// tests/homepage.spec.js
const { test, expect } = require('@playwright/test');

test('الصفحة الرئيسية يجب أن تبدو كما هي', async ({ page }) => {
  // 1. اذهب إلى الصفحة المطلوبة
  await page.goto('http://localhost:3000');

  // 2. التقط صورة وقارنها بالمرجعية
  // اسم اللقطة هنا هو 'homepage-screenshot.png'
  await expect(page).toHaveScreenshot('homepage-screenshot.png');
});

شرح الكود:

  • page.goto(...): بتوجه المتصفح الافتراضي للعنوان اللي بدنا نختبره.
  • expect(page).toHaveScreenshot(...): هاي هي الجوهرة. هذا السطر هو اللي بيقوم بكل السحر.

الخطوة الثالثة: تشغيل الاختبار وفهم النتائج

لما تشغل الاختبار لأول مرة، Playwright رح يشوف إنه ما في صورة مرجعية اسمها homepage-screenshot.png، فرح يقوم بإنشاء واحدة وحفظها في مجلد خاص (عادة اسمه tests/homepage.spec.js-snapshots). ورح ينجح الاختبار.

الآن، روح غير أي شي في الصفحة الرئيسية، مثلاً لون الخلفية أو حجم الخط، وشغل الاختبار مرة ثانية:

npx playwright test

هالمرة، الاختبار رح يفشل! والجميل في الموضوع إن Playwright رح يعطيك تقرير مفصل. رح تلاقي 3 صور في مجلد النتائج:

  1. actual.png: الصورة الجديدة بعد التغيير.
  2. expected.png: الصورة المرجعية القديمة.
  3. diff.png: صورة بتوضح الفروقات بين الصورتين باللون الأحمر.

هذه الصورة “diff” هي أفضل صديق لك. في ثوانٍ معدودة، بتكشف لك الخطأ اللي كان ممكن ياخذ منك ساعات من البحث.

الخطوة الرابعة: تحديث الصور المرجعية

طيب، لو كان التغيير اللي عملته مقصود؟ مثلاً، غيرنا تصميم الهيدر بشكل رسمي. في هاي الحالة، بدنا نحدّث الصورة المرجعية. بنعمل هالشي بأمر بسيط:

npx playwright test --update-snapshots

هذا الأمر رح ياخذ اللقطات الجديدة ويعتمدها كالصور المرجعية للمرات القادمة.

نصائح من خبرة “أبو عمر”: كيف تتجنب الفوضى؟

تطبيق الاختبار البصري ممكن يتحول لفوضى إذا ما تم بشكل صحيح. هاي شوية نصائح من القلب عشان تبدأ بداية صحيحة:

  • ابدأ صغيراً وتدريجياً: لا تحاول تغطية كل التطبيق من أول يوم. ابدأ بصفحة واحدة مهمة أو مكون حرج (Critical Component). “إشي أحسن من ولا إشي”. بعد ما تستقر العملية، توسع شوي شوي.
  • تعامل مع المحتوى الديناميكي: ماذا عن التاريخ والوقت، أو إعلانات بتتغير، أو محتوى بيجيبه المستخدم؟ هاي الأمور رح تسبب فشل دائم للاختبارات. الحل هو “إخفاء” (masking) هاي الأجزاء. Playwright بيسمح لك تعمل هالشي بسهولة:

    
    // إخفاء عنصر له id معين قبل أخذ اللقطة
    await expect(page).toHaveScreenshot('my-screenshot.png', { 
      mask: [page.locator('#dynamic-content')] 
    });
            
  • حدد “هامش الخطأ” (Threshold): أحياناً، بيكون في اختلافات طفيفة جداً بين أنظمة التشغيل أو كروت الشاشة في طريقة عرض الخطوط (anti-aliasing). عشان تتجنب فشل الاختبار بسبب هاي الفروقات اللي ما بتُرى بالعين المجردة، ممكن تحدد نسبة سماح بسيطة:

    
    // السماح باختلاف يصل إلى 5% من إجمالي البكسلات
    await expect(page).toHaveScreenshot('my-screenshot.png', { 
      maxDiffPixelRatio: 0.05 
    });
            
  • أتمتة العملية مع CI/CD: القوة الحقيقية بتظهر لما تدمج هاي الاختبارات مع نظام التكامل المستمر (CI/CD) مثل GitHub Actions. مع كل Pull Request جديد، الاختبارات البصرية بتشتغل تلقائياً. وإذا فشلت، بيتم رفع صور الفروقات (diffs) كـ “artifacts” ليراجعها الفريق قبل دمج الكود. هذا بيمنع وصول الأخطاء البصرية للفرع الرئيسي من الأساس.

الخلاصة: درهم وقاية خيرٌ من قنطار علاج

يا جماعة، الاستثمار في الاختبار البصري التراجعي مش رفاهية، بل هو ضرورة في عالم تطوير الويب الحديث. هو بمثابة شبكة أمان بتحمي تجربة المستخدم، وبتوفر على فريقك ساعات لا تحصى من التنبيش عن أخطاء خبيثة. صحيح، إعداده في البداية بيحتاج شوية وقت وجهد، لكن العائد على المدى الطويل ضخم جداً: ثقة أكبر عند إطلاق التحديثات، نوم أهنأ في ليلة الإطلاق، ومنتجات بجودة بصرية ثابتة وممتازة.

نصيحتي الأخيرة: لا تنتظروا “كارثة الزر السابح” تصير معكم. ابدأوا اليوم، ولو بخطوة صغيرة. جربوا أداة مثل Playwright على مكون واحد، وشوفوا الفرق بنفسكم. 😉

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

التوسع والأداء العالي والأحمال

كان مستخدمونا في الطرف الآخر من العالم ينتظرون إلى الأبد: كيف أنقذتنا شبكات توصيل المحتوى (CDN) من جحيم زمن الاستجابة المرتفع؟

قصة حقيقية من قلب المعركة البرمجية، وكيف انتقلنا من شكاوى المستخدمين بسبب بطء الموقع إلى أداء صاروخي عالمي. في هذه المقالة، أغوص معكم في عالم...

30 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من شبكة مثقوبة إلى حصن منيع: كيف أنقذتنا قواعد البيانات الرسومية من كابوس الاحتيال؟

كنا نغرق في بحر من الإنذارات الكاذبة والشبكات الاحتيالية المعقدة التي لم تستطع قواعدنا التقليدية كشفها. في هذه المقالة، أسرد لكم تجربتي كـ "أبو عمر"...

30 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

ميزانيات الخطأ (Error Budgets): كيف أنهت كابوس مكالمات منتصف الليل وأنقذتنا من الإرهاق؟

كنا غارقين في مكالمات طوارئ ليلية لا تنتهي، فريق منهك والمنتج على المحك. في هذه المقالة، أشارككم قصة كيف أنقذنا مفهوم "ميزانيات الخطأ" (Error Budgets)...

30 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كانت اجتماعاتنا الفردية استجواباً صامتاً: كيف حولنا الـ 1-on-1 من تقرير حالة ممل إلى محرك لنمو الفريق؟

أشارككم تجربتي كقائد فريق تقني في تحويل الاجتماعات الفردية (1-on-1s) من جلسات استجواب مملة إلى محادثات مثمرة تساهم في بناء الثقة وتطوير الفريق. هذه المقالة...

30 مايو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت اختباراتنا تصرخ ‘الذئب’: كيف قضينا على ‘الاختبارات المتقلبة’ (Flaky Tests) واستعدنا الثقة في خطوط الأنابيب؟

في هذه المقالة، أشارككم قصة من أرض المعركة البرمجية، وكيف تغلب فريقي على كابوس "الاختبارات المتقلبة" أو Flaky Tests. سنغوص في أسبابها الخفية، ونتعلم استراتيجيات...

30 مايو، 2026 قراءة المزيد
أدوات وانتاجية

كانت أصابعي تصرخ من التكرار: كيف أنقذتني ‘مقتطفات الشفرة’ (Code Snippets) من جحيم كتابة Boilerplate؟

أشارككم قصتي مع التكرار الممل في البرمجة وكيف غيرت "مقتطفات الشفرة" (Code Snippets) طريقة عملي تماماً. دليل عملي من مبرمج فلسطيني لزيادة إنتاجيتك والتخلص من...

30 مايو، 2026 قراءة المزيد
أتمتة العمليات

كانت تبعياتنا قنبلة موقوتة: كيف أنقذنا ‘التحديث الآلي للتبعيات’ من جحيم الثغرات الأمنية المنسية؟

أشارككم قصة حقيقية عن ليلة كادت فيها ثغرة أمنية في إحدى المكتبات المنسية أن تدمر مشروعنا بالكامل. اكتشفوا معنا كيف تحولنا من الفوضى إلى الأمان...

30 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت شفرتنا هرمًا من الهلاك: كيف أنقذتنا ‘شروط الحماية’ (Guard Clauses) من جحيم الـ if/else المتداخلة؟

في هذه المقالة، أشارككم قصة حقيقية من مسيرتي كمبرمج عن المعاناة مع الشفرات المتداخلة "هرم الهلاك". سنتعلم كيف تنقذنا تقنية "شروط الحماية" (Guard Clauses) من...

30 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

كانت خدماتنا متلاصقة كالغراء: كيف أنقذتنا ‘المعمارية الموجهة بالأحداث’ (EDA) من جحيم الاقتران المحكم؟

أنا أبو عمر، وهذا المقال ليس مجرد شرح تقني، بل هو قصة حقيقية من الخنادق البرمجية. سأروي لكم كيف كنا نغرق في بحر "الاقتران المحكم"،...

30 مايو، 2026 قراءة المزيد
البودكاست