كان كل تحديث CSS مغامرة مرعبة: كيف أنقذنا ‘الاختبار البصري التراجعي’ من جحيم ‘لقد بدت أفضل على جهازي’؟

يا جماعة الخير، كيف حالكم؟ معكم أبو عمر. اسمحوا لي اليوم أن أرجع بالزمن قليلاً، لأيام كان فيها فنجان القهوة الصباحي لا يكتمل إلا بجرعة من القلق والتوتر قبل أي عملية نشر (Deployment) جديدة. سأحكي لكم قصة قصيرة، لكنها غيرت طريقة تفكير فريقي بأكمله.

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

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

قضينا الساعات الثلاث التالية في عملية “إطفاء حرائق” محمومة، ونحن نشرب كاسات الشاي بالنعناع واحدة تلو الأخرى، لنكتشف في النهاية أن تعديل CSS “البسيط” الذي أجراه سامر على الزر، أثّر بشكل غير متوقع على بعض الأنماط العامة (Global Styles) التي تستخدمها لوحة التحكم. الجملة التي ترددت في تلك الليلة كانت، بالطبع، “والله يا عمي، لقد بدت أفضل على جهازي!”.

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

ما هو الاختبار البصري التراجعي (Visual Regression Testing)؟

ببساطة شديدة، هو عملية أتمتة “لعبة الفروقات” التي كنا نلعبها في طفولتنا. الفكرة هي أن نلتقط “صوراً” أو “لقطات شاشة” لواجهات المستخدم الخاصة بنا في حالة مستقرة ومعروفة (نسميها الخط الأساسي – Baseline)، وبعد أي تعديل جديد على الكود، نلتقط لقطات جديدة (نسميها المرشح – Candidate)، ثم نستخدم أداة لمقارنة اللقطات الجديدة بالقديمة بكسل ببكسل.

إذا لم يكن هناك أي اختلاف، فهذا يعني أن تغييرك لم “يكسر” أي شيء بصرياً. أما إذا كان هناك اختلاف، فإن الأداة تولّد صورة ثالثة (نسميها الفرق – Diff) تسلط الضوء على الأجزاء التي تغيرت باللون الأحمر، لتقرر أنت كـ”حكم” ما إذا كان هذا التغيير مقصودًا أم أنه خطأ كارثي.

Visual Regression Testing Process
مخطط يوضح آلية عمل الاختبار البصري التراجعي

لماذا هو بهذه الأهمية؟

  • يكشف الأخطاء الصامتة: العديد من الأخطاء البصرية لا تؤدي إلى انهيار التطبيق أو ظهور خطأ في الكونسول. قد يكون مجرد عنصر مزاح ببضعة بكسلات، أو لون خط مختلف قليلاً، أو تداخل عنصرين في شاشة بحجم معين. هذه الأخطاء تدمر تجربة المستخدم بصمت.
  • يعطي ثقة بالنشر: بدلاً من الخوف من كل تعديل CSS، يصبح لديك شبكة أمان تخبرك على الفور إذا كان تعديلك قد أثر على جزء آخر من التطبيق لم تفكر فيه حتى.
  • يوفر الوقت والجهد: تخيل أنك لست بحاجة إلى فتح 20 صفحة مختلفة على 3 متصفحات و 4 أحجام شاشة مختلفة بعد كل تغيير. الأتمتة تقوم بهذا العمل الشاق بدلاً عنك.
  • يُنهي جدل “تعمل على جهازي”: الدليل الآن ليس رأيًا، بل صورة “Diff” واضحة تظهر المشكلة للجميع. لا مجال للمجادلة.

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

هناك العديد من الأدوات الرائعة في هذا المجال، بعضها خدمات مدفوعة مثل Percy و Chromatic (وهي ممتازة للمشاريع الكبيرة والفرق)، وبعضها مفتوح المصدر يمكنك استضافته بنفسك مثل BackstopJS و Playwright.

دعونا نأخذ مثالاً عملياً باستخدام BackstopJS، لأنها أداة رائعة ومجانية للبدء.

مثال عملي مع BackstopJS

لنفترض أن لدينا موقعاً بسيطاً ونريد التأكد من أن الصفحة الرئيسية وصفحة “من نحن” لا تتغيران بشكل غير متوقع.

1. التثبيت والإعداد:

أولاً، ستحتاج إلى تثبيت الأداة في مشروعك:

npm install -g backstopjs
# داخل مجلد مشروعك
backstopjs init

سيؤدي هذا إلى إنشاء ملف تكوين افتراضي اسمه backstop.json. هذا هو قلب الأداة وعقلها المدبر.

2. ضبط ملف التكوين:

سنقوم بتعديل ملف backstop.json ليشمل السيناريوهات التي نريد اختبارها. الملف يبدو شيئاً كهذا:

{
  "id": "my_awesome_project",
  "viewports": [
    {
      "label": "phone",
      "width": 320,
      "height": 480
    },
    {
      "label": "tablet",
      "width": 1024,
      "height": 768
    },
    {
      "label": "desktop",
      "width": 1920,
      "height": 1080
    }
  ],
  "onBeforeScript": "playwright/onBefore.js",
  "onReadyScript": "playwright/onReady.js",
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://localhost:3000",
      "selectors": ["document"],
      "misMatchThreshold": 0.1
    },
    {
      "label": "About Us Page",
      "url": "http://localhost:3000/about",
      "selectors": ["document"],
      "delay": 500
    },
    {
      "label": "Login Modal",
      "url": "http://localhost:3000",
      "clickSelector": "#login-button",
      "postInteractionWait": 1000,
      "selectors": ["#login-modal"]
    }
  ],
  "paths": {
    "bitmaps_reference": "backstop_data/bitmaps_reference",
    "bitmaps_test": "backstop_data/bitmaps_test",
    "engine_scripts": "backstop_data/engine_scripts",
    "html_report": "backstop_data/html_report",
    "ci_report": "backstop_data/ci_report"
  },
  "report": ["browser"],
  "engine": "playwright",
  "engineOptions": {
    "browser": "chromium",
    "args": ["--no-sandbox"]
  },
  "asyncCaptureLimit": 5,
  "asyncCompareLimit": 50,
  "debug": false,
  "debugWindow": false
}

