1. Тестирование
  2. Laravel Dusk

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

Laravel Dusk предоставляет выразительный и простой в использовании API для автоматизации браузера и тестирования. По умолчанию Dusk не требует установки JDK или Selenium на вашем локальном компьютере. Вместо этого Dusk использует отдельную установку ChromeDriver. Однако вы свободны использовать любой другой совместимый с Selenium драйвер, который вы предпочтете.

Установка

Для начала вам следует установить Google Chrome и добавить зависимость Composer laravel/dusk в ваш проект:

composer require --dev laravel/dusk

Внимание Если вы вручную регистрируете службу Dusk, вы никогда не должны регистрировать ее в производственной среде, поскольку это может привести к возможности аутентификации произвольных пользователей в вашем приложении.

После установки пакета Dusk выполните команду Artisan dusk:install. Команда dusk:install создаст каталог tests/Browser, пример теста Dusk и установит двоичный файл Chrome Driver для вашей операционной системы:

php artisan dusk:install

Затем установите переменную окружения APP_URL в файле .env вашего приложения. Это значение должно соответствовать URL-адресу, который вы используете для доступа к вашему приложению в браузере.

Примечание Если вы используете Laravel Sail для управления локальной средой разработки, обратитесь также к документации Sail по настройке и запуску тестов Dusk.

Управление установками ChromeDriver

Если вы хотите установить другую версию ChromeDriver, чем устанавливается Laravel Dusk с помощью команды dusk:install, вы можете использовать команду dusk:chrome-driver:

# Install the latest version of ChromeDriver for your OS...
php artisan dusk:chrome-driver
 
# Install a given version of ChromeDriver for your OS...
php artisan dusk:chrome-driver 86
 
# Install a given version of ChromeDriver for all supported OSs...
php artisan dusk:chrome-driver --all
 
# Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...
php artisan dusk:chrome-driver --detect

Внимание Для работы Dusk требуется, чтобы бинарные файлы chromedriver были исполняемыми. Если у вас возникли проблемы с запуском Dusk, убедитесь, что бинарные файлы исполняемы с помощью следующей команды: chmod -R 0755 vendor/laravel/dusk/bin/.

Использование других браузеров

По умолчанию Dusk использует Google Chrome и автономную установку ChromeDriver, чтобы запустить тесты браузера. Однако вы можете запустить свой собственный сервер Selenium и запускать тесты с любым браузером, который вы хотите.

Для начала откройте файл tests/DuskTestCase.php, который является базовым тестовым случаем Dusk для вашего приложения. Внутри этого файла вы можете удалить вызов метода startChromeDriver. Это предотвратит автоматический запуск ChromeDriver Dusk:

/**
* Подготовка к выполнению тестов Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}

Затем вы можете изменить метод driver, чтобы подключиться к URL и порту по вашему выбору. Кроме того, вы можете изменить "желаемые возможности" (desired capabilities), которые должны быть переданы в WebDriver:

use Facebook\WebDriver\Remote\RemoteWebDriver;
 
/**
* Создание экземпляра RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}

Начало работы

Создание тестов

Чтобы создать тест Dusk, используйте команду Artisan dusk:make. Созданный тест будет помещен в каталог tests/Browser:

php artisan dusk:make LoginTest

Сброс базы данных после каждого теста

Большинство ваших тестов будут взаимодействовать с страницами, получающими данные из базы данных вашего приложения. Однако ваши тесты Dusk никогда не должны использовать трейт RefreshDatabase. Трейт RefreshDatabase использует базовые транзакции, которые не будут применяться или доступны через HTTP-запросы. Вместо этого у вас есть два варианта: трейт DatabaseMigrations и трейт DatabaseTruncation.

Использование миграций базы данных

Трейт DatabaseMigrations запускает миграции базы данных перед каждым тестом. Однако удаление и повторное создание таблиц базы данных для каждого теста обычно медленнее, чем обрезка таблиц:

<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
}

Внимание В тестах Dusk не могут использоваться базы данных SQLite в памяти. Поскольку браузер выполняется в своем собственном процессе, он не сможет получить доступ к базам данных в памяти других процессов.

Использование обрезки базы данных

Перед использованием трейта DatabaseTruncation вы должны установить пакет doctrine/dbal с помощью менеджера пакетов Composer:

composer require --dev doctrine/dbal

Трейт DatabaseTruncation будет мигрировать вашу базу данных на первом тесте, чтобы убедиться, что таблицы вашей базы данных были правильно созданы. Однако на последующих тестах таблицы базы данных будут просто обрезаны, что обеспечит ускорение по сравнению с повторным выполнением всех миграций базы данных:

<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
use DatabaseTruncation;
}

По умолчанию этот трейт будет обрезать все таблицы, кроме таблицы migrations. Если вы хотите настроить таблицы, которые должны быть обрезаны, вы можете определить свойство $tablesToTruncate в вашем классе теста:

/**
* Указывает, какие таблицы следует обрезать.
*
* @var array
*/
protected $tablesToTruncate = ['users'];

Кроме того, вы можете определить свойство $exceptTables в вашем классе теста для указания таблиц, которые должны быть исключены из обрезки:

/**
* Указывает, какие таблицы следует исключить из обрезки.
*
* @var array
*/
protected $exceptTables = ['users'];

Чтобы указать соединения с базой данных, таблицы которых следует обрезать, вы можете определить свойство $connectionsToTruncate в вашем классе теста:

/**
* Указывает, какие соединения должны иметь свои таблицы обрезаны.
*
* @var array
*/
protected $connectionsToTruncate = ['mysql'];

Если вы хотите выполнить код до или после обрезки базы данных, вы можете определить методы beforeTruncatingDatabase или afterTruncatingDatabase в вашем классе теста:

/**
* Выполняет любую работу, которая должна произойти до начала обрезки базы данных.
*/
protected function beforeTruncatingDatabase(): void
{
//
}
 
/**
* Выполняет любую работу, которая должна произойти после завершения обрезки базы данных.
*/
protected function afterTruncatingDatabase(): void
{
//
}

Запуск тестов

