اختبارات المتصفح (Laravel Dusk)

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

مقدمة

يوفّر لك Laravel Dusk أتمتة للمتصفّح واختبار للواجهات البرمجيّة بطريقة سهلة الاستخدام. بشكلٍ افتراضي، لا يتطلّب Dusk تنصيب JDK أو Selenium على جهازك، حيث يستعمل تثبيت مستقل (standalone) لبرمجية ChromeDriver. بأي حال، يمكنك استخدام أي برنامج تشغيل متوافق مع Selenium إذا أردت.

التنصيب

للبدء، أضف الاعتمادية laravel/dusk إلى مشروعك:

composer require --dev laravel/dusk

بعد تنصيب Dusk، سجّل مزوّد الخدمة الذي يتبع إلى Dusk، وهو Laravel\Dusk\DuskServiceProvider. بشكل عام، يكون هذا تلقائيًّا باستخدام التسجيل التلقائي لمزوّدات خدمة Laravel.

تنبيه: إذا قمت بتسجيل مزوّد خدمة Dusk يدويًّا، لا تسجّله ببيئة الإنتاج، إذ سينجرّ عن ذلك إمكانية استيثاق المستخدمين العشوائيين مع تطبيقك.

بعد تنصيب حزمة Dusk، شغّل الأمر artisan dusk:install:

php artisan dusk:install

سينشئ هذا الأمر مجلّد Browser داخل مجلّد الـ tests، وملف يحتوي على تجربة اختبار. بعد ذلك، عيّن متحول البيئة APP_URL في الملف env.، يجب أن يكون الرابط المعيّن في متحول البيئة موافقًا للرابط الذي تصل إلى موقعك من خلاله. لتشغيل اختباراتك، شغّل أمر artisan dusk. يقبل الأمر dusk أيّة وسائط مقبولة أيضًا من الأمر phpunit:

php artisan dusk

استخدام متصفّحات أخرى

بشكل افتراضي، يستخدم Dusk المتصفّح Chrome وتثبيت مستقل (standalone) لبرمجية ChromeDriver لتشغيل اختباراتك. لكن يمكنك تشغيل خادم Selenium الخاص بك وتشغيل اختباراتك على أيّ متصفّح من اختيارك.

للبدء، افتح الملف tests/DuskTestCase.php، الذي يحتوي على حالة الاختبار الأساسية لتطبيقك. في هذا الملف، يمكنك إزالة استدعاء التابع startChromeDriver، الأمر الذي سيوقف Dusk من تشغيل برمجية ChromeDriver تلقائيًّا.

/**
* التجهيز للبدء باختبار Dusk..
*
* @beforeClass
* @return void
*/
public static function prepare()
{
  // static::startChromeDriver();
}

بعد ذلك، قم بتعديل التابع driver للاتصال بالرابط والمنفذ الذي تريد. علاوةً على ذلك، يمكنك تعديل "الإمكانيات المتاحة" (Desired Capabilities) التي يجب أن تمرّر إلى WebDriver.

/**
* إنشاء كائن من نوع RemoteWebDriver..
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
   return RemoteWebDriver::create(
       'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
   );
}

البدء

توليد الاختبارات

لتوليد اختبار Dusk، شغّل الأمر artisan dusk:make. سيولد ملف الاختبار في مجلّد tests/Browser:

php artisan dusk:make LoginTest

تشغيل الاختبارات

لتشغيل اختبارات المتصفّحات، استخدم الأمر artisan dusk:

php artisan dusk

يقبل الأمر dusk الوسائط التي تقبل بشكل عادي من قبل مشغّل اختبارات PHPUnit، ممّا يتيح لك تشغيل الاختبارات ضمن مجموعة محددة، والعديد من الخيارات الأخرى:

php artisan dusk --group=foo

تشغيل ChromeDriver يدويًّا

يحاول Dusk تشغيل ChromeDriver افتراضيًا. في حال لم يكن يعمل ذلك في نظامك، يمكنك تشغيل ChromeDriver يدويًّا قبل تشغيل الأمر dusk. في حال قمت بتشغيله يدويًّا، يجب أن تعلّق السطر التالي في الملف tests/DuskTestCase.php:

/**
* التجهيز للبدء باختبار Dusk..
*
* @beforeClass
* @return void
*/

public static function prepare()
{
  // static::startChromeDriver();
}

إضافةً إلى ذلك، إذا شغّلت الـ ChromeDriver على منفذ مختلف عن المنفذ 9515، يجب عليك تعديل التابع driver الموجود في نفس الصنف:

/**
* إنشاء كائن من نوع RemoteWebDriver..
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/

protected function driver()
{
   return RemoteWebDriver::create(
       'http://localhost:9515', DesiredCapabilities::chrome()
   );
}

معالجة البيئات

لإجبار Dusk على استخدام ملف البيئة الخاص به عند تشغيل الاختبارات، أنشئ الملف env.dusk.{environment}‎. في جذر مشروعك. على سبيل المثال، إذا كنت بصدد تشغيل الأمر dusk من البيئة local، يجب أن تنشئ ملف env.dusk.local.

عند تشغيل الاختبارات، سينسخ Dusk الملف env. احتياطيًّا وإعادة تسمية ملف بيئة Dusk إلى env.، ثم بعد الانتهاء من الاختبارات، سيعاد الملف env. الأصلي.

إنشاء المتصفّحات

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

<?php

namespace Tests\Browser;

use App\User;
use Tests\DuskTestCase;
use Laravel\Dusk\Chrome;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends DuskTestCase
{
   use DatabaseMigrations;

   /**
    * مثال عن اختبار متصفح بسيط.
    *
    * @return void
    */
   public function testBasicExample()
   {
       $user = factory(User::class)->create([
           'email' => 'taylor@laravel.com',
       ]);

       $this->browse(function ($browser) use ($user) {
           $browser->visit('/login')
                   ->type('email', $user->email)
                   ->type('password', 'secret')
                   ->press('Login')
                   ->assertPathIs('/home');
       });
   }
}

كما هو موضّح في المثال أعلاه، يقبل التابع browse ردّ نداء. سيمرّر Dusk كائنًا من المتصفّح تلقائيًّا إلى ردّ النداء، الذي سيلعب دور الكائن الرئيسي للتعامل مع تطبيقك والتأكد من عمله بشكل صحيح.

ملاحظة: سيتأكد هذا الاختبار من عمل واجهة تسجيل الدخول المولّدة من أمر artisan make:Auth.

إنشاء عدّة مستعرضات

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

$this->browse(function ($first, $second) {
   $first->loginAs(User::find(1))
         ->visit('/home')
         ->waitForText('Message');

   $second->loginAs(User::find(2))
          ->visit('/home')
          ->waitForText('Message')
          ->type('message', 'Hey Taylor')
          ->press('Send');

   $first->waitForText('Hey Taylor')
         ->assertSee('Jeffrey Way');
});

تغيير حجم نوافذ المتصفّحات

يمكنك استخدام التابع resize لتغيير حجم نافذة المتصفّح:

$browser->resize(1920, 1080);

يمكنك أيضًا استخدام التابع maximize لتكبير حجم نافذة المتصفّح إلى أكبر حجم ممكن:

$browser->maximize();

الاستيثاق

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

$this->browse(function ($first, $second) {
   $first->loginAs(User::find(1))
         ->visit('/home');
});

ملاحظة: بعد استخدام التابع loginAs، ستحفظ جلسة المستخدم لكل الاختبارات ضمن الملف ذاته.

تهجيرات قاعدة البيانات

في حال تطلّبت اختباراتك تهجيرات لقاعدة البيانات، كما في مثال الاستيثاق الموضّح أعلاه، يجب ألّا تستخدم السمة RefreshDatabase. إذ تقوم هذه السمة باستخدام عمليات قاعدة البيانات، والتي لا تعمل في طلبات الـ HTTP العادية. عوضًا عن تلك السمة، استخدم السمة DatabaseMigrations:

<?php

namespace Tests\Browser;

use App\User;
use Tests\DuskTestCase;
use Laravel\Dusk\Chrome;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;
}

التفاعل مع العناصر

محدد Dusk

إن أحد أصعب أجزاء كتابة اختبارات Dusk هو اختيار محددات CSS جيدة للتفاعل مع العناصر. بمرور الوقت، يمكن لتغييرات الواجهة أن تجعل محددات CSS -كالّتي موضّحة أدناه- غير قابلة للعمل:

// HTML...

<button>Login</button>

// اختبار...

$browser->click('.login-page .container div > button');

تمكّنك محددات Dusk من التركيز على كتابة اختبارات فعّالة بدلًا من تذكّر محددات CSS. للتعريف بمحدد، أضف الخاصية dusk إلى عنصر HTML المراد التفاعل معه. بعد ذلك، اكتب @ قبل المحدد للتفاعل مع العنصر المرتبط بذلك المحدد:

// HTML...

<button dusk="login-button">Login</button>

// اختبار...

