اختبارات المتصفح (Laravel Dusk) في Laravel
مقدمة
يوفّر لك 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
و assert
و 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