إختبار الانحدار البصري (visual regression) باستخدام Cypress

Cypress

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

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

اختبار visual regression من خلال اختبار واجهة المستخدم

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

من cypress إلى screenshot

بعد الاطلاع على الوثائق المتاحة قررنا تجربة البرنامج المساعد cypress-snapshot. استغرق الأمر بضع دقائق فقط للإعداد وبمجرد أن فعلنا ذلك أدركنا بسرعة أننا لسنا في السعي وراء ناتج الانحدار البصري التقليدي.

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

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

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

إختبار الانحدار البصري (visual regression) باستخدام Cypress

تسخير واجهات برمجة التطبيقات لمقارنة الصور

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

كان من الممكن أن يكون الأسلوب البديل هو التقاط لقطات شاشة للصفحة بأكملها في اللحظات الحاسمة أثناء الاختبار لكننا قررنا أن هذه الصور ستكون صعبة للغاية للمقارنة. أيضًا قد تكون مثل هذه المقارنات أكثر عرضة للخطأ البشري مثل فقدان أن التذييل قد تغير.

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

لجأنا إلى واجهة برمجة تطبيقات Cypress لإنشاء الصور. يمكن للأمر cy.screenshot ()  من خارج الصندوق إنشاء صور فردية للمكونات وتسمح لنا واجهة برمجة تطبيقات After Screenshot بإعادة تسمية الملفات وتغيير الأدلة وتمييز عمليات الانحدار المرئي عن تلك القياسية. من خلال الجمع بين الاثنين أنشأنا عمليات تشغيل لم تؤثر على اختباراتنا الوظيفية ومكنتنا من تخزين الصور في مجلداتها المناسبة.

أولاً قمنا بتوسيع ملف index.js في دليل المكونات الإضافية لدينا لدعم نوعي التشغيل الجديدين (خط الأساس والمقارنة). بعد ذلك نضع مسار صورنا وفقًا لنوع التشغيل:

// plugins/index.js
const fs = require('fs')
const path = require('path')
module.exports = (on, config) => {
// Adding these values to your config object allows you to access them in your tests.
  config.env.baseline = process.env.BASELINE || false
  config.env.comparison = process.env.COMPARISON || false

  on('after:screenshot', details => {
    // We only want to modify the behavior of baseline and comparison runs.
    if (config.env.baseline || config.env.comparison) {
      // We keep track of the file name and number to make sure they are saved in the proper order and in their relevant folders.
      // An alternative would have been to look up the folder for the latest image, but this was the simpler approach.
      let lastScreenshotFile = ''
      let lastScreenshotNumber = 0

      // We append the proper suffix number to the image, create the folder, and move the file.
      const createDirAndRename = filePath => {
        if (lastScreenshotFile === filePath) {
          lastScreenshotNumber++
        } else {
          lastScreenshotNumber = 0
        }
        lastScreenshotFile = filePath
        const newPath = filePath.replace(
          '.png',
          ` #${lastScreenshotNumber}.png`
        )

        return new Promise((resolve, reject) => {
          fs.mkdir(path.dirname(newPath), { recursive: true }, mkdirErr => {
            if (mkdirErr) {
              return reject(mkdirErr)
            }
            fs.rename(details.path, newPath, renameErr => {
              if (renameErr) {
                return reject(renameErr)
              }
              resolve({ path: newPath })
            })
          })
        })
      }

      const screenshotPath = `visualComparison/${config.env.baseline ? 'baseline' : 'comparison'}`

      return createDirAndRename(details.path
        .replace('cypress/integration', screenshotPath)
        .replace('All Specs', screenshotPath)
      )
    }
  })
  return config
}

ثم استدعينا كل من عمليات التشغيل عن طريق إضافة متغير البيئة المقابل لاستدعاء Cypress في package.json للمشروع:

"scripts": {
  "cypress:baseline": "BASELINE=true yarn cypress:open",
  "cypress:comparison": "COMPARISON=true yarn cypress:open"
}

بمجرد تشغيل أوامرنا الجديدة يمكننا أن نرى أنه تم نقل جميع لقطات الشاشة التي تم التقاطها أثناء التشغيل إلى المجلدات المناسبة.

إختبار الانحدار البصري (visual regression) باستخدام Cypress

بعد ذل حاولنا الكتابة فوق cy.get ()  وهو الأمر الرئيسي لـ Cypress لإرجاع عناصر DOM والتقاط لقطة شاشة لأي عناصر يتم استدعاؤها جنبًا إلى جنب مع تنفيذها الافتراضي. لسوء الحظ يعد cy.get ()  أمرًا خادعًا للتغيير لأن استدعاء الأمر الأصلي في تعريفه الخاص يؤدي إلى حلقة لا نهائية. تتمثل الطريقة المقترحة للتغلب على هذا القيد في إنشاء أمر مخصص منفصل ثم جعل هذا الأمر الجديد يأخذ لقطة شاشة بعد العثور على العنصر:

Cypress.Commands.add("getAndScreenshot", (selector, options) => {
  // Note: You might need to tweak the command when getting multiple elements.
  return cy.get(selector).screenshot()
});

it("get overwrite", () => {
  cy.visit("https://example.cypress.io/commands/actions");
  cy.getAndScreenshot(".action-email")
})

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

النتائج التي تم الحصول عليها عن طريق اختبار (visual regression)

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

إختبار الانحدار البصري (visual regression) باستخدام Cypress

إضافة قيمة إلى اختبار واجهة المستخدم

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

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

المصدر

منشور ذات صلة
الخوارزمية 6 Minutes

مقدمة في الخوارزمية

جاسم ناظري

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

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

السلة