$browser->click('@login-button');

النقر على الروابط

للنقر على رابط ما، يمكنك استخدام التابع clickLink على نسخة المتصفّح. سينقر هذا التابع على الرابط الذي يحتوي على النص المعطى:

$browser->clickLink($linkText);

ملاحظة: يتعامل هذا التابع مع jQuery. في حال كانت jQuery غير متاحة على الصفحة، سيحملها Dusk تلقائيًّا على الصفحة حتى تكون متاحةً في جلسة الاختبار.

النصوص والقيم والخصائص

استرجاع وإعداد القيم

يزوّدك Dusk بمجموعة من التوابع للتعامل مع نصوص وقيم وخصائص عناصر الصفحة. مثلًا، للحصول على قيمة عنصر موافق لمحدد معيّن، استخدم التابع value:

// قراءة القيمة...

$value = $browser->value('selector');


// تعيين القيمة...

$browser->value('selector', 'value');

استرجاع النصوص

يمكن استخدام التابع text لاسترجاع نص العرض الخاص بالعنصر الموافق للمحدد المعطى:

$text = $browser->text('selector');

استرجاع الخاصيات

يمكن استخدام التابع attribute لاسترجاع خاصية خاصة بالعنصر الموافق للمحدد المعطى:

$attribute = $browser->attribute('selector', 'value');

استخدام النماذج Forms

إدخال القيم

يزوّدك Dusk بمجموعة من التوابع للتعامل مع النماذج وعناصر input. لنطلع أوّلًا على مثال يقوم بإدخال نص داخل عنصر input:

$browser->type('email', 'taylor@laravel.com');

لاحظ أنّه رغم أنّ التابع يقبل محدّدًا، لسنا مضطرين لتمريره. في حال لم يُمرّر محدد للتابع، سيبحث Dusk عن العنصر الذي يحتوي على الخاصية name الموافقة للقيمة المعطاة. لإضافة نص لعنصر دون مسح محتواه كاملًا، يمكنك استخدام التابع append:

$browser->type('tags', 'foo')
        ->append('tags', ', bar, baz');

يمكنك مسح محتوى عنصر معيّن باستخدام التابع clear:

$browser->clear('email');

القوائم المنسدلة Dropdowns

لاختيار قيمة ما من قائمة منسدلة، يمكنك استخدام التابع select. كما هو الحال في التابع type، ليس من الضرورة أن نمرر محددًا للتابع select. عند تمرير قيمة للتابع select، يجب أن تقوم بتمرير القيمة الموجودة في الخاصية value الموافقة للخيار option المراد تحديده:

$browser->select('size', 'Large');

يمكنك اختيار قيمة عشوائية وذلك بعدم تمرير المعامل الثاني للتابع:

$browser->select('size');

صناديق التأشير Checkboxes

لاختيار صندوق تأشير معين، يمكنك استخدام التابع check. كما هو الحال سابقًا، ليس ضروريًّا أن تمرر محدد كامل للتابع. إذا لم يتم العثور على العنصر الموافق للمحدد المعطى، يقوم Dusk بالبحث عن صندوق تأشير موافق للخاصية name.

$browser->check('terms');

$browser->uncheck('terms');

أزرار الانتقاء Radio Buttons

لاختيار زر انتقاء معين، يمكنك استخدام التابع radio. أيضًا، كما هو الحال سابقًا، يمكن عدم تمرير محدد كامل للتابع:

$browser->radio('version', 'php7');

إرفاق الملفّات

يمكن استخدام التابع attach لإرفاق ملفّات لعناصر file input. وأيضًا ليس من الضروري تمرير محدد كامل للتابع:

$browser->attach('photo', __DIR__.'/photos/me.png');

تنبيه: إن التابع attach يتطلب وجود وتشغيل الإضافة Zip على إصدار PHP المفعّل لديك.

استخدام لوحة المفاتيح

يتيح التابع keys تزويد سلاسل إدخال ذات تعقيد أكبر من تلك المتاحة من التابع type. مثلًا، يمكنك النقر مطوّلًا على أزرار التعديل أثناء كتابتك للقيم. في هذا المثال، سيتم النقر مطوّلًا على الزر shift أثناء إدخال النص taylor في العنصر الموافق للمحدد. بعد كتابة taylor، سيتم كتابة otwell دون أي أزرار تعديل.

$browser->keys('selector', ['{shift}', 'taylor'], 'otwell');

يمكنك إرسال اختصار "hot key" لمحدد الـ CSS الأساسي الذي يحتوي على تطبيقك:

$browser->keys('.app', ['{command}', 'j']);

ملاحظة: كل أزرار التعديل مطوّقة بالأقواس المجعّدة {}، وموافقة للثوابت المحددة في الصنف Facebook\WebDriver\WebDriverKeys، الموجود على GitHub.

استخدام الفأرة

النقر على العناصر

يمكن استخدام التابع click للنقر على العنصر الموافق للمحدد المعطى:

$browser->click('.selector');

الإشارة على الكائنات

يمكن استخدام التابع mouseover عندما تريد أن تشير بالفأرة فوق العنصر الموافق للمحدد المعطى:

$browser->mouseover('.selector');

السحب والإفلات

يمكن استخدام التابع drag لسحب عنصر موافق لمحدد معطى إلى كائن آخر:

$browser->drag('.from-selector', '.to-selector');

أو يمكنك سحب عنصر باتجاه محدد:

$browser->dragLeft('.selector', 10);

$browser->dragRight('.selector', 10);

$browser->dragUp('.selector', 10);

$browser->dragDown('.selector', 10);

محدّدات التجميع

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

$browser->with('.table', function ($table) {
   $table->assertSee('Hello World')
         ->clickLink('Delete');
});

انتظار العناصر

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

الانتظار

إذا احتجت إلى إيقاف اختبار ما بشكل مؤقت لعدد معيّن من الميلي ثواني، استخدم التابع pause:

$browser->pause(1000);

انتظار المحدّدات

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

// الانتظار لمدة أقصاها 5 ثواني للمحدد المعطى
$browser->waitFor('.selector');

// الانتظار لمدة أقصاها ثانية للمحدد المعطى
$browser->waitFor('.selector', 1);

يمكنك أيضًا الانتظار ريثما يختفي من الصفحة العنصر الموافق للمحدد المعطى، كالتالي:

$browser->waitUntilMissing('.selector');

$browser->waitUntilMissing('.selector', 1);

تجميع المحددات عند إتاحتها

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

$browser->whenAvailable('.modal', function ($modal) {
   $modal->assertSee('Hello World')
         ->press('OK');
});

انتظار النصوص

يمكن استخدام التابع waitForText للانتظار ريثما يظهر النص المعطى على الصفحة:

// الانتظار لمدة أقصاها 5 ثواني للنص المعطى
$browser->waitForText('Hello World');

// الانتظار لمدة أقصاها ثانية للنص المعطى
$browser->waitForText('Hello World', 1);

انتظار الروابط

يمكن استخدام التابع waitForLink للانتظار ريثما يظهر الرابط المعطى بالنص على الصفحة:

// الانتظار لمدة أقصاها 5 ثواني للرابط المعطى
$browser->waitForLink('Create');

// الانتظار لمدة أقصاها ثانية للرابط المعطى
$browser->waitForLink('Create', 1);

انتظار فتح مسار الصفحة

عند القيام بإثباتات مسار الصفحة كالإثبات assertPathIs، يمكن أن يفشل هذا الإثبات في حال كان المتحول window.location.pathname يحدث بشكل غير متزامن. يمكنك استخدام التابع waitForLocation للانتظار ريثما يصبح المسار الحالي للصفحة موافقًا للقيمة المعطاة:

$browser->waitForLocation('/secret');

يمكنك أيضًا تمرير مسار مسمّى للتابع:

$browser->waitForRoute($routeName, $parameters);

انتظار إعادة تحميل الصفحة

في حال كنت بحاجة إلى القيام بإثباتات بعد إعادة تحميل الصفحة، يمكنك استخدام التابع waitForReload:

$browser->click('.some-action')
        ->waitForReload()
        ->assertSee('something');

انتظار تعابير JavaScript

قد تحتاج في بعض الأحيان إلى إيقاف تنفيذ اختبار ما حتى يصبح تعبير JavaScript معيّن صحيحًا (true). يمكنك تحقيق ذلك بسهولة باستخدام التابع waitUntil. عند تمرير تعبير منطقي لذلك التابع، لست بحاجة لتضمين الكلمة return أو الفاصلة المنقوطة في النهاية:

// انتظار 5 ثواني كحد أعظمي ليصبح التعبير المعطى صحيحاً..
$browser->waitUntil('App.dataLoaded');
$browser->waitUntil('App.data.servers.length > 0');

// انتظار ثانية كحد أعظمي ليصبح التعبير المعطى صحيحاً..
$browser->waitUntil('App.data.servers.length > 0', 1);

الانتظار مع ردّ النداء Callback

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