Чтобы запустить ваши тесты браузера, выполните команду Artisan dusk:

php artisan dusk

Если у вас были сбои в тестах при последнем запуске команды dusk, вы можете сэкономить время, запустив сначала тесты, завершившиеся неудачей, с использованием команды dusk:fails:

php artisan dusk:fails

Команда dusk принимает любой аргумент, который обычно принимает тестовый раннер PHPUnit, такой как, например, позволяя вам запускать тесты только для заданной группы:

php artisan dusk --group=foo

Примечание Если вы используете Laravel Sail для управления локальной средой разработки, обратитесь к документации Sail по настройке и запуску тестов Dusk.

Ручной запуск ChromeDriver

По умолчанию Dusk автоматически попытается запустить ChromeDriver. Если это не работает для вашей системы, вы можете вручную запустить ChromeDriver перед запуском команды dusk. Если вы решите запустить ChromeDriver вручную, вы должны закомментировать следующую строку вашего файла tests/DuskTestCase.php:

/**
* Подготовка к выполнению тестов Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}

Кроме того, если вы запускаете ChromeDriver на порту, отличном от 9515, вы должны изменить метод driver в том же классе, чтобы отразить правильный порт:

use Facebook\WebDriver\Remote\RemoteWebDriver;
 
/**
* Создание экземпляра RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}

Обработка окружения

Чтобы заставить Dusk использовать свой собственный файл окружения при запуске тестов, создайте файл .env.dusk.{environment} в корне вашего проекта. Например, если вы собираетесь запускать команду dusk из вашего окружения local, вы должны создать файл .env.dusk.local.

При запуске тестов Dusk будет создана резервная копия вашего файла .env, и окружение Dusk будет переименовано в .env. После завершения тестов ваш файл .env будет восстановлен.

Основы работы с браузером

Создание браузеров

Для начала давайте напишем тест, который проверит, что мы можем войти в наше приложение. После создания теста мы можем изменить его для перехода на страницу входа, ввода учетных данных и нажатия кнопки "Вход". Для создания экземпляра браузера вы можете вызвать метод browse из вашего теста Dusk:

<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
 
/**
* Простой пример теста браузера.
*/
public function test_basic_example(): void
{
$user = User::factory()->create([
'email' => '[email protected]',
]);
 
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}

Как видно в приведенном выше примере, метод browse принимает замыкание. Экземпляр браузера будет автоматически передан в это замыкание Dusk и является основным объектом для взаимодействия и проверки утверждений в вашем приложении.

Создание нескольких браузеров

Иногда вам может потребоваться несколько браузеров для правильного выполнения теста. Например, для тестирования экрана чата, который взаимодействует с веб-сокетами, может потребоваться несколько браузеров. Чтобы создать несколько браузеров, просто добавьте больше аргументов браузера в сигнатуру замыкания, переданного методу browse:

$this->browse(function (Browser $first, Browser $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');
});

Навигация

Метод visit может использоваться для перехода к указанному URI внутри вашего приложения:

$browser->visit('/login');

Вы можете использовать метод visitRoute для перехода к именованному маршруту:

$browser->visitRoute('login');

Вы можете перемещаться "назад" и "вперед" с использованием методов back и forward:

$browser->back();
 
$browser->forward();

Вы можете использовать метод refresh для обновления страницы:

$browser->refresh();

Изменение размеров окон браузера

Метод resize может использоваться для изменения размера окна браузера:

$browser->resize(1920, 1080);

Метод maximize может использоваться для максимизации окна браузера:

$browser->maximize();

Метод fitContent изменит размер окна браузера, чтобы соответствовать размеру его содержимого:

$browser->fitContent();

Когда тест завершится неудачей, Dusk автоматически изменит размер браузера, чтобы соответствовать содержимому перед созданием снимка. Вы можете отключить эту функцию, вызвав метод disableFitOnFailure в вашем тесте:

$browser->disableFitOnFailure();

Метод move может использоваться для перемещения окна браузера в другое положение на вашем экране:

$browser->move($x = 100, $y = 100);

Макросы браузера

Если вы хотите определить пользовательский метод браузера, который вы можете использовать в различных ваших тестах, вы можете использовать метод macro в классе Browser. Обычно этот метод следует вызывать из метода boot поставщика услуг:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
 
class DuskServiceProvider extends ServiceProvider
{
/**
* Регистрация макросов браузера Dusk.
*/
public function boot(): void
{
Browser::macro('scrollToElement', function (string $element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
 
return $this;
});
}
}

Функция macro принимает имя в качестве первого аргумента и замыкание в качестве второго. Замыкание макроса будет выполнено при вызове макроса в виде метода на экземпляре Browser:

$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});

Аутентификация

Часто приходится тестировать страницы, требующие аутентификации. Вы можете использовать метод loginAs Dusk, чтобы избежать взаимодействия с экраном входа в приложение в каждом тесте. Метод loginAs принимает первичный ключ, связанный с вашей аутентифицируемой моделью, или экземпляр аутентифицируемой модели:

use App\Models\User;
use Laravel\Dusk\Browser;
 
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});

Внимание После использования метода loginAs сеанс пользователя сохранится для всех тестов внутри файла.

Cookies

Метод cookie можно использовать для получения или установки значения зашифрованного куки. По умолчанию все куки, созданные Laravel, зашифрованы:

$browser->cookie('name');
 
$browser->cookie('name', 'Taylor');

Метод plainCookie можно использовать для получения или установки значения незашифрованного куки:

$browser->plainCookie('name');
 
$browser->plainCookie('name', 'Taylor');

Метод deleteCookie можно использовать для удаления указанного куки:

$browser->deleteCookie('name');

Выполнение JavaScript

Метод script можно использовать для выполнения произвольных операторов JavaScript в браузере:

$browser->script('document.documentElement.scrollTop = 0');
 
$browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
 
$output = $browser->script('return window.location.pathname');

Создание снимка экрана

Метод screenshot можно использовать для создания снимка экрана и сохранения его с указанным именем файла. Все снимки будут сохранены в каталоге tests/Browser/screenshots:

