Идёт обновление сайта. Несколько дней возможны сбои в оформлении и переводах. Документация работает — если страница выглядит сломанной, обновите её позже.

Документация
L Laravel L intervention/image
Войти

Laravel Dusk

10.x 7 мар 2026 г.

#Введение

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

#Установка

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

composer require laravel/dusk --dev
Внимание

Если вы регистрируете сервис-провайдер 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:

# Установить последнюю версию 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. Это остановит автоматический запуск 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;
}
Внимание

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

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

Перед использованием трейда 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. В этом случае следует закомментировать следующую строку в файле 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 будет восстановлен.

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

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

Для начала напишем тест, который проверит возможность входа в приложение. После генерации теста мы можем изменить его, чтобы перейти на страницу входа, ввести данные и нажать кнопку "Login". Для создания экземпляра браузера вызовите метод 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' => 'taylor@laravel.com',
        ]);

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

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

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

Иногда для корректного выполнения теста требуется несколько браузеров. Например, для тестирования чата с использованием websocket. Чтобы создать несколько браузеров, добавьте дополнительные аргументы браузера в сигнатуру замыкания, передаваемого в метод 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 сессия пользователя будет сохраняться для всех тестов в файле.

#Куки

Метод 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. Со временем изменения во frontend могут привести к тому, что селекторы, подобные следующим, сломают ваши тесты:

// 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 вашего 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', 'taylor@laravel.com');

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

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

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

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

$browser->clear('email');

Метод 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');

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

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

#Флажки (Checkboxes)

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

$browser->check('terms');

Метод uncheck снимает отметку с флажка:

$browser->uncheck('terms');

#Радиокнопки

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

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

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

Метод attach позволяет прикрепить файл к элементу ввода типа file. Как и в других методах, полный 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.

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

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-диалоги

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

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

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

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

Если диалог содержит prompt, метод typeInDialog позволяет ввести значение в prompt:

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

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

$browser->acceptDialog();

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

$browser->dismissDialog();

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

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

$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 или точку с запятой:

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

// Ждать максимум одну секунду, пока выражение станет true...
$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) {
    // Ждать событие load у iframe...
    $iframe->waitForEvent('load');
});

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

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

Можно ждать события на объектах document и window:

// Ждать прокрутки документа...
$browser->waitForEvent('scroll', 'document');

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

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

Многие методы «wait» в 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 предоставляет множество утверждений, которые можно использовать для проверки вашего приложения. Все доступные утверждения перечислены ниже:

<style> .collection-method-list > p { columns: 10.8em 3; -moz-columns: 10.8em 3; -webkit-columns: 10.8em 3; } .collection-method-list a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } </style>

#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

Проверяет, что путь текущего URL совпадает с указанным:

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

#assertPathIsNot

Проверяет, что путь текущего URL не совпадает с указанным:

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

Проверяет, что указанная ссылка присутствует на странице:

$browser->assertSeeLink($linkText);

Проверяет, что указанная ссылка отсутствует на странице:

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

#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-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>

// Component Definition...

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

#assertVueDoesntContain

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

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

#Pages

Иногда для тестов требуется выполнить несколько сложных действий последовательно. Это может усложнить чтение и понимание тестов. 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', 'taylor@laravel.com');

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

После установки Dusk в директории tests/Browser/Pages появится базовый класс Page. В нём есть метод 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');

#Components

Компоненты похожи на «объекты страниц» 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);
                });
    }
}

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

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

<?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. Поэтому перед продолжением убедитесь, что в вашей среде CI установлена переменная окружения 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, чтобы слушать запросы:

# файл .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 env файл, убедившись, что 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.