$browser->waitUsing(10, 1, function () use ($something) {
   return $something->isReady();
}, "Something wasn't ready in time.");

القيام بإثباتات Vue

تمكّنك Dusk أيضًا من القيام بإثباتات حول حالة مكوّنات Vue وبياناتها. على سبيل المثال، قد يحوي تطبيقك مكوّن Vue التالي:

// HTML...
<profile dusk="profile-component"></profile>

// تعريف المكون..
Vue.component('profile', {
   template: '<div>{{ user.name }}</div>',
   data: function () {
       return 
           user: {
             name: 'Taylor'
           }
       };
   }
});

يمكنك إثبات حالة المكوّن كالتالي:

/**
* مثال بسيط عن اختبار Vue..
*
* @return void
*/
public function testVue()
{
   $this->browse(function (Browser $browser) {
       $browser->visit('/')
               ->assertVue('user.name', 'Taylor', '@profile-component');
   });
}

الإثباتات المتاحة

تزوّدك Dusk بمجموعة من الإثباتات التي يمكن استعمالها في تطبيقك. كل الإثباتات المتاحة موجودة في القائمة أدناه:

assertTitle

التأكد من أن عنوان الصفحة يوافق القيمة المعطاة:

$browser->assertTitle($title);

assertTitleContains التأكد من أن عنوان الصفحة يحتوي على القيمة المعطاة:

$browser->assertTitleContains($title);

assertUrlIs التأكد من أن الرابط الحالي (بدون سلسلة الاستعلام) يوافق القيمة المعطاة:

$browser->assertUrlIs($url);

assertPathBeginsWith التأكد من أن المسار الحالي يبدأ بالمسار المعطى:

$browser->assertPathBeginsWith($path);

assertPathIs التأكد من أن المسار الحالي يوافق المسار المعطى:

$browser->assertPathIs('/home');

assertPathIsNot التأكد من أن المسار الحالي لا يوافق المسار المعطى:

$browser->assertPathIsNot('/home');

assertRouteIs التأكد من أن الرابط الحالي يوافق الرابط الموافق للوجهة المسمّاة المعطاة:

$browser->assertRouteIs($name, $parameters);

assertQueryStringHas التأكد من أن معامل سلسلة الاستعلام المعطى موجود:

$browser->assertQueryStringHas($name);

التأكد من أن معامل سلسلة الاستعلام المعطى موجود ويحوي القيمة المعطاة:

$browser->assertQueryStringHas($name, $value);

assertQueryStringMissing التأكد من أن معامل سلسلة الاستعلام المعطى غير موجود:

$browser->assertQueryStringMissing($name);

assertFragmentIs التأكد من أن القطعة الحالية توافق القطعة المعطاة:

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith التأكد من أن القطعة الحالية تبدأ بالقطعة المعطاة:

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot التأكد من أن القطعة الحالية لا توافق القطعة المعطاة:

$browser->assertFragmentIsNot('anchor');

assertHasCookie التأكد من أن ملف تعريف الارتباط cookie المعطى موجود:

$browser->assertHasCookie($name);

assertCookieMissing التأكد من أن ملف تعريف الارتباط المعطى غير موجود:

$browser->assertCookieMissing($name);

assertCookieValue التأكد من أن ملف تعريف الارتباط المعطى يحتوي على القيمة المعطاة:

$browser->assertCookieValue($name, $value);

assertPlainCookieValue التأكد من أن ملف تعريف الارتباط غير المشرف المعطى يحتوي على القيمة المعطاة:

$browser->assertPlainCookieValue($name, $value);

assertSee التأكد من أن النص المعطى موجود على الصفحة:

$browser->assertSee($text);

assertDontSee التأكد من أن النص المعطى غير موجود على الصفحة:

$browser->assertDontSee($text);

assertSeeIn التأكد من أن النص المعطى موجود ضمن المحدد المعطى:

$browser->assertSeeIn($selector, $text);

assertDontSeeIn التأكد من أن النص المعطى غير موجود ضمن المحدد المعطى:

$browser->assertDontSeeIn($selector, $text);

assertSourceHas التأكد من أن المصدر المعطى موجود ضمن الملف المصدري للصفحة:

$browser->assertSourceHas($code);

assertSourceMissing التأكد من أن المصدر المعطى غير موجود ضمن الملف المصدري للصفحة:

$browser->assertSourceMissing($code);

assertSeeLink التأكد من أن الرابط المعطى موجود على الصفحة:

$browser->assertSeeLink($linkText);