$browser->screenshot('filename');

Метод responsiveScreenshots можно использовать для создания серии снимков экрана при различных точках останова:

$browser->responsiveScreenshots('filename');

Сохранение вывода консоли на диск

Метод storeConsoleLog можно использовать для записи вывода консоли текущего браузера на диск с указанным именем файла. Вывод консоли будет сохранен в каталоге tests/Browser/console:

$browser->storeConsoleLog('filename');

Сохранение исходного кода страницы на диск

Метод storeSource можно использовать для записи исходного кода текущей страницы на диск с указанным именем файла. Исходный код страницы будет сохранен в каталоге tests/Browser/source:

$browser->storeSource('filename');

Взаимодействие с элементами

Селекторы Dusk

Выбор хороших CSS-селекторов для взаимодействия с элементами - одна из самых сложных частей написания тестов в Dusk. С течением времени изменения на фронтенде могут вызвать сбой ваших тестов, если использовать такие CSS-селекторы, как в следующем примере:

// HTML...
 
<button>Login</button>
 
// Тест...
 
$browser->click('.login-page .container div > button');

Селекторы Dusk позволяют вам сосредоточиться на написании эффективных тестов, а не на запоминании CSS-селекторов. Чтобы определить селектор, добавьте атрибут dusk к вашему HTML-элементу. Затем при взаимодействии с браузером Dusk добавьте префикс @ к селектору, чтобы управлять прикрепленным элементом внутри вашего теста:

// HTML...
 
<button dusk="login-button">Login</button>
 
// Тест...
 
$browser->click('@login-button');

При необходимости вы можете настроить атрибут HTML, который использует селектор Dusk, с помощью метода selectorHtmlAttribute. Обычно этот метод следует вызывать из метода boot вашего поставщика услуг приложения:

use Laravel\Dusk\Dusk;
 
Dusk::selectorHtmlAttribute('data-dusk');

Текст, значения и атрибуты

Получение и установка значений

Dusk предоставляет несколько методов для взаимодействия с текущим значением, текстом отображения и атрибутами элементов на странице. Например, чтобы получить "значение" элемента, соответствующего заданному CSS- или Dusk-селектору, используйте метод value:

// Получение значения...
$value = $browser->value('selector');
 
// Установка значения...
$browser->value('selector', 'value');

Метод inputValue можно использовать для получения "значения" элемента ввода с заданным именем поля:

$value = $browser->inputValue('field');

Получение текста

Метод text можно использовать для извлечения текста отображения элемента, соответствующего заданному селектору:

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

Получение атрибутов

Наконец, метод attribute можно использовать для извлечения значения атрибута элемента, соответствующего заданному селектору:

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

Взаимодействие с формами

Ввод значений

Dusk предоставляет разнообразные методы для взаимодействия с формами и элементами ввода. Давайте сначала рассмотрим пример ввода текста в поле ввода:

$browser->type('email', '[email protected]');

Обратите внимание, что, хотя метод принимает один, если это необходимо, мы не обязаны передавать CSS-селектор в метод type. Если CSS-селектор не предоставлен, Dusk будет искать поле input или textarea с заданным атрибутом name.

Чтобы добавить текст в поле, не очищая его содержимое, вы можете использовать метод append:

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

Вы можете очистить значение ввода с помощью метода clear:

$browser->clear('email');

Вы можете указать Dusk вводить текст медленно с использованием метода typeSlowly. По умолчанию Dusk будет делать паузу в 100 миллисекунд между нажатиями клавиш. Чтобы настроить количество времени между нажатиями клавиш, вы можете передать соответствующее количество миллисекунд в качестве третьего аргумента методу:

$browser->typeSlowly('mobile', '+1 (202) 555-5555');
 
$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);

Метод appendSlowly можно использовать для медленного добавления текста:

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

Раскрывающиеся списки

Чтобы выбрать значение, доступное в элементе select, вы можете использовать метод select. Как и метод type, метод select не требует полного CSS-селектора. Передавая значение методу select, вы должны передавать фактическое значение опции, а не отображаемый текст:

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

Вы можете выбрать случайную опцию, опустив второй аргумент:

$browser->select('size');

Предоставив массив в качестве второго аргумента методу select, вы можете указать методу выбор нескольких опций:

$browser->select('categories', ['Art', 'Music']);

Флажки

Чтобы "отметить" флажок ввода, вы можете использовать метод check. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать флажок с соответствующим атрибутом name:

$browser->check('terms');

Метод uncheck можно использовать для "снятия отметки" с флажка ввода:

$browser->uncheck('terms');

Переключатели

Чтобы "выбрать" опцию ввода radio, вы можете использовать метод radio. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать ввод radio с совпадающими атрибутами name и value:

$browser->radio('size', 'large');

Прикрепление файлов

Метод attach можно использовать для прикрепления файла к элементу ввода file. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать ввод file с совпадающим атрибутом name:

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

Внимание Функция attach требует установленного и включенного расширения PHP Zip на вашем сервере.

Нажатие кнопок

Метод press можно использовать для щелчка по кнопке на странице. Аргумент, передаваемый методу press, может быть как отображаемым текстом кнопки, так и CSS- или Dusk-селектором:

$browser->press('Login');

При отправке форм многие приложения отключают кнопку отправки формы после ее нажатия, а затем снова включают кнопку, когда HTTP-запрос отправки формы завершен. Чтобы нажать кнопку и дождаться, когда кнопка снова будет включена, вы можете использовать метод pressAndWaitFor:

// Нажатие кнопки и ожидание не более 5 секунд, чтобы она стала активной...
$browser->pressAndWaitFor('Save');
 
// Нажатие кнопки и ожидание не более 1 секунды, чтобы она стала активной...
$browser->pressAndWaitFor('Save', 1);

Клик по ссылкам

Чтобы нажать на ссылку, вы можете использовать метод clickLink экземпляра браузера. Метод clickLink будет нажимать на ссылку с заданным отображаемым текстом:

$browser->clickLink($linkText);

Метод seeLink можно использовать для определения того, видна ли на странице ссылка с заданным отображаемым текстом:

if ($browser->seeLink($linkText)) {
// ...
}

Внимание Эти методы взаимодействуют с jQuery. Если jQuery отсутствует на странице, Dusk автоматически внедрит его на страницу, чтобы оно было доступно в течение длительности теста.

Использование клавиатуры

Метод keys позволяет предоставить более сложные последовательности ввода для данного элемента, чем обычно позволяет метод type. Например, вы можете указать Dusk удерживать модификаторы клавиш при вводе значений. В этом примере клавиша shift будет удерживаться при вводе taylor в элемент, соответствующий заданному селектору. После ввода taylor будет введено swift без удерживания модификаторов клавиш:

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

Другим полезным случаем использования метода keys является отправка комбинации "горячих клавиш" в основной CSS-селектор вашего приложения:

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

Примечание Все модификаторы клавиш, такие как {command}, заключены в фигурные скобки {}, и соответствуют константам, определенным в классе Facebook\WebDriver\WebDriverKeys, которые можно найти на GitHub.

Плавные взаимодействия с клавиатурой

Если вы хотите определить пользовательские взаимодействия с клавиатурой, которые вы можете легко использовать повторно во всем вашем наборе тестов, вы можете использовать метод macro, предоставленный классом Keyboard. Обычно этот метод следует вызывать из метода boot поставщика услуг:

use Laravel\Dusk\Keyboard;
 
$browser->withKeyboard(function (Keyboard $keyboard) {
$keyboard->press('c')
->pause(1000)
->release('c')
->type(['c', 'e', 'o']);
});

Макросы клавиатуры

Функция macro принимает имя в качестве своего первого аргумента и замыкание в качестве второго. Замыкание макроса будет выполнено при вызове макроса в качестве метода экземпляра Keyboard:

<?php
 
namespace App\Providers;
 
use Facebook\WebDriver\WebDriverKeys;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Keyboard;
use Laravel\Dusk\OperatingSystem;
 
class DuskServiceProvider extends ServiceProvider
{
/**
* Регистрация макросов браузера Dusk.
*/
public function boot(): void
{
Keyboard::macro('copy', function (string $element = null) {
$this->type([
OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c',
]);
 
return $this;
});
 
Keyboard::macro('paste', function (string $element = null) {
$this->type([
OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v',
]);
 
return $this;
});
}
}

Метод macro также предоставляет метод withKeyboard, позволяющий легко выполнять сложные взаимодействия с клавиатурой с использованием класса Laravel\Dusk\Keyboard. Класс Keyboard предоставляет методы press, release, type и pause:

$browser->click('@textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy())
->click('@another-textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());

Использование мыши

Клик по элементам

Метод click можно использовать для щелчка по элементу, соответствующему заданному CSS- или Dusk-селектору:

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

Метод clickAtXPath можно использовать для щелчка по элементу, соответствующему заданному выражению XPath:

$browser->clickAtXPath('//div[@class = "selector"]');

Метод clickAtPoint можно использовать для щелчка по верхнему элементу по заданной паре координат относительно видимой области браузера:

$browser->clickAtPoint($x = 0, $y = 0);

Метод doubleClick можно использовать для имитации двойного щелчка мышью:

$browser->doubleClick();

Метод rightClick можно использовать для имитации правого щелчка мышью:

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

Метод clickAndHold можно использовать для имитации щелчка мыши и удержания кнопки. Последующий вызов метода releaseMouse отменит это поведение и освободит кнопку мыши:

$browser->clickAndHold()
->pause(1000)
->releaseMouse();

Метод controlClick можно использовать для имитации события ctrl+click в браузере:

$browser->controlClick();

Наведение курсора мыши

Метод mouseover можно использовать, когда вам нужно переместить мышь над элементом, соответствующим заданному CSS- или Dusk-селектору:

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

Перетаскивание и бросание

Метод drag можно использовать для перетаскивания элемента, соответствующего заданному селектору, к другому элементу:

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

Или вы можете перетащить элемент в одном направлении:

$browser->dragLeft('.selector', $pixels = 10);
$browser->dragRight('.selector', $pixels = 10);
$browser->dragUp('.selector', $pixels = 10);
$browser->dragDown('.selector', $pixels = 10);

Наконец, вы можете перетащить элемент на заданное смещение:

$browser->dragOffset('.selector', $x = 10, $y = 10);

Диалоговые окна JavaScript

Dusk предоставляет различные методы для взаимодействия с диалоговыми окнами JavaScript. Например, вы можете использовать метод waitForDialog, чтобы подождать, пока не появится диалог JavaScript. Этот метод принимает необязательный аргумент, указывающий, сколько секунд ждать появления диалога:

$browser->waitForDialog($seconds = null);

Метод assertDialogOpened можно использовать для проверки того, что диалоговое окно отображено и содержит заданное сообщение:

$browser->assertDialogOpened('Dialog message');

Если диалоговое окно JavaScript содержит подсказку, вы можете использовать метод typeInDialog для ввода значения в подсказку:

$browser->typeInDialog('Hello World');

Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку "OK", вы можете вызвать метод acceptDialog:

$browser->acceptDialog();

Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку "Отмена", вы можете вызвать метод dismissDialog:

$browser->dismissDialog();

Взаимодействие с встроенными фреймами

Если вам нужно взаимодействовать с элементами внутри фрейма, вы можете использовать метод withinFrame. Все взаимодействия с элементами, которые происходят в пределах замыкания, предоставленного методу withinFrame, будут ограничены контекстом указанного фрейма:

$browser->withinFrame('#credit-card-details', function ($browser) {
$browser->type('input[name="cardnumber"]', '4242424242424242')
->type('input[name="exp-date"]', '12/24')
->type('input[name="cvc"]', '123');
})->press('Pay');
});

Ограничение селекторов

Иногда вам может потребоваться выполнить несколько операций, ограничив все операции в пределах заданного селектора. Например, вы можете захотеть проверить, что какой-то текст существует только в таблице, а затем щелкнуть кнопку в этой таблице. Вы можете использовать метод with для достижения этого. Все операции, выполненные в пределах замыкания, предоставленного методу with, будут ограничены исходным селектором:

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

