Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
Laravel Dusk предоставляет выразительный и простой в использовании API для автоматизации браузера и тестирования. По умолчанию Dusk не требует установки JDK или Selenium на вашем локальном компьютере. Вместо этого Dusk использует отдельную установку ChromeDriver. Однако вы свободны использовать любой другой совместимый с Selenium драйвер, который вы предпочтете.
Для начала вам следует установить Google Chrome и добавить зависимость Composer laravel/dusk
в ваш проект:
composer require --dev laravel/dusk
Внимание Если вы вручную регистрируете службу Dusk, вы никогда не должны регистрировать ее в производственной среде, поскольку это может привести к возможности аутентификации произвольных пользователей в вашем приложении.
После установки пакета Dusk выполните команду Artisan dusk:install
. Команда dusk:install
создаст каталог tests/Browser
, пример теста Dusk и установит двоичный файл Chrome Driver для вашей операционной системы:
php artisan dusk:install
Затем установите переменную окружения APP_URL
в файле .env
вашего приложения. Это значение должно соответствовать URL-адресу, который вы используете для доступа к вашему приложению в браузере.
Примечание Если вы используете Laravel Sail для управления локальной средой разработки, обратитесь также к документации Sail по настройке и запуску тестов Dusk.
Если вы хотите установить другую версию ChromeDriver, чем устанавливается Laravel Dusk с помощью команды dusk:install
, вы можете использовать команду dusk:chrome-driver
:
# Install the latest version of ChromeDriver for your OS...php artisan dusk:chrome-driver # Install a given version of ChromeDriver for your OS...php artisan dusk:chrome-driver 86 # Install a given version of ChromeDriver for all supported OSs...php artisan dusk:chrome-driver --all # Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...php artisan dusk:chrome-driver --detect
Внимание Для работы Dusk требуется, чтобы бинарные файлы
chromedriver
были исполняемыми. Если у вас возникли проблемы с запуском Dusk, убедитесь, что бинарные файлы исполняемы с помощью следующей команды:chmod -R 0755 vendor/laravel/dusk/bin/
.
По умолчанию Dusk использует Google Chrome и автономную установку ChromeDriver, чтобы запустить тесты браузера. Однако вы можете запустить свой собственный сервер Selenium и запускать тесты с любым браузером, который вы хотите.
Для начала откройте файл tests/DuskTestCase.php
, который является базовым тестовым случаем Dusk для вашего приложения. Внутри этого файла вы можете удалить вызов метода startChromeDriver
. Это предотвратит автоматический запуск ChromeDriver Dusk:
/** * Подготовка к выполнению тестов Dusk. * * @beforeClass */public static function prepare(): void{ // static::startChromeDriver();}
Затем вы можете изменить метод driver
, чтобы подключиться к URL и порту по вашему выбору. Кроме того, вы можете изменить "желаемые возможности" (desired capabilities), которые должны быть переданы в WebDriver:
use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Создание экземпляра RemoteWebDriver. */protected function driver(): RemoteWebDriver{ return RemoteWebDriver::create( 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() );}
Чтобы создать тест Dusk, используйте команду Artisan dusk:make
. Созданный тест будет помещен в каталог tests/Browser
:
php artisan dusk:make LoginTest
Большинство ваших тестов будут взаимодействовать с страницами, получающими данные из базы данных вашего приложения. Однако ваши тесты Dusk никогда не должны использовать трейт RefreshDatabase
. Трейт RefreshDatabase
использует базовые транзакции, которые не будут применяться или доступны через HTTP-запросы. Вместо этого у вас есть два варианта: трейт DatabaseMigrations
и трейт DatabaseTruncation
.
Трейт DatabaseMigrations
запускает миграции базы данных перед каждым тестом. Однако удаление и повторное создание таблиц базы данных для каждого теста обычно медленнее, чем обрезка таблиц:
<?php namespace Tests\Browser; use App\Models\User;use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Chrome;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseMigrations;}
Внимание В тестах Dusk не могут использоваться базы данных SQLite в памяти. Поскольку браузер выполняется в своем собственном процессе, он не сможет получить доступ к базам данных в памяти других процессов.
Перед использованием трейта DatabaseTruncation
вы должны установить пакет doctrine/dbal
с помощью менеджера пакетов Composer:
composer require --dev doctrine/dbal
Трейт DatabaseTruncation
будет мигрировать вашу базу данных на первом тесте, чтобы убедиться, что таблицы вашей базы данных были правильно созданы. Однако на последующих тестах таблицы базы данных будут просто обрезаны, что обеспечит ускорение по сравнению с повторным выполнением всех миграций базы данных:
<?php namespace Tests\Browser; use App\Models\User;use Illuminate\Foundation\Testing\DatabaseTruncation;use Laravel\Dusk\Chrome;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseTruncation;}
По умолчанию этот трейт будет обрезать все таблицы, кроме таблицы migrations
. Если вы хотите настроить таблицы, которые должны быть обрезаны, вы можете определить свойство $tablesToTruncate
в вашем классе теста:
/** * Указывает, какие таблицы следует обрезать. * * @var array */protected $tablesToTruncate = ['users'];
Кроме того, вы можете определить свойство $exceptTables
в вашем классе теста для указания таблиц, которые должны быть исключены из обрезки:
/** * Указывает, какие таблицы следует исключить из обрезки. * * @var array */protected $exceptTables = ['users'];
Чтобы указать соединения с базой данных, таблицы которых следует обрезать, вы можете определить свойство $connectionsToTruncate
в вашем классе теста:
/** * Указывает, какие соединения должны иметь свои таблицы обрезаны. * * @var array */protected $connectionsToTruncate = ['mysql'];
Если вы хотите выполнить код до или после обрезки базы данных, вы можете определить методы beforeTruncatingDatabase
или afterTruncatingDatabase
в вашем классе теста:
/** * Выполняет любую работу, которая должна произойти до начала обрезки базы данных. */protected function beforeTruncatingDatabase(): void{ //} /** * Выполняет любую работу, которая должна произойти после завершения обрезки базы данных. */protected function afterTruncatingDatabase(): void{ //}
Чтобы запустить ваши тесты браузера, выполните команду Artisan dusk
:
php artisan dusk
Если у вас были сбои в тестах при последнем запуске команды dusk
, вы можете сэкономить время, запустив сначала тесты, завершившиеся неудачей, с использованием команды dusk:fails
:
php artisan dusk:fails
Команда dusk
принимает любой аргумент, который обычно принимает тестовый раннер PHPUnit, такой как, например, позволяя вам запускать тесты только для заданной группы:
php artisan dusk --group=foo
Примечание Если вы используете Laravel Sail для управления локальной средой разработки, обратитесь к документации Sail по настройке и запуску тестов Dusk.
По умолчанию Dusk автоматически попытается запустить ChromeDriver. Если это не работает для вашей системы, вы можете вручную запустить ChromeDriver перед запуском команды dusk
. Если вы решите запустить ChromeDriver вручную, вы должны закомментировать следующую строку вашего файла tests/DuskTestCase.php
:
/** * Подготовка к выполнению тестов Dusk. * * @beforeClass */public static function prepare(): void{ // static::startChromeDriver();}
Кроме того, если вы запускаете ChromeDriver на порту, отличном от 9515, вы должны изменить метод driver
в том же классе, чтобы отразить правильный порт:
use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Создание экземпляра RemoteWebDriver. */protected function driver(): RemoteWebDriver{ return RemoteWebDriver::create( 'http://localhost:9515', DesiredCapabilities::chrome() );}
Чтобы заставить Dusk использовать свой собственный файл окружения при запуске тестов, создайте файл .env.dusk.{environment}
в корне вашего проекта. Например, если вы собираетесь запускать команду dusk
из вашего окружения local
, вы должны создать файл .env.dusk.local
.
При запуске тестов Dusk будет создана резервная копия вашего файла .env
, и окружение Dusk будет переименовано в .env
. После завершения тестов ваш файл .env
будет восстановлен.
Для начала давайте напишем тест, который проверит, что мы можем войти в наше приложение. После создания теста мы можем изменить его для перехода на страницу входа, ввода учетных данных и нажатия кнопки "Вход". Для создания экземпляра браузера вы можете вызвать метод browse
из вашего теста Dusk:
<?php namespace Tests\Browser; use App\Models\User;use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser;use Laravel\Dusk\Chrome;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ use DatabaseMigrations; /** * Простой пример теста браузера. */ public function test_basic_example(): void { $user = User::factory()->create([ ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); }); }}
Как видно в приведенном выше примере, метод browse
принимает замыкание. Экземпляр браузера будет автоматически передан в это замыкание Dusk и является основным объектом для взаимодействия и проверки утверждений в вашем приложении.
Иногда вам может потребоваться несколько браузеров для правильного выполнения теста. Например, для тестирования экрана чата, который взаимодействует с веб-сокетами, может потребоваться несколько браузеров. Чтобы создать несколько браузеров, просто добавьте больше аргументов браузера в сигнатуру замыкания, переданного методу browse
:
$this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) ->visit('/home') ->waitForText('Message'); $second->loginAs(User::find(2)) ->visit('/home') ->waitForText('Message') ->type('message', 'Hey Taylor') ->press('Send'); $first->waitForText('Hey Taylor') ->assertSee('Jeffrey Way');});
Метод visit
может использоваться для перехода к указанному URI внутри вашего приложения:
$browser->visit('/login');
Вы можете использовать метод visitRoute
для перехода к именованному маршруту:
$browser->visitRoute('login');
Вы можете перемещаться "назад" и "вперед" с использованием методов back
и forward
:
$browser->back(); $browser->forward();
Вы можете использовать метод refresh
для обновления страницы:
$browser->refresh();
Метод resize
может использоваться для изменения размера окна браузера:
$browser->resize(1920, 1080);
Метод maximize
может использоваться для максимизации окна браузера:
$browser->maximize();
Метод fitContent
изменит размер окна браузера, чтобы соответствовать размеру его содержимого:
$browser->fitContent();
Когда тест завершится неудачей, Dusk автоматически изменит размер браузера, чтобы соответствовать содержимому перед созданием снимка. Вы можете отключить эту функцию, вызвав метод disableFitOnFailure
в вашем тесте:
$browser->disableFitOnFailure();
Метод move
может использоваться для перемещения окна браузера в другое положение на вашем экране:
$browser->move($x = 100, $y = 100);
Если вы хотите определить пользовательский метод браузера, который вы можете использовать в различных ваших тестах, вы можете использовать метод macro
в классе Browser
. Обычно этот метод следует вызывать из метода boot
поставщика услуг:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider;use Laravel\Dusk\Browser; class DuskServiceProvider extends ServiceProvider{ /** * Регистрация макросов браузера Dusk. */ public function boot(): void { Browser::macro('scrollToElement', function (string $element = null) { $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); return $this; }); }}
Функция macro
принимает имя в качестве первого аргумента и замыкание в качестве второго. Замыкание макроса будет выполнено при вызове макроса в виде метода на экземпляре Browser
:
$this->browse(function (Browser $browser) use ($user) { $browser->visit('/pay') ->scrollToElement('#credit-card-details') ->assertSee('Enter Credit Card Details');});
Часто приходится тестировать страницы, требующие аутентификации. Вы можете использовать метод loginAs
Dusk, чтобы избежать взаимодействия с экраном входа в приложение в каждом тесте. Метод loginAs
принимает первичный ключ, связанный с вашей аутентифицируемой моделью, или экземпляр аутентифицируемой модели:
use App\Models\User;use Laravel\Dusk\Browser; $this->browse(function (Browser $browser) { $browser->loginAs(User::find(1)) ->visit('/home');});
Внимание После использования метода
loginAs
сеанс пользователя сохранится для всех тестов внутри файла.
Метод cookie
можно использовать для получения или установки значения зашифрованного куки. По умолчанию все куки, созданные Laravel, зашифрованы:
$browser->cookie('name'); $browser->cookie('name', 'Taylor');
Метод plainCookie
можно использовать для получения или установки значения незашифрованного куки:
$browser->plainCookie('name'); $browser->plainCookie('name', 'Taylor');
Метод deleteCookie
можно использовать для удаления указанного куки:
$browser->deleteCookie('name');
Метод 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');
Выбор хороших CSS-селекторов для взаимодействия с элементами - одна из самых сложных частей написания тестов в Dusk. С течением времени изменения на фронтенде могут вызвать сбой ваших тестов, если использовать такие CSS-селекторы, как в следующем примере:
// HTML... <button>Login</button> // Тест... $browser->click('.login-page .container div > button');
Селекторы Dusk позволяют вам сосредоточиться на написании эффективных тестов, а не на запоминании CSS-селекторов. Чтобы определить селектор, добавьте атрибут dusk
к вашему HTML-элементу. Затем при взаимодействии с браузером Dusk добавьте префикс @
к селектору, чтобы управлять прикрепленным элементом внутри вашего теста:
// HTML... <button dusk="login-button">Login</button> // Тест... $browser->click('@login-button');
При необходимости вы можете настроить атрибут HTML, который использует селектор Dusk, с помощью метода selectorHtmlAttribute
. Обычно этот метод следует вызывать из метода boot
вашего поставщика услуг приложения:
use Laravel\Dusk\Dusk; Dusk::selectorHtmlAttribute('data-dusk');
Dusk предоставляет несколько методов для взаимодействия с текущим значением, текстом отображения и атрибутами элементов на странице. Например, чтобы получить "значение" элемента, соответствующего заданному CSS- или Dusk-селектору, используйте метод value
:
// Получение значения...$value = $browser->value('selector'); // Установка значения...$browser->value('selector', 'value');
Метод inputValue
можно использовать для получения "значения" элемента ввода с заданным именем поля:
$value = $browser->inputValue('field');
Метод text
можно использовать для извлечения текста отображения элемента, соответствующего заданному селектору:
$text = $browser->text('selector');
Наконец, метод attribute
можно использовать для извлечения значения атрибута элемента, соответствующего заданному селектору:
$attribute = $browser->attribute('selector', 'value');
Dusk предоставляет разнообразные методы для взаимодействия с формами и элементами ввода. Давайте сначала рассмотрим пример ввода текста в поле ввода:
Обратите внимание, что, хотя метод принимает один, если это необходимо, мы не обязаны передавать CSS-селектор в метод type
. Если CSS-селектор не предоставлен, Dusk будет искать поле input
или textarea
с заданным атрибутом name
.
Чтобы добавить текст в поле, не очищая его содержимое, вы можете использовать метод append
:
$browser->type('tags', 'foo') ->append('tags', ', bar, baz');
Вы можете очистить значение ввода с помощью метода clear
:
$browser->clear('email');
Вы можете указать Dusk вводить текст медленно с использованием метода typeSlowly
. По умолчанию Dusk будет делать паузу в 100 миллисекунд между нажатиями клавиш. Чтобы настроить количество времени между нажатиями клавиш, вы можете передать соответствующее количество миллисекунд в качестве третьего аргумента методу:
$browser->typeSlowly('mobile', '+1 (202) 555-5555'); $browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
Метод appendSlowly
можно использовать для медленного добавления текста:
$browser->type('tags', 'foo') ->appendSlowly('tags', ', bar, baz');
Чтобы выбрать значение, доступное в элементе select
, вы можете использовать метод select
. Как и метод type
, метод select
не требует полного CSS-селектора. Передавая значение методу select
, вы должны передавать фактическое значение опции, а не отображаемый текст:
$browser->select('size', 'Large');
Вы можете выбрать случайную опцию, опустив второй аргумент:
$browser->select('size');
Предоставив массив в качестве второго аргумента методу select
, вы можете указать методу выбор нескольких опций:
$browser->select('categories', ['Art', 'Music']);
Чтобы "отметить" флажок ввода, вы можете использовать метод check
. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать флажок с соответствующим атрибутом name
:
$browser->check('terms');
Метод uncheck
можно использовать для "снятия отметки" с флажка ввода:
$browser->uncheck('terms');
Чтобы "выбрать" опцию ввода radio
, вы можете использовать метод radio
. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать ввод radio
с совпадающими атрибутами name
и value
:
$browser->radio('size', 'large');
Метод attach
можно использовать для прикрепления файла к элементу ввода file
. Как и во многих других методах ввода, полный CSS-селектор не требуется. Если совпадение с CSS-селектором не может быть найдено, Dusk будет искать ввод file
с совпадающим атрибутом name
:
$browser->attach('photo', __DIR__.'/photos/mountains.png');
Внимание Функция attach требует установленного и включенного расширения PHP
Zip
на вашем сервере.
Метод press
можно использовать для щелчка по кнопке на странице. Аргумент, передаваемый методу press
, может быть как отображаемым текстом кнопки, так и CSS- или Dusk-селектором:
$browser->press('Login');
При отправке форм многие приложения отключают кнопку отправки формы после ее нажатия, а затем снова включают кнопку, когда HTTP-запрос отправки формы завершен. Чтобы нажать кнопку и дождаться, когда кнопка снова будет включена, вы можете использовать метод pressAndWaitFor
:
// Нажатие кнопки и ожидание не более 5 секунд, чтобы она стала активной...$browser->pressAndWaitFor('Save'); // Нажатие кнопки и ожидание не более 1 секунды, чтобы она стала активной...$browser->pressAndWaitFor('Save', 1);
Чтобы нажать на ссылку, вы можете использовать метод clickLink
экземпляра браузера. Метод clickLink
будет нажимать на ссылку с заданным отображаемым текстом:
$browser->clickLink($linkText);
Метод seeLink
можно использовать для определения того, видна ли на странице ссылка с заданным отображаемым текстом:
if ($browser->seeLink($linkText)) { // ...}
Внимание Эти методы взаимодействуют с jQuery. Если jQuery отсутствует на странице, Dusk автоматически внедрит его на страницу, чтобы оно было доступно в течение длительности теста.
Метод keys
позволяет предоставить более сложные последовательности ввода для данного элемента, чем обычно позволяет метод type
. Например, вы можете указать Dusk удерживать модификаторы клавиш при вводе значений. В этом примере клавиша shift
будет удерживаться при вводе taylor
в элемент, соответствующий заданному селектору. После ввода taylor
будет введено swift
без удерживания модификаторов клавиш:
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
Другим полезным случаем использования метода keys
является отправка комбинации "горячих клавиш" в основной CSS-селектор вашего приложения:
$browser->keys('.app', ['{command}', 'j']);
Примечание Все модификаторы клавиш, такие как
{command}
, заключены в фигурные скобки{}
, и соответствуют константам, определенным в классеFacebook\WebDriver\WebDriverKeys
, которые можно найти на GitHub.
Если вы хотите определить пользовательские взаимодействия с клавиатурой, которые вы можете легко использовать повторно во всем вашем наборе тестов, вы можете использовать метод macro
, предоставленный классом Keyboard
. Обычно этот метод следует вызывать из метода boot
поставщика услуг:
use Laravel\Dusk\Keyboard; $browser->withKeyboard(function (Keyboard $keyboard) { $keyboard->press('c') ->pause(1000) ->release('c') ->type(['c', 'e', 'o']);});
Функция macro
принимает имя в качестве своего первого аргумента и замыкание в качестве второго. Замыкание макроса будет выполнено при вызове макроса в качестве метода экземпляра Keyboard
:
<?php namespace App\Providers; use Facebook\WebDriver\WebDriverKeys;use Illuminate\Support\ServiceProvider;use Laravel\Dusk\Keyboard;use Laravel\Dusk\OperatingSystem; class DuskServiceProvider extends ServiceProvider{ /** * Регистрация макросов браузера Dusk. */ public function boot(): void { Keyboard::macro('copy', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', ]); return $this; }); Keyboard::macro('paste', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', ]); return $this; }); }}
Метод macro
также предоставляет метод withKeyboard
, позволяющий легко выполнять сложные взаимодействия с клавиатурой с использованием класса Laravel\Dusk\Keyboard
. Класс Keyboard
предоставляет методы press
, release
, type
и pause
:
$browser->click('@textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) ->click('@another-textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());
Метод click
можно использовать для щелчка по элементу, соответствующему заданному CSS- или Dusk-селектору:
$browser->click('.selector');
Метод clickAtXPath
можно использовать для щелчка по элементу, соответствующему заданному выражению XPath:
$browser->clickAtXPath('//div[@class = "selector"]');
Метод clickAtPoint
можно использовать для щелчка по верхнему элементу по заданной паре координат относительно видимой области браузера:
$browser->clickAtPoint($x = 0, $y = 0);
Метод doubleClick
можно использовать для имитации двойного щелчка мышью:
$browser->doubleClick();
Метод rightClick
можно использовать для имитации правого щелчка мышью:
$browser->rightClick(); $browser->rightClick('.selector');
Метод clickAndHold
можно использовать для имитации щелчка мыши и удержания кнопки. Последующий вызов метода releaseMouse
отменит это поведение и освободит кнопку мыши:
$browser->clickAndHold() ->pause(1000) ->releaseMouse();
Метод controlClick
можно использовать для имитации события ctrl+click
в браузере:
$browser->controlClick();
Метод mouseover
можно использовать, когда вам нужно переместить мышь над элементом, соответствующим заданному CSS- или Dusk-селектору:
$browser->mouseover('.selector');
Метод drag
можно использовать для перетаскивания элемента, соответствующего заданному селектору, к другому элементу:
$browser->drag('.from-selector', '.to-selector');
Или вы можете перетащить элемент в одном направлении:
$browser->dragLeft('.selector', $pixels = 10);$browser->dragRight('.selector', $pixels = 10);$browser->dragUp('.selector', $pixels = 10);$browser->dragDown('.selector', $pixels = 10);
Наконец, вы можете перетащить элемент на заданное смещение:
$browser->dragOffset('.selector', $x = 10, $y = 10);
Dusk предоставляет различные методы для взаимодействия с диалоговыми окнами JavaScript. Например, вы можете использовать метод waitForDialog
, чтобы подождать, пока не появится диалог JavaScript. Этот метод принимает необязательный аргумент, указывающий, сколько секунд ждать появления диалога:
$browser->waitForDialog($seconds = null);
Метод assertDialogOpened
можно использовать для проверки того, что диалоговое окно отображено и содержит заданное сообщение:
$browser->assertDialogOpened('Dialog message');
Если диалоговое окно JavaScript содержит подсказку, вы можете использовать метод typeInDialog
для ввода значения в подсказку:
$browser->typeInDialog('Hello World');
Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку "OK", вы можете вызвать метод acceptDialog
:
$browser->acceptDialog();
Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку "Отмена", вы можете вызвать метод dismissDialog
:
$browser->dismissDialog();
Если вам нужно взаимодействовать с элементами внутри фрейма, вы можете использовать метод withinFrame
. Все взаимодействия с элементами, которые происходят в пределах замыкания, предоставленного методу withinFrame
, будут ограничены контекстом указанного фрейма:
$browser->withinFrame('#credit-card-details', function ($browser) { $browser->type('input[name="cardnumber"]', '4242424242424242') ->type('input[name="exp-date"]', '12/24') ->type('input[name="cvc"]', '123'); })->press('Pay');});
Иногда вам может потребоваться выполнить несколько операций, ограничив все операции в пределах заданного селектора. Например, вы можете захотеть проверить, что какой-то текст существует только в таблице, а затем щелкнуть кнопку в этой таблице. Вы можете использовать метод with
для достижения этого. Все операции, выполненные в пределах замыкания, предоставленного методу with
, будут ограничены исходным селектором:
$browser->with('.table', function (Browser $table) { $table->assertSee('Hello World') ->clickLink('Delete');});
Иногда вам может потребоваться выполнить утверждения вне текущего контекста. Вы можете использовать методы elsewhere
и elsewhereWhenAvailable
для достижения этого:
$browser->with('.table', function (Browser $table) { // Текущий контекст - `body .table`... $browser->elsewhere('.page-title', function (Browser $title) { // Текущий контекст - `body .page-title`... $title->assertSee('Hello World'); }); $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { // Текущий контекст - `body .page-title`... $title->assertSee('Hello World'); });});
При тестировании приложений, которые активно используют JavaScript, часто становится необходимым "ожидание" доступности определенных элементов или данных перед продолжением теста. Dusk упрощает это. Используя различные методы, вы можете ждать, пока элементы не станут видимыми на странице или даже дождаться, пока заданное выражение JavaScript не вычислится в true
.
Если вам просто нужно приостановить выполнение теста на определенное количество миллисекунд, используйте метод pause
:
$browser->pause(1000);
Если вам нужно приостановить выполнение теста только в том случае, если заданное условие равно true
, используйте метод pauseIf
:
$browser->pauseIf(App::environment('production'), 1000);
Точно так же, если вам нужно приостановить выполнение теста, если заданное условие не равно true
, вы можете использовать метод pauseUnless
:
$browser->pauseUnless(App::environment('testing'), 1000);
Метод waitFor
можно использовать для приостановки выполнения теста до тех пор, пока элемент, соответствующий заданному CSS- или Dusk-селектору, не будет отображен на странице. По умолчанию это приостановит выполнение теста не более чем на пять секунд перед вызовом исключения. При необходимости вы можете передать собственный порог тайм-аута в качестве второго аргумента методу:
// Ожидание не более пяти секунд для селектора...$browser->waitFor('.selector'); // Ожидание не более одной секунды для селектора...$browser->waitFor('.selector', 1);
Вы также можете ждать, пока элемент, соответствующий заданному селектору, не содержит заданный текст:
// Ждем не более пяти секунд, пока селектор не содержит указанный текст...$browser->waitForTextIn('.selector', 'Hello World'); // Ждем не более одной секунды, пока селектор не содержит указанный текст...$browser->waitForTextIn('.selector', 'Hello World', 1);
Вы также можете ждать, пока элемент, соответствующий заданному селектору, отсутствует на странице:
// Ждем не более пяти секунд, пока селектор не исчезнет...$browser->waitUntilMissing('.selector'); // Ждем не более одной секунды, пока селектор не исчезнет...$browser->waitUntilMissing('.selector', 1);
Или вы можете ждать, пока элемент, соответствующий заданному селектору, не станет доступным или недоступным:
// Ждем не более пяти секунд, пока селектор не станет активным...$browser->waitUntilEnabled('.selector'); // Ждем не более одной секунды, пока селектор не станет активным...$browser->waitUntilEnabled('.selector', 1); // Ждем не более пяти секунд, пока селектор не станет неактивным...$browser->waitUntilDisabled('.selector'); // Ждем не более одной секунды, пока селектор не станет неактивным...$browser->waitUntilDisabled('.selector', 1);
Иногда вам может потребоваться подождать, пока не появится элемент, соответствующий заданному селектору, и затем взаимодействовать с элементом. Например, вы можете подождать, пока не станет доступно модальное окно, а затем нажать кнопку "OK" внутри модального окна. Метод whenAvailable
можно использовать для достижения этого. Все операции с элементами, выполненные в пределах заданного замыкания, будут ограничены исходным селектором:
$browser->whenAvailable('.modal', function (Browser $modal) { $modal->assertSee('Hello World') ->press('OK');});
Метод waitForText
можно использовать, чтобы подождать, пока заданный текст не отобразится на странице:
// Ждем не более пяти секунд, чтобы увидеть текст...$browser->waitForText('Hello World'); // Ждем не более одной секунды, чтобы увидеть текст...$browser->waitForText('Hello World', 1);
Вы можете использовать метод waitUntilMissingText
для ожидания, пока отображаемый текст не исчезнет со страницы:
// Ждем не более пяти секунд, чтобы текст исчез...$browser->waitUntilMissingText('Hello World'); // Ждем не более одной секунды, чтобы текст исчез...$browser->waitUntilMissingText('Hello World', 1);
Метод waitForLink
можно использовать для ожидания, пока на странице не будет отображен указанный текст ссылки:
// Ждем не более пяти секунд, чтобы увидеть ссылку...$browser->waitForLink('Create'); // Ждем не более одной секунды, чтобы увидеть ссылку...$browser->waitForLink('Create', 1);
Метод waitForInput
можно использовать для ожидания, пока указанное поле ввода не станет видимым на странице:
// Ждем не более пяти секунд, чтобы увидеть ввод...$browser->waitForInput($field); // Ждем не более одной секунды, чтобы увидеть ввод...$browser->waitForInput($field, 1);
При выполнении проверки пути, такой как $browser->assertPathIs('/home')
, проверка может завершиться неудачно, если window.location.pathname
обновляется асинхронно. Вы можете использовать метод waitForLocation
для ожидания, чтобы расположение было равно заданному значению:
$browser->waitForLocation('/secret');
Метод waitForLocation
также может быть использован для ожидания того, чтобы текущее местоположение окна было полностью квалифицированным URL:
$browser->waitForLocation('https://example.com/path');
Вы также можете ждать расположение именованного маршрута:
$browser->waitForRoute($routeName, $parameters);
Если вам нужно дождаться перезагрузки страницы после выполнения действия, используйте метод waitForReload
:
use Laravel\Dusk\Browser; $browser->waitForReload(function (Browser $browser) { $browser->press('Submit');})->assertSee('Success!');
Поскольку необходимость дожидаться перезагрузки страницы обычно возникает после нажатия кнопки, вы можете использовать метод clickAndWaitForReload
для удобства:
$browser->clickAndWaitForReload('.selector') ->assertSee('something');
Иногда вам может потребоваться приостановить выполнение теста до тех пор, пока данное выражение JavaScript не станет true
. Это можно легко сделать с использованием метода waitUntil
. При передаче выражения в этот метод не нужно включать ключевое слово return
или конечную точку с запятой:
// Ждем не более пяти секунд, пока выражение не станет истинным...$browser->waitUntil('App.data.servers.length > 0'); // Ждем не более одной секунды, пока выражение не станет истинным...$browser->waitUntil('App.data.servers.length > 0', 1);
Методы waitUntilVue
и waitUntilVueIsNot
могут использоваться для ожидания, пока у Vue-компонента не будет задано указанное значение:
// Ждем, пока атрибут компонента не содержит указанное значение...$browser->waitUntilVue('user.name', 'Taylor', '@user'); // Ждем, пока атрибут компонента не содержит указанное значение...$browser->waitUntilVueIsNot('user.name', null, '@user');
Метод 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 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 assertAriaAttribute assertDataAttribute assertVisible assertPresent assertNotPresent assertMissing assertInputPresent assertInputMissing assertDialogOpened assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertFocused assertNotFocused assertAuthenticated assertGuest assertAuthenticatedAs assertVue assertVueIsNot assertVueContains assertVueDoesNotContain
Утверждать, что заголовок страницы соответствует заданному тексту:
$browser->assertTitle($title);
Утверждайте, что заголовок страницы содержит указанный текст:
$browser->assertTitleContains($title);
Утверждайте, что текущий URL (без строки запроса) соответствует указанной строке:
$browser->assertUrlIs($url);
Утверждайте, что текущая схема URL соответствует указанной схеме:
$browser->assertSchemeIs($scheme);
Утверждайте, что текущая схема URL не соответствует указанной схеме:
$browser->assertSchemeIsNot($scheme);
Утверждайте, что текущий хост URL соответствует указанному хосту:
$browser->assertHostIs($host);
Утверждайте, что текущий хост URL не соответствует указанному хосту:
$browser->assertHostIsNot($host);
Утверждайте, что текущий порт URL соответствует указанному порту:
$browser->assertPortIs($port);
Утверждайте, что текущий порт URL не соответствует указанному порту:
$browser->assertPortIsNot($port);
Утверждайте, что текущий путь URL начинается с указанного пути:
$browser->assertPathBeginsWith('/home');
Утверждайте, что текущий путь соответствует указанному пути:
$browser->assertPathIs('/home');
Утверждайте, что текущий путь не соответствует указанному пути:
$browser->assertPathIsNot('/home');
Утверждайте, что текущий URL соответствует URL именованного маршрута:
$browser->assertRouteIs($name, $parameters);
Утверждайте, что указанный параметр строки запроса присутствует:
$browser->assertQueryStringHas($name);
Утверждайте, что указанный параметр строки запроса присутствует и имеет заданное значение:
$browser->assertQueryStringHas($name, $value);
Утверждайте, что указанный параметр строки запроса отсутствует:
$browser->assertQueryStringMissing($name);
Утверждайте, что текущий фрагмент хеша URL соответствует указанному фрагменту:
$browser->assertFragmentIs('anchor');
Утверждайте, что текущий фрагмент хеша URL начинается с указанного фрагмента:
$browser->assertFragmentBeginsWith('anchor');
Утверждайте, что текущий фрагмент хеша URL не соответствует указанному фрагменту:
$browser->assertFragmentIsNot('anchor');
Утверждайте, что указанная зашифрованная куки присутствует:
$browser->assertHasCookie($name);
Утверждайте, что указанная не зашифрованная куки присутствует:
$browser->assertHasPlainCookie($name);
Утверждайте, что указанная зашифрованная куки отсутствует:
$browser->assertCookieMissing($name);
Утверждайте, что указанная не зашифрованная куки отсутствует:
$browser->assertPlainCookieMissing($name);
Утверждайте, что зашифрованная куки имеет заданное значение:
$browser->assertCookieValue($name, $value);
Утверждайте, что не зашифрованная куки имеет заданное значение:
$browser->assertPlainCookieValue($name, $value);
Утверждайте, что указанный текст присутствует на странице:
$browser->assertSee($text);
Утверждайте, что указанный текст отсутствует на странице:
$browser->assertDontSee($text);
Утверждайте, что указанный текст присутствует внутри селектора:
$browser->assertSeeIn($selector, $text);
Утверждайте, что указанный текст отсутствует внутри селектора:
$browser->assertDontSeeIn($selector, $text);
Утверждайте, что внутри селектора присутствует любой текст:
$browser->assertSeeAnythingIn($selector);
Утверждайте, что внутри селектора отсутствует любой текст:
$browser->assertSeeNothingIn($selector);
Утверждайте, что указанное JavaScript-выражение оценивается в указанное значение:
$browser->assertScript('window.isLoaded') ->assertScript('document.readyState', 'complete');
Утверждайте, что указанный исходный код присутствует на странице:
$browser->assertSourceHas($code);
Утверждайте, что указанный исходный код отсутствует на странице:
$browser->assertSourceMissing($code);
Утверждайте, что указанная ссылка присутствует на странице:
$browser->assertSeeLink($linkText);
Утверждайте, что указанная ссылка отсутствует на странице:
$browser->assertDontSeeLink($linkText);
Утверждайте, что у указанного поля ввода заданное значение:
$browser->assertInputValue($field, $value);
Утверждайте, что у указанного поля ввода нет заданного значения:
$browser->assertInputValueIsNot($field, $value);
Утверждайте, что указанный флажок установлен:
$browser->assertChecked($field);
Утверждайте, что указанный флажок не установлен:
$browser->assertNotChecked($field);
Утверждайте, что указанный флажок в нерешенном состоянии:
$browser->assertIndeterminate($field);
Утверждайте, что указанное поле радио выбрано:
$browser->assertRadioSelected($field, $value);
Утверждайте, что указанное поле радио не выбрано:
$browser->assertRadioNotSelected($field, $value);
Утверждайте, что у указанного выпадающего списка выбрано указанное значение:
$browser->assertSelected($field, $value);
Утверждайте, что у указанного выпадающего списка не выбрано указанное значение:
$browser->assertNotSelected($field, $value);
Утверждайте, что указанный массив значений доступен для выбора:
$browser->assertSelectHasOptions($field, $values);
Утверждайте, что указанный массив значений недоступен для выбора:
$browser->assertSelectMissingOptions($field, $values);
Утверждайте, что указанное значение доступно для выбора на указанном поле:
$browser->assertSelectHasOption($field, $value);
Утверждайте, что указанное значение недоступно для выбора:
$browser->assertSelectMissingOption($field, $value);
Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение:
$browser->assertValue($selector, $value);
Утверждайте, что элемент, соответствующий указанному селектору, не имеет указанного значения:
$browser->assertValueIsNot($selector, $value);
Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте:
$browser->assertAttribute($selector, $attribute, $value);
Утверждайте, что элемент, соответствующий указанному селектору, содержит указанное значение в указанном атрибуте:
$browser->assertAttributeContains($selector, $attribute, $value);
Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте aria:
$browser->assertAriaAttribute($selector, $attribute, $value);
Например, при данной разметке <button aria-label=\"Add\"></button>
вы можете утверждать против атрибута aria-label
следующим образом:
$browser->assertAriaAttribute('button', 'label', 'Add')
Утверждайте, что элемент, соответствующий указанному селектору, имеет указанное значение в указанном атрибуте данных:
$browser->assertDataAttribute($selector, $attribute, $value);
Например, при данной разметке <tr id=\"row-1\" data-content=\"attendees\"></tr>
вы можете утверждать против атрибута data-label
следующим образом:
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
Утверждайте, что элемент, соответствующий указанному селектору, видим:
$browser->assertVisible($selector);
Утверждайте, что элемент, соответствующий указанному селектору, присутствует в исходном коде:
$browser->assertPresent($selector);
Утверждайте, что элемент, соответствующий указанному селектору, отсутствует в исходном коде:
$browser->assertNotPresent($selector);
Утверждайте, что элемент, соответствующий указанному селектору, не видим:
$browser->assertMissing($selector);
Утверждайте, что присутствует поле ввода с указанным именем:
$browser->assertInputPresent($name);
Утверждайте, что поле ввода с указанным именем отсутствует в исходном коде:
$browser->assertInputMissing($name);
Утверждайте, что открыт диалог JavaScript с указанным сообщением:
$browser->assertDialogOpened($message);
Утверждайте, что указанное поле включено:
$browser->assertEnabled($field);
Утверждайте, что указанное поле отключено:
$browser->assertDisabled($field);
Утверждайте, что указанная кнопка включена:
$browser->assertButtonEnabled($button);
Утверждайте, что указанная кнопка отключена:
$browser->assertButtonDisabled($button);
Утверждайте, что указанное поле сфокусировано:
$browser->assertFocused($field);
Утверждайте, что указанное поле не сфокусировано:
$browser->assertNotFocused($field);
Утверждайте, что пользователь аутентифицирован:
$browser->assertAuthenticated();
Утверждайте, что пользователь не аутентифицирован:
$browser->assertGuest();
Утверждайте, что пользователь аутентифицирован как указанный пользователь:
$browser->assertAuthenticatedAs($user);
Dusk даже позволяет делать утверждения о состоянии данных Vue компонента. Например, представьте, что ваше приложение содержит следующий Vue компонент:
// HTML... <profile dusk="profile-component"></profile> // Определение компонента... Vue.component('profile', { template: '<div>{{ user.name }}</div>', data: function () { return { user: { name: 'Taylor' } }; }});
Вы можете утверждать о состоянии Vue компонента следующим образом:
/** * Простой пример теста Vue. */public function test_vue(): void{ $this->browse(function (Browser $browser) { $browser->visit('/') ->assertVue('user.name', 'Taylor', '@profile-component'); });}
Утверждайте, что указанное свойство данных Vue компонента не соответствует указанному значению:
$browser->assertVueIsNot($property, $value, $componentSelector = null);
Утверждайте, что указанное свойство данных Vue компонента является массивом и содержит указанное значение:
$browser->assertVueContains($property, $value, $componentSelector = null);
Утверждайте, что указанное свойство данных Vue компонента является массивом и не содержит указанное значение:
$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);
Иногда тесты требуют выполнения нескольких сложных действий последовательно. Это может сделать ваши тесты более сложными для чтения и понимания. Dusk Pages позволяют вам определять выразительные действия, которые затем могут быть выполнены на данной странице с помощью одного метода. Pages также позволяют вам определять ярлыки для общих селекторов для вашего приложения или для одной страницы.
Для создания объекта страницы выполните команду Artisan dusk:page
. Все объекты страниц будут помещены в каталог tests/Browser/Pages
вашего приложения:
php artisan dusk:page Login
По умолчанию у страниц есть три метода: url
, assert
и elements
. Мы обсудим методы url
и assert
сейчас. Метод elements
будет обсуждаться более подробно ниже.
url
Метод url
должен возвращать путь URL, представляющий страницу. Dusk будет использовать этот URL при переходе на страницу в браузере:
/** * Получение URL страницы. */public function url(): string{ return '/login';}
assert
Метод assert
может делать любые утверждения, необходимые для проверки того, что браузер действительно находится на указанной странице. Фактически, не обязательно что-то помещать в этот метод; однако вы вольны делать эти утверждения, если хотите. Эти утверждения будут выполняться автоматически при переходе на страницу:
/** * Проверка, что браузер открыт на странице. */public function assert(Browser $browser): void{ $browser->assertPathIs($this->url());}
После того как страница была определена, вы можете перейти на нее, используя метод visit
:
use Tests\Browser\Pages\Login; $browser->visit(new Login);
Иногда вы можете уже находиться на определенной странице и вам нужно «загрузить» селекторы и методы страницы в текущий контекст теста. Это обычно происходит при нажатии кнопки и перенаправлении на указанную страницу без явного перехода на нее. В этой ситуации вы можете использовать метод on
для загрузки страницы:
use Tests\Browser\Pages\CreatePlaylist; $browser->visit('/dashboard') ->clickLink('Create Playlist') ->on(new CreatePlaylist) ->assertSee('@create');
Метод elements
внутри классов страниц позволяет вам определять быстрые, легко запоминаемые ярлыки для любого CSS-селектора на вашей странице. Например, давайте определим ярлык для поля ввода «email» страницы входа в приложение:
/** * Получение ярлыков элементов для страницы. * * @return array<string, string> */public function elements(): array{ return [ '@email' => 'input[name=email]', ];}
После определения ярлыка вы можете использовать ярлык везде, где обычно использовали бы полный CSS-селектор:
После установки Dusk будет создан базовый класс Page
в каталоге tests/Browser/Pages
. Этот класс содержит метод siteElements
, который может быть использован для определения глобальных ярлыков селекторов, которые должны быть доступны на каждой странице в вашем приложении:
/** * Получение глобальных ярлыков элементов для сайта. * * @return array<string, string> */public static function siteElements(): array{ return [ '@element' => '#selector', ];}
Помимо методов, определенных по умолчанию на страницах, вы можете определить дополнительные методы, которые могут быть использованы во всех ваших тестах. Например, представьте, что вы создаете приложение для управления музыкой. Общим действием для одной страницы приложения может быть создание плейлиста. Вместо повторного написания логики создания плейлиста в каждом тесте вы можете определить метод createPlaylist
на странице:
<?php namespace Tests\Browser\Pages; use Laravel\Dusk\Browser; class Dashboard extends Page{ // Другие методы страницы... /** * Создание нового плейлиста. */ public function createPlaylist(Browser $browser, string $name): void { $browser->type('name', $name) ->check('share') ->press('Create Playlist'); }}
После того как метод был определен, вы можете использовать его в любом тесте, который использует страницу. Экземпляр браузера будет автоматически передан в качестве первого аргумента для пользовательских методов страницы:
use Tests\Browser\Pages\Dashboard; $browser->visit(new Dashboard) ->createPlaylist('My Playlist') ->assertSee('My Playlist');
Компоненты подобны “объектам страниц” в Dusk, но предназначены для частей интерфейса и функционала, которые повторно используются в вашем приложении, таких как панель навигации или окно уведомлений. Таким образом, компоненты не привязаны к конкретным URL.
Для создания компонента выполните команду Artisan dusk:component
. Новые компоненты помещаются в каталог tests/Browser/Components
:
php artisan dusk:component DatePicker
Как показано выше, "выбор даты" - это пример компонента, который может существовать в вашем приложении на различных страницах. Ручное написание логики автоматизации браузера для выбора даты в десятках тестов может быть неудобным. Вместо этого мы можем определить компонент Dusk, представляющий выбор даты, что позволит нам инкапсулировать эту логику внутри компонента:
<?php namespace Tests\Browser\Components; use Laravel\Dusk\Browser;use Laravel\Dusk\Component as BaseComponent; class DatePicker extends BaseComponent{ /** * Получение корневого селектора компонента. */ public function selector(): string { return '.date-picker'; } /** * Проверка, что страница браузера содержит компонент. */ public function assert(Browser $browser): void { $browser->assertVisible($this->selector()); } /** * Получение ярлыков элементов для компонента. * * @return array<string, string> */ public function elements(): array { return [ '@date-field' => 'input.datepicker-input', '@year-list' => 'div > div.datepicker-years', '@month-list' => 'div > div.datepicker-months', '@day-list' => 'div > div.datepicker-days', ]; } /** * Выбор указанной даты. */ public function selectDate(Browser $browser, int $year, int $month, int $day): void { $browser->click('@date-field') ->within('@year-list', function (Browser $browser) use ($year) { $browser->click($year); }) ->within('@month-list', function (Browser $browser) use ($month) { $browser->click($month); }) ->within('@day-list', function (Browser $browser) use ($day) { $browser->click($day); }); }}
После определения компонента мы легко можем выбрать дату в компоненте из любого теста. И если логика выбора даты изменится, нам нужно будет обновить только компонент:
<?php namespace Tests\Browser; use Illuminate\Foundation\Testing\DatabaseMigrations;use Laravel\Dusk\Browser;use Tests\Browser\Components\DatePicker;use Tests\DuskTestCase; class ExampleTest extends DuskTestCase{ /** * Простой пример теста компонента. */ public function test_basic_example(): void { $this->browse(function (Browser $browser) { $browser->visit('/') ->within(new DatePicker, function (Browser $browser) { $browser->selectDate(2019, 1, 30); }) ->assertSee('January'); }); }}
Внимание Большинство конфигураций непрерывной интеграции Dusk предполагают, что ваше приложение Laravel обслуживается с использованием встроенного веб-сервера PHP на порту 8000. Поэтому перед продолжением вы должны убедиться, что в вашей среде непрерывной интеграции установлено значение переменной среды
APP_URL
равнымhttp://127.0.0.1:8000
.
Чтобы запустить тесты 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" } } }}
Для запуска ваших тестов 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 для запуска ваших тестов Dusk, вы можете использовать следующий файл конфигурации как отправную точку. Как и в TravisCI, мы будем использовать команду php artisan serve
для запуска встроенного веб-сервера PHP:
name: CIon: [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 для запуска ваших тестов Dusk, вы можете использовать следующий файл конфигурации как отправную точку. Мы будем использовать встроенный сервер PHP для запуска Laravel, чтобы прослушивать запросы:
# file .chipperci.ymlversion: 1 environment: php: 8.2 node: 16 # Include Chrome in the build environmentservices: - dusk # Build all commitson: push: branches: .* pipeline: - name: Setup cmd: | cp -v .env.example .env composer install --no-interaction --prefer-dist --optimize-autoloader php artisan key:generate # Create a dusk env file, ensuring APP_URL uses BUILD_HOST cp -v .env .env.dusk.ci sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci - name: Compile Assets cmd: | npm ci --no-audit npm run build - name: Browser Tests cmd: | php -S [::0]:8000 -t public 2>server.log & sleep 2 php artisan dusk:chrome-driver $CHROME_DRIVER php artisan dusk --env=ci
Чтобы узнать больше о запуске тестов Dusk на Chipper CI, включая использование баз данных, проконсультируйтесь с официальной документацией Chipper CI.