assertDontSeeLink التأكد من أن الرابط المعطى غير موجود على الصفحة:

$browser->assertDontSeeLink($linkText);

assertInputValue التأكد من أن حقل الإدخال المعطى يحتوي على القيمة المعطاة:

$browser->assertInputValue($field, $value);

assertInputValueIsNot التأكد من أن حقل الإدخال المعطى لا يحتوي على القيمة المعطاة:

$browser->assertInputValueIsNot($field, $value);

assertChecked التأكد من أن اختيار مربع الاختيار المعطى:

$browser->assertChecked($field);

assertNotChecked التأكد من أن عدم اختيار مربع الاختيار المعطى:

$browser->assertNotChecked($field);

assertRadioSelected التأكد من أن زر الانتقاء المعطى بالقيمة محدد:

$browser->assertRadioSelected($field, $value);

assertRadioNotSelected التأكد من أن زر الانتقاء المعطى بالقيمة غير محدد:

$browser->assertRadioNotSelected($field, $value);

assertSelected التأكد من اختيار القيمة المعطاة ضمن قائمة المنسدلة المعطاة بالحقل:

$browser->assertSelected($field, $value);

assertNotSelected التأكد من عدم اختيار القيمة المعطاة ضمن القائمة المنسدلة المعطاة بالحقل:

$browser->assertNotSelected($field, $value);

assertSelectHasOptions التأكد من أن مصفوفة القيم المعطاة موجودة ضمن القائمة المنسدلة المعطاة:

$browser->assertSelectHasOptions($field, $values);

assertSelectMissingOptions التأكد من أن مصفوفة القيم المعطاة غير موجودة ضمن القائمة المنسدلة المعطاة:

$browser->assertSelectMissingOptions($field, $values);

assertSelectHasOption التأكد من أن القيمة المعطاة موجودة ضمن القائمة المنسدلة المعطاة:

$browser->assertSelectHasOption($field, $value);

assertValue التأكد من أن الكائن المعطى بالمحدد يحتوي على القيمة المعطاة:

$browser->assertValue($selector, $value);

assertVisible التأكد من ظهور الكائن المعطى بالمحدد:

$browser->assertVisible($selector);

assertPresent التأكد من وجود الكائن المعطى بالمحدد:

$browser->assertPresent($selector);

assertMissing التأكد من عدم وجود الكائن المعطى بالمحدد:

$browser->assertMissing($selector);

assertDialogOpened التأكد من أن مربع حوار الجافاسكربت المحدد بالرسالة المعطاة تمّ فتحه:

$browser->assertDialogOpened($message);

assertEnabled التأكد من تفعيل الحقل المعطى:

$browser->assertEnabled($field);

assertDisabled التأكد من إلغاء تفعيل الحقل المعطى:

$browser->assertDisabled($field);

assertFocused التأكد من أن الحقل المعطى مركّز عليه حالياً:

$browser->assertFocused($field);

assertNotFocused التأكد من أن الحقل المعطى غير مركّز عليه حالياً:

$browser->assertNotFocused($field);

assertVue التأكد من أن خاصية مكوّن الـ Vue المعطاة توافق القيمة المعطاة:

$browser->assertVue($property, $value, $componentSelector = null);

assertVueIsNot التأكد من أن خاصية مكوّن الـ Vue المعطاة لا توافق القيمة المعطاة:

$browser->assertVueIsNot($property, $value, $componentSelector = null);

assertVueContains التأكد من أن خاصية مكوّن الـ Vue المعطاة هي مصفوفة وتحتوي على القيمة المعطاة:

$browser->assertVueContains($property, $value, $componentSelector = null);

assertVueDoesNotContain التأكد من أن خاصية مكوّن الـ Vue المعطاة هي مصفوفة ولا تحتوي على القيمة المعطاة:

$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);

الصفحات

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

توليد الصفحات

لتوليد كائن صفحة، استخدم أمر artisan dusk:page. جميع كائنات الصفحات متوضّعة في المجلّد tests/Browser/Pages:

php artisan dusk:page Login

ضبط الصفحات

تحتوي الصفحات بشكل افتراضي على ثلاث توابع: url و asset و elements. سنناقش التابعين url و assert الآن، أما التابع elements فسنناقشه بتفصيل أكبر أدناه.

التابع url

يعيد التابع url مسار الرابط الذي يمثل الصفحة. يستخدم Dusk هذا الرابط عند التنقل للصفحة في المتصفح:

/**
* قراءة رابط الصفحة.
*
* @return string
*/

public function url()
{
   return '/login';
}

التابع assert