Иногда вам может потребоваться выполнить утверждения вне текущего контекста. Вы можете использовать методы elsewhere и elsewhereWhenAvailable для достижения этого:

$browser->with('.table', function (Browser $table) {
// Текущий контекст - `body .table`...
 
$browser->elsewhere('.page-title', function (Browser $title) {
// Текущий контекст - `body .page-title`...
$title->assertSee('Hello World');
});
 
$browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
// Текущий контекст - `body .page-title`...
$title->assertSee('Hello World');
});
});

Ожидание элементов

При тестировании приложений, которые активно используют JavaScript, часто становится необходимым "ожидание" доступности определенных элементов или данных перед продолжением теста. Dusk упрощает это. Используя различные методы, вы можете ждать, пока элементы не станут видимыми на странице или даже дождаться, пока заданное выражение JavaScript не вычислится в true.

Ожидание

Если вам просто нужно приостановить выполнение теста на определенное количество миллисекунд, используйте метод pause:

$browser->pause(1000);

Если вам нужно приостановить выполнение теста только в том случае, если заданное условие равно true, используйте метод pauseIf:

$browser->pauseIf(App::environment('production'), 1000);

Точно так же, если вам нужно приостановить выполнение теста, если заданное условие не равно true, вы можете использовать метод pauseUnless:

$browser->pauseUnless(App::environment('testing'), 1000);

Ожидание селекторов

Метод waitFor можно использовать для приостановки выполнения теста до тех пор, пока элемент, соответствующий заданному CSS- или Dusk-селектору, не будет отображен на странице. По умолчанию это приостановит выполнение теста не более чем на пять секунд перед вызовом исключения. При необходимости вы можете передать собственный порог тайм-аута в качестве второго аргумента методу:

// Ожидание не более пяти секунд для селектора...
$browser->waitFor('.selector');
 
// Ожидание не более одной секунды для селектора...
$browser->waitFor('.selector', 1);

Вы также можете ждать, пока элемент, соответствующий заданному селектору, не содержит заданный текст:

// Ждем не более пяти секунд, пока селектор не содержит указанный текст...
$browser->waitForTextIn('.selector', 'Hello World');
 
// Ждем не более одной секунды, пока селектор не содержит указанный текст...
$browser->waitForTextIn('.selector', 'Hello World', 1);

Вы также можете ждать, пока элемент, соответствующий заданному селектору, отсутствует на странице:

// Ждем не более пяти секунд, пока селектор не исчезнет...
$browser->waitUntilMissing('.selector');
 
// Ждем не более одной секунды, пока селектор не исчезнет...
$browser->waitUntilMissing('.selector', 1);

Или вы можете ждать, пока элемент, соответствующий заданному селектору, не станет доступным или недоступным:

// Ждем не более пяти секунд, пока селектор не станет активным...
$browser->waitUntilEnabled('.selector');
 
// Ждем не более одной секунды, пока селектор не станет активным...
$browser->waitUntilEnabled('.selector', 1);
 
// Ждем не более пяти секунд, пока селектор не станет неактивным...
$browser->waitUntilDisabled('.selector');
 
// Ждем не более одной секунды, пока селектор не станет неактивным...
$browser->waitUntilDisabled('.selector', 1);

Ограничение селекторов, если доступно

Иногда вам может потребоваться подождать, пока не появится элемент, соответствующий заданному селектору, и затем взаимодействовать с элементом. Например, вы можете подождать, пока не станет доступно модальное окно, а затем нажать кнопку "OK" внутри модального окна. Метод whenAvailable можно использовать для достижения этого. Все операции с элементами, выполненные в пределах заданного замыкания, будут ограничены исходным селектором:

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

Ожидание текста

Метод waitForText можно использовать, чтобы подождать, пока заданный текст не отобразится на странице:

// Ждем не более пяти секунд, чтобы увидеть текст...
$browser->waitForText('Hello World');
 
// Ждем не более одной секунды, чтобы увидеть текст...
$browser->waitForText('Hello World', 1);

Вы можете использовать метод waitUntilMissingText для ожидания, пока отображаемый текст не исчезнет со страницы:

// Ждем не более пяти секунд, чтобы текст исчез...
$browser->waitUntilMissingText('Hello World');
 
// Ждем не более одной секунды, чтобы текст исчез...
$browser->waitUntilMissingText('Hello World', 1);

Ожидание ссылок

Метод waitForLink можно использовать для ожидания, пока на странице не будет отображен указанный текст ссылки:

// Ждем не более пяти секунд, чтобы увидеть ссылку...
$browser->waitForLink('Create');
 
// Ждем не более одной секунды, чтобы увидеть ссылку...
$browser->waitForLink('Create', 1);

Ожидание вводов

Метод waitForInput можно использовать для ожидания, пока указанное поле ввода не станет видимым на странице:

// Ждем не более пяти секунд, чтобы увидеть ввод...
$browser->waitForInput($field);
 
// Ждем не более одной секунды, чтобы увидеть ввод...
$browser->waitForInput($field, 1);

Ожидание местоположения на странице

При выполнении проверки пути, такой как $browser->assertPathIs('/home'), проверка может завершиться неудачно, если window.location.pathname обновляется асинхронно. Вы можете использовать метод waitForLocation для ожидания, чтобы расположение было равно заданному значению:

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

Метод waitForLocation также может быть использован для ожидания того, чтобы текущее местоположение окна было полностью квалифицированным URL:

$browser->waitForLocation('https://example.com/path');

Вы также можете ждать расположение именованного маршрута:

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

Ожидание перезагрузки страницы

Если вам нужно дождаться перезагрузки страницы после выполнения действия, используйте метод waitForReload:

use Laravel\Dusk\Browser;
 
$browser->waitForReload(function (Browser $browser) {
$browser->press('Submit');
})
->assertSee('Success!');

Поскольку необходимость дожидаться перезагрузки страницы обычно возникает после нажатия кнопки, вы можете использовать метод clickAndWaitForReload для удобства:

$browser->clickAndWaitForReload('.selector')
->assertSee('something');

Ожидание выражений JavaScript

Иногда вам может потребоваться приостановить выполнение теста до тех пор, пока данное выражение JavaScript не станет true. Это можно легко сделать с использованием метода waitUntil. При передаче выражения в этот метод не нужно включать ключевое слово return или конечную точку с запятой:

// Ждем не более пяти секунд, пока выражение не станет истинным...
$browser->waitUntil('App.data.servers.length > 0');
 
// Ждем не более одной секунды, пока выражение не станет истинным...
$browser->waitUntil('App.data.servers.length > 0', 1);

Ожидание выражений Vue

Методы waitUntilVue и waitUntilVueIsNot могут использоваться для ожидания, пока у Vue-компонента не будет задано указанное значение:

// Ждем, пока атрибут компонента не содержит указанное значение...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
 
// Ждем, пока атрибут компонента не содержит указанное значение...
$browser->waitUntilVueIsNot('user.name', null, '@user');

Ожидание событий JavaScript

Метод waitForEvent может использоваться для приостановки выполнения теста до тех пор, пока не произойдет событие JavaScript:

$browser->waitForEvent('load');

Слушатель событий прикреплен к текущей области видимости, которая по умолчанию является элементом body. При использовании сфокусированного селектора слушатель событий будет прикреплен к соответствующему элементу:

$browser->with('iframe', function (Browser $iframe) {
// Ждем события загрузки iframe...
$iframe->waitForEvent('load');
});

Вы также можете предоставить селектор в качестве второго аргумента методу waitForEvent, чтобы прикрепить слушатель событий к конкретному элементу:

$browser->waitForEvent('load', '.selector');

Вы также можете ожидать событий на объектах document и window:

// Ждем, пока документ прокручивается...
$browser->waitForEvent('scroll', 'document');
 
// Ждем не более пяти секунд до изменения размеров окна...
$browser->waitForEvent('resize', 'window', 5);

Ожидание с обратным вызовом

Многие методы "ожидания" в Dusk зависят от основного метода waitUsing. Вы можете использовать этот метод напрямую, чтобы ждать, пока заданное замыкание не вернет true. Метод waitUsing принимает максимальное количество секунд для ожидания, интервал, с которым должно быть оценено замыкание, само замыкание и необязательное сообщение об ошибке:

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

Прокрутка элемента в видимую область

Иногда вы можете не иметь возможности нажать на элемент, потому что он находится за пределами видимой области браузера. Метод scrollIntoView прокрутит окно браузера, пока элемент с заданным селектором не окажется в видимости:

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

Доступные утверждения

Dusk предоставляет различные утверждения, которые вы можете сделать в отношении вашего приложения. Все доступные утверждения приведены в списке ниже:

assertTitle

Утверждать, что заголовок страницы соответствует заданному тексту:

$browser->assertTitle($title);

assertTitleContains

Утверждайте, что заголовок страницы содержит указанный текст:

$browser->assertTitleContains($title);

assertUrlIs

Утверждайте, что текущий URL (без строки запроса) соответствует указанной строке:

$browser->assertUrlIs($url);

assertSchemeIs

Утверждайте, что текущая схема URL соответствует указанной схеме:

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

Утверждайте, что текущая схема URL не соответствует указанной схеме:

$browser->assertSchemeIsNot($scheme);

assertHostIs

Утверждайте, что текущий хост URL соответствует указанному хосту:

$browser->assertHostIs($host);

assertHostIsNot

Утверждайте, что текущий хост URL не соответствует указанному хосту:

$browser->assertHostIsNot($host);

assertPortIs

Утверждайте, что текущий порт URL соответствует указанному порту:

$browser->assertPortIs($port);

assertPortIsNot

Утверждайте, что текущий порт URL не соответствует указанному порту:

$browser->assertPortIsNot($port);

assertPathBeginsWith

Утверждайте, что текущий путь URL начинается с указанного пути:

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

assertPathIs

Утверждайте, что текущий путь соответствует указанному пути:

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

assertPathIsNot

Утверждайте, что текущий путь не соответствует указанному пути:

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

assertRouteIs

Утверждайте, что текущий URL соответствует URL именованного маршрута:

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

assertQueryStringHas

Утверждайте, что указанный параметр строки запроса присутствует:

$browser->assertQueryStringHas($name);

Утверждайте, что указанный параметр строки запроса присутствует и имеет заданное значение:

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

assertQueryStringMissing

Утверждайте, что указанный параметр строки запроса отсутствует:

$browser->assertQueryStringMissing($name);

assertFragmentIs

Утверждайте, что текущий фрагмент хеша URL соответствует указанному фрагменту:

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

Утверждайте, что текущий фрагмент хеша URL начинается с указанного фрагмента:

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

Утверждайте, что текущий фрагмент хеша URL не соответствует указанному фрагменту:

$browser->assertFragmentIsNot('anchor');

assertHasCookie

Утверждайте, что указанная зашифрованная куки присутствует:

$browser->assertHasCookie($name);

assertHasPlainCookie

Утверждайте, что указанная не зашифрованная куки присутствует:

$browser->assertHasPlainCookie($name);

assertCookieMissing

Утверждайте, что указанная зашифрованная куки отсутствует:

$browser->assertCookieMissing($name);

assertPlainCookieMissing

Утверждайте, что указанная не зашифрованная куки отсутствует:

$browser->assertPlainCookieMissing($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);

assertSeeAnythingIn

Утверждайте, что внутри селектора присутствует любой текст:

$browser->assertSeeAnythingIn($selector);

assertSeeNothingIn

Утверждайте, что внутри селектора отсутствует любой текст:

$browser->assertSeeNothingIn($selector);

assertScript

Утверждайте, что указанное JavaScript-выражение оценивается в указанное значение:

$browser->assertScript('window.isLoaded')
->assertScript('document.readyState', 'complete');

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);

assertIndeterminate

Утверждайте, что указанный флажок в нерешенном состоянии:

$browser->assertIndeterminate($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);

