Laravel Dusk
- Введение
- Установка
- Управление установками ChromeDriver
- Использование других браузеров
- Начало работы
- Генерация тестов
- Сброс базы данных после каждого теста
- Запуск тестов
- Обработка файла переменных окружения
- Основы работы с браузером
- Создание браузеров
- Навигация
- Изменение размера окна браузера
- Макрокоманды браузера
- Аутентификация
- Cookies
- Выполнение JavaScript
- Получение снимка экрана
- Сохранение вывода консоли на диск
- Сохранение исходного кода страницы на диск
- Взаимодействие с элементами
- Селекторы Dusk
- Текст, значения и атрибуты
- Взаимодействие с формами
- Прикрепление файлов
- Нажатие кнопок
- Клик по ссылкам
- Использование клавиатуры
- Использование мыши
- Диалоговые окна JavaScript (Alert, Prompt, Confirm)
- Взаимодействие с фреймами
- Сегментированное тестирование по селекторам
- Ожидание доступности элементов
- Прокрутка элемента в область видимости пользователя
- Доступные утверждения
- Тестовые страницы
- Генерация тестовых страниц
- Конфигурирование тестовых страниц
- Навигация по тестовым страницам
- Псевдонимы селекторов
- Методы тестовых страниц
- Компоненты для тестов
- Генерация компонентов
- Использование компонентов
- Непрерывная интеграция
- Heroku CI
- Travis CI
- GitHub Actions
- Chipper CI
Введение
Laravel Dusk предоставляет выразительный и простой в использовании API для автоматизации и тестирования браузера. По умолчанию Dusk не требует установки JDK или Selenium на ваш локальный компьютер. Вместо этого Dusk использует автономную установку ChromeDriver. По желанию вы можете использовать любой другой драйвер, совместимый с Selenium.
Установка
Для начала установите Google Chrome и laravel/dusk
с помощью менеджера пакетов Composer в свой проект:
composer require laravel/dusk --dev
Если вы вручную регистрируете поставщика
DuskServiceProvider
, вам никогда не следует регистрировать его в рабочем окружении, так как это может привести к тому, что случайные пользователи смогут пройти аутентификацию в вашем приложении.
После установки пакета 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
:
# Установить последнюю версию ChromeDriver для вашей ОС ...
php artisan dusk:chrome-driver
# Установить конкретную версию ChromeDriver для вашей ОС ...
php artisan dusk:chrome-driver 86
# Установить конкретную версию ChromeDriver для всех поддерживаемых ОС ...
php artisan dusk:chrome-driver --all
# Установить версию ChromeDriver, которая соответствует обнаруженной версии Chrome / Chromium для вашей ОС ...
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
. Это остановит Dusk от автоматического запуска ChromeDriver:
/**
* Подготовить Dusk для выполнения теста.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}
Затем вы можете изменить метод driver
для подключения к URL-адресу и порту по вашему выбору. Кроме того, вы можете изменить «требуемые характеристики» через класс DesiredCapabilities
, передаваемые экземпляру WebDriver:
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Создать экземпляр RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}
Начало работы
Генерация тестов
Чтобы сгенерировать тест Dusk, используйте команду dusk:make
Artisan. Сгенерированный тест будет помещен в каталог tests/Browser
:
php artisan dusk:make LoginTest
Сброс базы данных после каждого теста
Большинство тестов, которые вы пишете, будут взаимодействовать со страницами, получающими данные из базы данных вашего приложения; однако, ваши тесты Dusk никогда не должны использовать трейт RefreshDatabase
. Трейт RefreshDatabase
использует транзакции базы данных, которые не будут применимы или доступны в течение HTTP-запросов. Вместо этого у вас есть два варианта: трейт DatabaseMigrations
и трейт DatabaseTruncation
.
Использование миграций
Трейт DatabaseMigrations
будет запускать миграции базы данных перед каждым тестом. Однако удаление и воссоздание таблиц базы данных для каждого теста обычно происходит медленнее, чем очистка таблиц:
<?php
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
//
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
//
}
Базы данных SQLite, хранимые в памяти, нельзя использовать при выполнении тестов Dusk. Поскольку браузер выполняет свой собственный процесс, он не сможет получить доступ к базам данных, хранимых в памяти, других процессов.
Использование Truncation
Трейт DatabaseTruncation
проведет миграцию вашей базы данных перед первым тестом, чтобы убедиться, что таблицы базы данных были правильно созданы. Однако в последующих тестах таблицы базы данных будут просто очищены, что обеспечивает ускорение по сравнению с повторным выполнением всех миграций базы данных:
<?php
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
uses(DatabaseTruncation::class);
//
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseTruncation;
//
}
По умолчанию этот трейт очищает все таблицы, кроме таблицы migrations
. Если вы хотите настроить таблицы, которые должны быть очищены, вы можете определить свойство $tablesToTruncate
в вашем тестовом классе:
Если вы используете Pest, вам следует определить свойства или методы базового класса
DuskTestCase
или любого класса, расширяемого вашим тестовым файлом.
/**
* Указывает, какие таблицы должны быть очищены.
*
* @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
{
//
}
Запуск тестов
Чтобы запустить браузерные тесты, выполните команду dusk
Artisan:
php artisan dusk
Если при последнем запуске команды dusk
у вас были ошибки тестирования, то вы можете сэкономить время, повторно запустив сначала неудачные тесты с помощью команды dusk:fails
:
php artisan dusk:fails
Команда dusk
принимает любой аргумент, который обычно принимается тестером Pest / 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
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
test('basic example', function () {
$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');
});
});
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* A basic browser test example.
*/
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($routeName, $parameters);
Вы можете перемещаться «назад» и «вперед», используя методы 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');
});
Аутентификация
Часто вы будете тестировать страницы, требующие аутентификации. Вы можете использовать метод Dusk loginAs
, чтобы избежать взаимодействия с экраном входа в систему вашего приложения во время каждого теста. Метод loginAs
принимает первичный ключ аутентифицируемой модели или, непосредственно, экземпляр аутентифицируемой модели:
use App\Models\User;
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});
После использования метода
loginAs
сессия пользователя будет поддерживаться для всех тестов, находящихся в файле.
Cookies
Вы можете использовать метод cookie
для получения или установления зашифрованного значения cookie. По умолчанию все файлы cookie, созданные Laravel, зашифрованы:
$browser->cookie('name');
$browser->cookie('name', 'Taylor');
Вы можете использовать метод plainCookie
для получения или установления незашифрованного значения cookie:
$browser->plainCookie('name');
$browser->plainCookie('name', 'Taylor');
Вы можете использовать метод deleteCookie
для удаления конкретного файла cookie:
$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');
Метод screenshotElement
можно использовать для создания снимка экрана определенного элемента на странице:
$browser->screenshotElement('#selector', '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. Чтобы определить селектор, добавьте к вашему элементу HTML-атрибут dusk
. Затем, при взаимодействии с браузером Dusk, добавьте к селектору префикс @
, чтобы управлять закрепленным элементом в вашем тесте:
// HTML-разметка ...
<button dusk="login-button">Login</button>
// Выполнение теста ...
$browser->click('@login-button');
Если вам нужно, вы можете настроить HTML-атрибут, который использует селектор Dusk, с помощью метода selectorHtmlAttribute
. Обычно этот метод следует вызывать из метода boot
вашего AppServiceProvider
приложения:
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
. Как и метод type
, метод select
не требует полного селектора CSS. При передаче значения методу select
вы должны передать значение параметра value
вместо отображаемого текста:
$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
. Как и многие другие методы, связанные с вводом, полный селектор CSS не требуется. Если совпадение селектора CSS не найдено, то Dusk будет искать радиокнопку с соответствующими атрибутами name
и value
:
$browser->radio('size', 'large');
Прикрепление файлов
Метод attach
используется для прикрепления файла к элементу выбора файлов. Как и многие другие методы, связанные с вводом, полный селектор CSS не требуется. Если совпадение селектора CSS не найдено, то Dusk будет искать элемент выбора файлов с соответствующим атрибутом name
:
$browser->attach('photo', __DIR__.'/photos/mountains.png');
Функционал прикрепления требует, чтобы на вашем сервере было установлено и включено расширение
Zip
PHP.
Нажатие кнопок
Метод 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.
Взаимодействия с клавиатурой
Dusk также предоставляет метод withKeyboard
, который позволяет гибко выполнять сложные взаимодействия с клавиатурой через класс Laravel\Dusk\Keyboard
. Класс Keyboard
предоставляет методы press
, release
, type
и pause
:
use Laravel\Dusk\Keyboard;
$browser->withKeyboard(function (Keyboard $keyboard) {
$keyboard->press('c')
->pause(1000)
->release('c')
->type(['c', 'e', 'o']);
});
Макросы Клавиатуры
Если вы хотите определить пользовательские взаимодействия с клавиатурой, которые вы можете легко повторно использовать в вашем наборе тестов, вы можете использовать метод macro
, предоставляемый классом Keyboard
. Обычно этот метод следует вызывать из метода boot
поставщика услуг:
<?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
принимает имя в качестве первого аргумента и замыкание в качестве второго. Замыкание макроса будет выполнено при вызове макроса в качестве метода экземпляра Keyboard
:
$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();
$browser->doubleClick('.selector');
Метод rightClick
используется для имитации щелчка правой кнопкой мыши:
$browser->rightClick();
$browser->rightClick('.selector');
Метод clickAndHold
используется для имитации нажатия и удержания кнопки мыши. Последующий вызов метода releaseMouse
отменяет это поведение и отпускает кнопку мыши:
$browser->clickAndHold('.selector');
$browser->clickAndHold()
->pause(1000)
->releaseMouse();
Метод controlClick
может быть использован для симуляции события ctrl+click
в браузере:
$browser->controlClick();
$browser->controlClick('.selector');
Наведение мыши
Метод 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 (Alert, Prompt, Confirm)
Dusk содержит различные методы для взаимодействия с диалогами JavaScript. Например, вы можете использовать метод waitForDialog
, чтобы дождаться появления диалогового окна JavaScript. Этот метод принимает необязательный аргумент, указывающий, сколько секунд ждать до появления диалогового окна:
$browser->waitForDialog($seconds = null);
Метод assertDialogOpened
используется для утверждения того, что диалоговое окно было отображено и содержит указанное сообщение:
$browser->assertDialogOpened('Dialog message');
Если диалоговое окно JavaScript содержит поле ввода, то вы можете использовать метод typeInDialog
, чтобы ввести значение:
$browser->typeInDialog('Hello World');
Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку «ОК», вы можете вызвать метод acceptDialog
:
$browser->acceptDialog();
Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку «Отмена», вы можете вызвать метод dismissDialog
:
$browser->dismissDialog();
Взаимодействие с фреймами
Если вам нужно взаимодействовать с элементами внутри iframe, вы можете использовать метод withinFrame
. Все взаимодействия с элементами, происходящие в замыкании, предоставленном методу withinFrame
, будут ограничены контекстом указанного iframe:
$browser->withinFrame('#credit-card-details', function ($browser) {
$browser->type('input[name="cardnumber"]', '4242424242424242')
->type('input[name="exp-date"]', '1224')
->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 не станет «истинным».
Ожидание
Если вам нужно просто приостановить тест на определенное количество миллисекунд, используйте метод 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);
Сегментированное тестирование при доступности селекторов
Иногда требуется дождаться появления элемента с указанным селектором, а затем взаимодействовать с этим элементом. Например, вы можете подождать, пока не станет доступно модальное окно, а затем нажать кнопку «ОК» в модальном окне. Для этого можно использовать метод 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);
Ожидание Input-элементов
Метод 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 не станет истинным. Вы можете легко сделать это, используя метод 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
- assertTitleContains
- assertUrlIs
- assertSchemeIs
- assertSchemeIsNot
- assertHostIs
- assertHostIsNot
- assertPortIs
- assertPortIsNot
- assertPathBeginsWith
- assertPathEndsWith
- assertPathContains
- assertPathIs
- assertPathIsNot
- assertRouteIs
- assertQueryStringHas
- assertQueryStringMissing
- assertFragmentIs
- assertFragmentBeginsWith
- assertFragmentIsNot
- assertHasCookie
- assertHasPlainCookie
- assertCookieMissing
- assertPlainCookieMissing
- assertCookieValue
- assertPlainCookieValue
- assertSee
- assertDontSee
- assertSeeIn
- assertDontSeeIn
- assertSeeAnythingIn
- assertSeeNothingIn
- assertScript
- assertSourceHas
- assertSourceMissing
- assertSeeLink
- assertDontSeeLink
- assertInputValue
- assertInputValueIsNot
- assertChecked
- assertNotChecked
- assertIndeterminate
- assertRadioSelected
- assertRadioNotSelected
- assertSelected
- assertNotSelected
- assertSelectHasOptions
- assertSelectMissingOptions
- assertSelectHasOption
- assertSelectMissingOption
- assertValue
- assertValueIsNot
- assertAttribute
- assertAttributeContains
- assertAttributeDoesntContain
- assertAriaAttribute
- assertDataAttribute
- assertVisible
- assertPresent
- assertNotPresent
- assertMissing
- assertInputPresent
- assertInputMissing
- assertDialogOpened
- assertEnabled
- assertDisabled
- assertButtonEnabled
- assertButtonDisabled
- assertFocused
- assertNotFocused
- assertAuthenticated
- assertGuest
- assertAuthenticatedAs
- assertVue
- assertVueIsNot
- assertVueContains
- assertVueDoesntContain
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');
assertPathEndsWith
Утверждает, что текущий путь URL-адреса заканчивается заданным путем:
$browser->assertPathEndsWith('/home');
assertPathContains
Утверждает, что текущий путь URL-адреса содержит заданный путь:
$browser->assertPathContains('/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
Утверждает, что переданный зашифрованный файл cookie присутствует:
$browser->assertHasCookie($name);
assertHasPlainCookie
Утверждает, что переданный незашифрованный файл cookie присутствует:
$browser->assertHasPlainCookie($name);
assertCookieMissing
Утверждает, что переданный зашифрованный файл cookie отсутствует:
$browser->assertCookieMissing($name);
assertPlainCookieMissing
Утверждает, что переданный незашифрованный файл cookie отсутствует:
$browser->assertPlainCookieMissing($name);
assertCookieValue
Утверждает, что зашифрованный файл cookie имеет указанное значение:
$browser->assertCookieValue($name, $value);
assertPlainCookieValue
Утверждает, что незашифрованный файл cookie имеет указанное значение:
$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
Утверждение, что данный флажок (checkbox) находится в неопределенном состоянии:
$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);
assertAttributeDoesntContain
Утверждение, что элемент, соответствующий данному селектору, не содержит заданное значение в указанном атрибуте:
$browser->assertAttributeDoesntContain($selector, $attribute, $value);
assertAriaAttribute
Утверждает, что элемент с указанным селектором, имеет переданное значение aria
-атрибута:
$browser->assertAriaAttribute($selector, $attribute, $value);
Например, учитывая разметку <button aria-label="Add"></button>
, вы можете выстроить утверждение относительно атрибута aria-label
следующим образом:
$browser->assertAriaAttribute('button', 'label', 'Add')
assertDataAttribute
Утверждает, что элемент с указанным селектором, имеет переданное значение data
-атрибута:
$browser->assertDataAttribute($selector, $attribute, $value);
Например, учитывая разметку <tr id="row-1" data-content="attendees"></tr>
, вы можете выстроить утверждение относительно атрибута data-content
следующим образом:
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
assertVisible
Утверждает, что элемент с указанным селектором, видим:
$browser->assertVisible($selector);
assertPresent
Утверждает, что элемент с указанным селектором, присутствует в исходном коде страницы:
$browser->assertPresent($selector);
assertNotPresent
Утверждает, что элемент с указанным селектором, отсутствует в исходном коде страницы:
$browser->assertNotPresent($selector);
assertMissing
Утверждает, что элемент с указанным селектором, не виден:
$browser->assertMissing($selector);
assertInputPresent
Утверждают, что присутствует “input” с заданным именем:
$browser->assertInputPresent($name);
assertInputMissing
Утверждают, что “input” с данным именем отсутствует в источнике:
$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 следующим образом:
test('vue', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
});
/**
* A basic Vue test example.
*/
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);
assertVueDoesntContain
Утверждает, что переданное свойство данных компонента Vue является массивом и не содержит указанное значения:
$browser->assertVueDoesntContain($property, $value, $componentSelector = null);
Тестовые страницы
Иногда тесты требуют последовательного выполнения нескольких сложных действий. Это может затруднить чтение и понимание ваших тестов. Страницы Dusk позволяют вам выразительно определять действия, которые затем могут быть выполнены на данной странице с помощью одного метода. Страницы также позволяют вам определять псевдонимы для общих селекторов всего приложения или отдельной страницы.
Генерация тестовых страниц
Чтобы сгенерировать класс страницы, выполните команду dusk:page
Artisan. Все классы страниц будут помещены в каталог 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 на вашей странице. Например, давайте определим псевдоним для поля ввода «электронная почта» на странице входа в приложение:
/**
* Получить псевдонимы элементов страницы.
*
* @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;
use Laravel\Dusk\Page;
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, но предназначены для частей пользовательского интерфейса и функций, которые повторно используются в вашем приложении, таких как панель навигации или окно уведомлений. Таким образом, компоненты не привязаны к конкретным URL-адресам.
Генерация компонентов
Чтобы сгенерировать компонент, выполните команду dusk:component
Artisan. Новые компоненты будут помещены в каталог 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
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
uses(DatabaseMigrations::class);
test('basic example', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function (Browser $browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
});
<?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
{
/**
* A basic component test example.
*/
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 в свой файл app.json
Heroku:
{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /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:
- 8.2
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 --port=9515 &
- 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@v4
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: console
path: tests/Browser/console
Chipper CI
Если вы используете Chipper CI для запуска ваших тестов Dusk, вы можете использовать следующий конфигурационный файл в качестве отправной точки. Мы будем использовать встроенный сервер PHP для запуска Laravel, чтобы прослушивать запросы:
# файл .chipperci.yml
version: 1
environment:
php: 8.2
node: 16
# Включаем Chrome в среду сборки
services:
- dusk
# Собираем все коммиты
on:
push:
branches: .*
pipeline:
- name: Setup
cmd: |
cp -v .env.example .env
composer install --no-interaction --prefer-dist --optimize-autoloader
php artisan key:generate
# Создаем файл окружения dusk, убедившись, что APP_URL использует 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.