يقوم التابع assert بأي إثبات ضروري للتأكد من أن المتصفح موجود على الصفحة المعطاة حاليًا. ليس من الضروري استكمال هذا التابع؛ لكن يمكنك القيام بهذه التأكيدات يدويًا إذا أردت. إذ ستُشغَّل هذه التأكيدات تلقائيًا عند التنقل للصفحة.

/**
 * التأكد أن الصفحة في الرابط المحدد.
 *
 * @return void
 */

public function assert(Browser $browser)
{
    $browser->assertPathIs($this->url());
}

التنقل للصفحات

بعد ضبط الصفحة، يمكنك الانتقال إليها باستخدام التابع visit:

use Tests\Browser\Pages\Login;

$browser->visit(new Login);

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

use Tests\Browser\Pages\CreatePlaylist;

$browser->visit('/dashboard')
       ->clickLink('Create Playlist')
       ->on(new CreatePlaylist)
       ->assertSee('@create');

المحددات المختصرة

يسمح لك التابع elements الخاص بالصفحات بتعريف اختصارات سريعة وسهلة التذكر لأي محدد CSS على صفحتك. على سبيل المثال، لنعرف اختصارا لحقل إدخال الـ "email" الموجود في صفحة تسجيل الدخول الخاصة بالتطبيق:

/**
* قراءة الاسم المختصر للمحدد.
*
* @return array
*/

public function elements()
{
   return [
       '@email' => 'input[name=email]',
   ];
}

يمكنك الآن استخدام هذا المحدد المختصر عند الحاجة لاستخدام المحدد الكامل:

$browser->type('@email', 'taylor@laravel.com');

المحددات المختصرة العامة

بعد تنصيب Dusk، سيوضع صنف Page أساسي ضمن مجلد الـ tests/Browser/Pages. يحوي هذا الصنف على التابع المسمى siteElements الذي يستخدم لتعريف المحددات المختصرة العامة التي يجب أن تتوفر في جميع الصفحات في تطبيقك.

/**
* قراءة المحدد المختصر العام.
*
* @return array
*/

public static function siteElements()
{
   return [
       '@element' => '#selector',
   ];
}

توابع الصفحة

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

<?php

namespace Tests\Browser\Pages;

use Laravel\Dusk\Browser;

class Dashboard extends Page
{
   // توابع أخرى..

   /**
    * إنشاء قائمة تشغيل جديدة.
    *
    * @param  \Laravel\Dusk\Browser  $browser
    * @param  string $name
    * @return void
    */
   public function createPlaylist(Browser $browser, $name)
   {
       $browser->type('name', $name)
               ->check('share')
               ->press('Create Playlist');
   }
}

بعد التعريف عن التابع، يمكنك استخدامه ضمن أي اختبار يستخدم الصفحة. إذ سيمرر كائن المتصفح تلقائيًّا لتابع الصفحة:

use Tests\Browser\Pages\Dashboard;

$browser->visit(new Dashboard)
       ->createPlaylist('My Playlist')
       ->assertSee('My Playlist');

المكوّنات

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

توليد المكوّنات

لتوليد مكوّن، استخدم أمر artisan dusk:component. تتوضّع المكوّنات الجديدة في مجلّد الـ tests/Browser/Components:

php artisan dusk:component DatePicker

كما هو موضّح أعلاه، الـ "date picker" أو محدد التاريخ هو مثال عن المكوّنات التي من الممكن أن تكون محتواة ضمن تطبيقك في مجموعة من الصفحات، إذ من الممكن أن يصبح صعبًا عليك أن تعيد كتابة منطق أتمتة المتصفح لتحديد تاريخ على مجموعة كبيرة من الصفحات ضمن اختباراتك. بدلًا من ذلك، يمكننا تعريف مكوّن Dusk لتمثيل محدد التاريخ date picker، مما يجعلنا قابلين على تغليف المنطق البرمجي ضمن المكوِّن:

<?php

namespace Tests\Browser\Components;

use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;

class DatePicker extends BaseComponent
{
   /**
    * قراءة المحدد الجذر للمكون.
    *
    * @return string
    */
   public function selector()
   {
       return '.date-picker';
   }

   /**
    * التأكد أن الصفحة تحتوي على المكون.
    *
    * @param  Browser $browser
    * @return void
    */
   public function assert(Browser $browser)
   {
       $browser->assertVisible($this->selector())
   }

   /**
    * قراءة المحدد المختصر للمكون.
    *
    * @return array
    */
   public function elements()
   {
       return [
           '@date-field' => 'input.datepicker-input',
           '@month-list' => 'div > div.datepicker-months',
           '@day-list' => 'div > div.datepicker-days',
       ];
   }