assertSelectMissingOption

Утверждайте, что указанное значение недоступно для выбора:

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

assertValue

Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение:

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

assertValueIsNot

Утверждайте, что элемент, соответствующий указанному селектору, не имеет указанного значения:

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

assertAttribute

Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте:

$browser->assertAttribute($selector, $attribute, $value);

assertAttributeContains

Утверждайте, что элемент, соответствующий указанному селектору, содержит указанное значение в указанном атрибуте:

$browser->assertAttributeContains($selector, $attribute, $value);

assertAriaAttribute

Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте aria:

$browser->assertAriaAttribute($selector, $attribute, $value);

Например, при данной разметке <button aria-label=\"Add\"></button> вы можете утверждать против атрибута aria-label следующим образом:

$browser->assertAriaAttribute('button', 'label', 'Add')

assertDataAttribute

Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте данных:

$browser->assertDataAttribute($selector, $attribute, $value);

Например, при данной разметке <tr id=\"row-1\" data-content=\"attendees\"></tr> вы можете утверждать против атрибута data-label следующим образом:

$browser->assertDataAttribute('#row-1', 'content', 'attendees')

assertVisible

Утверждайте, что элемент, соответствующий указанному селектору, видим:

$browser->assertVisible($selector);

assertPresent

Утверждайте, что элемент, соответствующий указанному селектору, присутствует в исходном коде:

$browser->assertPresent($selector);

assertNotPresent

Утверждайте, что элемент, соответствующий указанному селектору, отсутствует в исходном коде:

$browser->assertNotPresent($selector);

assertMissing

Утверждайте, что элемент, соответствующий указанному селектору, не видим:

$browser->assertMissing($selector);

assertInputPresent

Утверждайте, что присутствует поле ввода с указанным именем:

$browser->assertInputPresent($name);

assertInputMissing

Утверждайте, что поле ввода с указанным именем отсутствует в исходном коде:

$browser->assertInputMissing($name);

assertDialogOpened

Утверждайте, что открыт диалог JavaScript с указанным сообщением:

$browser->assertDialogOpened($message);

assertEnabled

Утверждайте, что указанное поле включено:

$browser->assertEnabled($field);

assertDisabled

Утверждайте, что указанное поле отключено:

$browser->assertDisabled($field);

assertButtonEnabled

Утверждайте, что указанная кнопка включена:

$browser->assertButtonEnabled($button);

assertButtonDisabled

Утверждайте, что указанная кнопка отключена:

$browser->assertButtonDisabled($button);

assertFocused

Утверждайте, что указанное поле сфокусировано:

$browser->assertFocused($field);

assertNotFocused

Утверждайте, что указанное поле не сфокусировано:

$browser->assertNotFocused($field);

assertAuthenticated

Утверждайте, что пользователь аутентифицирован:

$browser->assertAuthenticated();

assertGuest

Утверждайте, что пользователь не аутентифицирован:

$browser->assertGuest();

assertAuthenticatedAs

Утверждайте, что пользователь аутентифицирован как указанный пользователь:

$browser->assertAuthenticatedAs($user);

assertVue

Dusk даже позволяет делать утверждения о состоянии данных Vue компонента. Например, представьте, что ваше приложение содержит следующий Vue компонент:

// HTML...
 
<profile dusk="profile-component"></profile>
 
// Определение компонента...
 
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
 
data: function () {
return {
user: {
name: 'Taylor'
}
};
}
});

Вы можете утверждать о состоянии Vue компонента следующим образом:

/**
* Простой пример теста Vue.
*/
public function test_vue(): void
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}

assertVueIsNot

Утверждайте, что указанное свойство данных Vue компонента не соответствует указанному значению:

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

assertVueContains

Утверждайте, что указанное свойство данных Vue компонента является массивом и содержит указанное значение:

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

assertVueDoesNotContain

Утверждайте, что указанное свойство данных Vue компонента является массивом и не содержит указанное значение:

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

Страницы

Иногда тесты требуют выполнения нескольких сложных действий последовательно. Это может сделать ваши тесты более сложными для чтения и понимания. Dusk Pages позволяют вам определять выразительные действия, которые затем могут быть выполнены на данной странице с помощью одного метода. Pages также позволяют вам определять ярлыки для общих селекторов для вашего приложения или для одной страницы.

Создание страниц

Для создания объекта страницы выполните команду Artisan dusk:page. Все объекты страниц будут помещены в каталог tests/Browser/Pages вашего приложения:

php artisan dusk:page Login

Настройка страниц

По умолчанию у страниц есть три метода: url, assert и elements. Мы обсудим методы url и assert сейчас. Метод elements будет обсуждаться более подробно ниже.

Метод url

Метод url должен возвращать путь URL, представляющий страницу. Dusk будет использовать этот URL при переходе на страницу в браузере:

/**
* Получение URL страницы.
*/
public function url(): string
{
return '/login';
}

Метод assert

Метод assert может делать любые утверждения, необходимые для проверки того, что браузер действительно находится на указанной странице. Фактически, не обязательно что-то помещать в этот метод; однако вы вольны делать эти утверждения, если хотите. Эти утверждения будут выполняться автоматически при переходе на страницу:

/**
* Проверка, что браузер открыт на странице.
*/
public function assert(Browser $browser): void
{
$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<string, string>
*/
public function elements(): array
{
return [
'@email' => 'input[name=email]',
];
}

После определения ярлыка вы можете использовать ярлык везде, где обычно использовали бы полный CSS-селектор:

$browser->type('@email', '[email protected]');

Глобальные сокращенные селекторы

После установки Dusk будет создан базовый класс Page в каталоге tests/Browser/Pages. Этот класс содержит метод siteElements, который может быть использован для определения глобальных ярлыков селекторов, которые должны быть доступны на каждой странице в вашем приложении:

/**
* Получение глобальных ярлыков элементов для сайта.
*
* @return array<string, string>
*/
public static function siteElements(): array
{
return [
'@element' => '#selector',
];
}

Методы страницы

Помимо методов, определенных по умолчанию на страницах, вы можете определить дополнительные методы, которые могут быть использованы во всех ваших тестах. Например, представьте, что вы создаете приложение для управления музыкой. Общим действием для одной страницы приложения может быть создание плейлиста. Вместо повторного написания логики создания плейлиста в каждом тесте вы можете определить метод createPlaylist на странице:

<?php
 
namespace Tests\Browser\Pages;
 
use Laravel\Dusk\Browser;
 
class Dashboard extends Page
{
// Другие методы страницы...
 
/**
* Создание нового плейлиста.
*/
public function createPlaylist(Browser $browser, string $name): void
{
$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, но предназначены для частей интерфейса и функционала, которые повторно используются в вашем приложении, таких как панель навигации или окно уведомлений. Таким образом, компоненты не привязаны к конкретным URL.

Создание компонентов

Для создания компонента выполните команду Artisan dusk:component. Новые компоненты помещаются в каталог tests/Browser/Components:

php artisan dusk:component DatePicker

Как показано выше, "выбор даты" - это пример компонента, который может существовать в вашем приложении на различных страницах. Ручное написание логики автоматизации браузера для выбора даты в десятках тестов может быть неудобным. Вместо этого мы можем определить компонент Dusk, представляющий выбор даты, что позволит нам инкапсулировать эту логику внутри компонента:

<?php
 
namespace Tests\Browser\Components;
 
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
 
class DatePicker extends BaseComponent
{
/**
* Получение корневого селектора компонента.
*/
public function selector(): string
{
return '.date-picker';
}
 
/**
* Проверка, что страница браузера содержит компонент.
*/
public function assert(Browser $browser): void
{
$browser->assertVisible($this->selector());
}
 
/**
* Получение ярлыков элементов для компонента.
*
* @return array<string, string>
*/
public function elements(): array
{
return [
'@date-field' => 'input.datepicker-input',
'@year-list' => 'div > div.datepicker-years',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
];
}
 
/**
* Выбор указанной даты.
*/
public function selectDate(Browser $browser, int $year, int $month, int $day): void
{
$browser->click('@date-field')
->within('@year-list', function (Browser $browser) use ($year) {
$browser->click($year);
})
->within('@month-list', function (Browser $browser) use ($month) {
$browser->click($month);
})
->within('@day-list', function (Browser $browser) use ($day) {
$browser->click($day);
});
}
}

Использование компонентов

После определения компонента мы легко можем выбрать дату в компоненте из любого теста. И если логика выбора даты изменится, нам нужно будет обновить только компонент:

<?php
 
namespace Tests\Browser;
 
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
/**
* Простой пример теста компонента.
*/
public function test_basic_example(): void
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function (Browser $browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
}
}

Непрерывная интеграция

Внимание Большинство конфигураций непрерывной интеграции Dusk предполагают, что ваше приложение Laravel обслуживается с использованием встроенного веб-сервера PHP на порту 8000. Поэтому перед продолжением вы должны убедиться, что в вашей среде непрерывной интеграции установлено значение переменной среды APP_URL равным http://127.0.0.1:8000.

Heroku CI

Чтобы запустить тесты Dusk на Heroku CI, добавьте следующий Google Chrome buildpack и скрипты в ваш файл 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 --no-reload > /dev/null 2>&1 &' && php artisan dusk"
}
}
}
}

Travis CI

Для запуска ваших тестов Dusk на Travis CI используйте следующую конфигурацию .travis.yml. Поскольку Travis CI не является графической средой, нам придется предпринять дополнительные шаги для запуска браузера Chrome. Кроме того, мы будем использовать php artisan serve для запуска веб-сервера PHP:

language: php
 
php:
- 7.3
 
addons:
chrome: stable
 
install:
- cp .env.testing .env
- travis_retry composer install --no-interaction --prefer-dist
- php artisan key:generate
- php artisan dusk:chrome-driver
 
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- php artisan serve --no-reload &
 
script:
- php artisan dusk

GitHub Actions

Если вы используете GitHub Actions для запуска ваших тестов Dusk, вы можете использовать следующий файл конфигурации как отправную точку. Как и в TravisCI, мы будем использовать команду php artisan serve для запуска встроенного веб-сервера PHP:

name: CI
on: [push]
jobs:
 
dusk-php:
runs-on: ubuntu-latest
env:
APP_URL: "http://127.0.0.1:8000"
DB_USERNAME: root
DB_PASSWORD: root
MAIL_MAILER: log
steps:
- uses: actions/checkout@v4
- name: Prepare The Environment
run: cp .env.example .env
- name: Create Database
run: |
sudo systemctl start mysql
mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;"
- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver --detect
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux &
- name: Run Laravel Server
run: php artisan serve --no-reload &
- name: Run Dusk Tests
run: php artisan dusk
- name: Upload Screenshots
if: failure()
uses: actions/upload-artifact@v2
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v2
with:
name: console
path: tests/Browser/console

Chipper CI

Если вы используете Chipper CI для запуска ваших тестов Dusk, вы можете использовать следующий файл конфигурации как отправную точку. Мы будем использовать встроенный сервер PHP для запуска Laravel, чтобы прослушивать запросы:

# file .chipperci.yml
version: 1
 
environment:
php: 8.2
node: 16
 
# Include Chrome in the build environment
services:
- dusk
 
# Build all commits
on:
push:
branches: .*
 
pipeline:
- name: Setup
cmd: |
cp -v .env.example .env
composer install --no-interaction --prefer-dist --optimize-autoloader
php artisan key:generate
 
# Create a dusk env file, ensuring APP_URL uses BUILD_HOST
cp -v .env .env.dusk.ci
sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci
 
- name: Compile Assets
cmd: |
npm ci --no-audit
npm run build
 
- name: Browser Tests
cmd: |
php -S [::0]:8000 -t public 2>server.log &
sleep 2
php artisan dusk:chrome-driver $CHROME_DRIVER
php artisan dusk --env=ci

Чтобы узнать больше о запуске тестов Dusk на Chipper CI, включая использование баз данных, проконсультируйтесь с официальной документацией Chipper CI.