شرح سريع للكود:

  • viewports: نحدد هنا أحجام الشاشات التي نريد الاختبار عليها (هاتف، جهاز لوحي، سطح مكتب).
  • scenarios: هذه هي قائمة الاختبارات. لكل سيناريو نحدد:
    • label: اسم وصفي للاختبار.
    • url: الرابط الذي سيتم اختباره.
    • selectors: يمكننا تحديد جزء معين من الصفحة لاختباره (مثل #main-header) أو الصفحة بأكملها (document).
    • clickSelector / postInteractionWait: يمكننا محاكاة تفاعل المستخدم، مثل النقر على زر لفتح نافذة منبثقة، ثم الانتظار قليلاً قبل التقاط الصورة.
  • engine: نحدد هنا محرك المتصفح الذي سيستخدم لالتقاط الصور. Playwright خيار حديث وقوي.

3. إنشاء الخط الأساسي (Baseline):

الآن، والتطبيق في حالته المستقرة، نقوم بتشغيل الأمر التالي لإنشاء لقطات الشاشة المرجعية:

backstopjs reference

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

4. إجراء الاختبار بعد التعديل:

الآن، اذهب وقم بتعديل CSS الذي تريده. بعد الانتهاء، بدلاً من القلق، قم بتشغيل الأمر التالي:

backstopjs test

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

5. المراجعة والموافقة:

إذا كان التغيير مقصوداً (مثلاً، غيرت لون زر عمداً)، يمكنك إخبار BackstopJS بقبول التغييرات الجديدة كخط أساسي جديد عبر الأمر:

backstopjs approve

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

نصائح أبو عمر الذهبية ✨

بعد سنوات من استخدام هذه التقنية، اسمحوا لي أن أقدم لكم بعض النصائح من القلب:

  • ابدأ صغيراً ولكن مهماً: لا تحاول اختبار كل بكسل في موقعك من اليوم الأول. ابدأ بالصفحات والمكونات الأكثر أهمية: الصفحة الرئيسية، صفحة المنتج، عملية الدفع، لوحة التحكم.
  • تجاهل المحتوى الديناميكي: تطبيقك قد يعرض بيانات متغيرة مثل “آخر زيارة كانت قبل 5 دقائق” أو إعلانات عشوائية. هذا سيؤدي إلى فشل الاختبارات دائماً. معظم الأدوات تسمح لك بإخفاء عناصر معينة (hideSelectors) أو استبدالها بمحتوى ثابت قبل التقاط الصورة.
  • أتمتة الأتمتة (CI/CD): القوة الحقيقية تكمن في دمج هذه الاختبارات مع سير عملك (CI/CD Pipeline). اجعل الاختبارات البصرية تعمل تلقائياً مع كل Pull Request. إذا فشل الاختبار، يتم منع الدمج (Merge) حتى يتم حل المشكلة. هذا يضمن عدم وصول أي خطأ بصري إلى الكود الرئيسي.
  • إدارة الخط الأساسي بحكمة: تحديث الخط الأساسي (approve) يجب أن يكون قراراً واعياً من الفريق، وليس مجرد نقرة زر للتخلص من “الفشل”. تأكد من أن كل تغيير تمت الموافقة عليه هو تغيير مقصود ومطلوب.

الخلاصة: من الفوضى إلى الثقة

التحول من الاعتماد على الاختبار اليدوي المجهد والممل إلى نظام آلي للاختبار البصري التراجعي كان واحداً من أفضل القرارات التقنية التي اتخذناها. لقد حول عملية نشر التحديثات من مغامرة مرعبة إلى عملية روتينية يمكن التنبؤ بها.

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

أبو عمر

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

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

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

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

آخر المدونات

الحوسبة السحابية

كانت خوادمنا خاملة 90% من الوقت: كيف أنقذتنا ‘الحوسبة بدون خوادم’ (Serverless) من جحيم التكاليف المهدرة؟

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

14 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت إجاباتي في المقابلات عشوائية: كيف أنقذتني منهجية STAR من جحيم أسئلة “حدثنا عن موقف…”؟

هل تجد نفسك تائهًا ومشتتًا عند الإجابة على أسئلة المقابلات السلوكية؟ في هذه المقالة، أشاركك تجربتي الشخصية مع منهجية STAR، الأداة التي حولت إجاباتي الفوضوية...

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

كيف أنقذ ‘موازن الحمل’ خادمنا الوحيد من الانهيار؟ قصة من قلب المعركة

هل يواجه تطبيقك بطئًا وتوقفًا مفاجئًا مع زيادة عدد المستخدمين؟ في هذه المقالة، أشارككم قصتي مع انهيار خادمنا الوحيد وكيف كان 'موازن الحمل' (Load Balancer)...

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

من كشط الشاشة إلى الخدمات المصرفية المفتوحة: كيف أنقذت واجهات الـ API تطبيقاتنا المالية؟

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

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

وداعاً لـ `kubectl apply -f`: كيف حولنا إدارة Kubernetes إلى عملية آلية وموثوقة مع GitOps؟

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

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

كانت الأفكار تموت في صمت: كيف أنقذتنا ‘السلامة النفسية’ من جحيم الخوف من الفشل؟

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

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