   /**
    * اختيار التاريخ المعطى.
    *
    * @param  \Laravel\Dusk\Browser  $browser
    * @param  int $month
    * @param  int $year
    * @return void
    */
   public function selectDate($browser, $month, $year)
   {
       $browser->click('@date-field')
               ->within('@month-list', function ($browser) use ($month) {
                   $browser->click($month);
               })
               ->within('@day-list', function ($browser) use ($day) {
                   $browser->click($day);
               });
   }
}

استخدام المكوّنات

بعد التعريف عن المكوّن، يمكننا ببساطة تحديد تاريخ ضمن محدد التاريخ من أي اختبار. وفي حال طرأ تغيير على منطق تحديد التاريخ، فإنّنا بحاجة فقط لتعديل المكوّن:

<?php

namespace Tests\Browser;

use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends DuskTestCase
{
   /**
    * مثال اختبار بسيط.
    *
    * @return void
    */
   public function testBasicExample()
   {
       $this->browse(function (Browser $browser) {
           $browser->visit('/')
                   ->within(new DatePicker, function ($browser) {
                       $browser->selectDate(1, 2018);
                   })
                   ->assertSee('January');
       });
   }
}

التكامل المتواصل Continuous Integration

CircleCI

CircleCI 1.0

في حال استخدامك لـ CircleCI 1.0 لتشغيل اختبارات Dusk الخاصة بك، يمكنك استخدام ملف الضبط التالي كنقطة البداية. كما هو الحال في TravisCI، سنقوم باستخدام الأمر php artisan serve لتشغيل المخدم الداخلي PHP:

dependencies:
  pre:
      - curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
      - sudo dpkg -i google-chrome.deb
      - sudo sed -i 's|HERE/chrome\"|HERE/chrome\" --disable-setuid-sandbox|g' /opt/google/chrome/google-chrome
      - rm google-chrome.deb

test:
    pre:
        - "./vendor/laravel/dusk/bin/chromedriver-linux":
            background: true
        - cp .env.testing .env
        - "php artisan serve":
            background: true

    override:
        - php artisan dusk

CircleCI 2.0

في حال استخدامك لـ CircleCI 2.0 لتشغيل اختبارات Dusk، يمكنك إضافة الخطوات التالية لملف البناء:

version: 2
 jobs:
     build:
         steps:
            - run: sudo apt-get install -y libsqlite3-dev
            - run: cp .env.testing .env
            - run: composer install -n --ignore-platform-reqs
            - run: npm install
            - run: npm run production
            - run: vendor/bin/phpunit

            - run:
               name: Start Chrome Driver
               command: ./vendor/laravel/dusk/bin/chromedriver-linux
               background: true

            - run:
               name: Run Laravel Server
               command: php artisan serve
               background: true

            - run:
               name: Run Laravel Dusk Tests
               command: php artisan dusk

Codeship

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

phpenv local 7.1
cp .env.testing .env
composer install --no-interaction
nohup bash -c "./vendor/laravel/dusk/bin/chromedriver-linux 2>&1 &"
nohup bash -c "php artisan serve 2>&1 &" && sleep 5
php artisan dusk

Heroku CI

لتشغيل اختبارات Dusk على Heroku CI، أضف حزم بناء Google Chrome والمصادر التالية لملف app.json الخاص بـ Heroku:

{
  "environments": {
    "test": {
      "buildpacks": [
        { "url": "heroku/php" },
        { "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
      ],
      "scripts": {
        "test-setup": "cp .env.testing .env",
        "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve > /dev/null 2>&1 &' && php artisan dusk"
      }
    }
  }
}

Travis CI

لتشغيل اختبارات Dusk على Travis CI، سنحتاج إلى استخدام توزيعة Ubuntu 14.04 (Trusty)‎ التي تملك فيها صلاحيات الجذر عبر الأمر sudo. بسبب عدم كون Travis CI بيئة رسومية، سنحتاج إلى القيام ببعض الخطوات الإضافية لتشغيل متصفح Chrome. علاوةً على ذلك، سنستخدم php artisan serve لتشغيل مخدّم PHP الداخلي:

sudo: required
dist: trusty

addons:
   chrome: stable

install:
   - cp .env.testing .env
   - travis_retry composer install --no-interaction --prefer-dist --no-suggest

before_script:
   - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
   - php artisan serve &

script:
   - php artisan dusk

مصادر