git 5cd91818357436e34b683c8750f1e72f73252cd3
- Введение
- Установка
- Начало работы
- Основы работы с браузером
- Взаимодействие с элементами
- Селекторы Dusk
- Текст, значения и атрибуты
- Взаимодействие с формами
- Прикрепление файлов
- Нажатие кнопок
- Кликанье по ссылкам
- Использование клавиатуры
- Использование мыши
- Диалоговые окна JavaScript (Alert, Prompt, Confirm)
- Сегментированное тестирование по селекторам
- Ожидание доступности элементов
- Прокрутка элемента в область видимости пользователя
- Доступные утверждения
- Тестовые страницы
- Компоненты для тестов
- Непрерывная интеграция
Laravel Dusk предлагает выразительный и простой в использовании API для автоматизации поведения браузера и тестирования. По умолчанию Dusk не требует установки JDK или Selenium на ваш локальный компьютер. Вместо этого Dusk использует автономную установку ChromeDriver. По желанию вы можете использовать любой другой драйвер, совместимый с Selenium.
Для начала установите laravel/dusk
с помощью менеджера пакетов Composer в свой проект:
composer require --dev laravel/dusk
{note} Если вы вручную регистрируете поставщика
DuskServiceProvider
, вам никогда не следует регистрировать его в рабочем окружении, так как это может привести к тому, что случайные пользователи смогут пройти аутентификацию в вашем приложении.
После установки пакета Dusk выполните команду dusk:install
Artisan. Команда dusk:install
создаст каталог tests/Browser
и пример теста Dusk:
php artisan dusk:install
Затем установите переменную окружения APP_URL
в файле .env
вашего приложения. Это значение должно соответствовать URL-адресу, который вы используете для доступа к вашему приложению в браузере.
{tip} Если вы используете Laravel Sail для управления своей локальной средой разработки, то обратитесь также к документации Sail по настройке и запуску тестов Dusk.
Если вы хотите установить версию ChromeDriver, отличную от той, которая включена в Laravel Dusk, то вы можете использовать команду 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
{note} Dusk требует, чтобы файлы
chromedriver
были доступны для выполнения. Если у вас возникли проблемы с запуском Dusk, то вы должны убедиться, что файлы доступны для выполнения, используя следующую команду:chmod -R 0755 vendor/laravel/dusk/bin/
.
По умолчанию Dusk использует Google Chrome и автономную установку ChromeDriver для запуска ваших браузерных тестов. Тем не менее, вы можете запустить свой собственный сервер Selenium и запускать тесты в любом браузере по желанию.
Для начала откройте файл tests/DuskTestCase.php
, который является базовым тестовым классом Dusk вашего приложения. Внутри этого файла вы можете удалить вызов метода startChromeDriver
. Это остановит Dusk от автоматического запуска ChromeDriver:
/**
* Подготовить Dusk для выполнения теста.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
Затем вы можете изменить метод driver
для подключения к URL-адресу и порту по вашему выбору. Кроме того, вы можете изменить «требуемые характеристики» через класс DesiredCapabilities
, передаваемые экземпляру WebDriver:
/**
* Создать экземпляр RemoteWebDriver.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'https://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}
Чтобы сгенерировать тест Dusk, используйте команду dusk:make
Artisan. Сгенерированный тест будет помещен в каталог tests/Browser
:
php artisan dusk:make LoginTest
Большинство тестов, которые вы пишете, будут взаимодействовать со страницами, которые извлекают данные из базы данных вашего приложения; однако в ваших тестах Dusk никогда не следует использовать трейт RefreshDatabase
. Трейт RefreshDatabase
использует транзакции базы данных, которые не будут доступны и следовательно применяться через HTTP-запросы. Вместо этого используйте трейт 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;
}
{note} Базы данных SQLite, хранимые в памяти, нельзя использовать при выполнении тестов Dusk. Поскольку браузер выполняет свой собственный процесс, он не сможет получить доступ к базам данных, хранимых в памяти, других процессов.
Чтобы запустить браузерные тесты, выполните команду dusk
Artisan:
php artisan dusk
Если при последнем запуске команды dusk
у вас были ошибки тестирования, то вы можете сэкономить время, повторно запустив сначала неудачные тесты с помощью команды dusk:fails
:
php artisan dusk:fails
Команда dusk
принимает любой аргумент, который обычно принимается тестером PHPUnit, например, позволяет вам запускать тесты только для указанной группы:
php artisan dusk --group=foo
{tip} Если вы используете Laravel Sail для управления своей локальной средой разработки, обратитесь к документации Sail по настройке и запуску тестов Dusk.
По умолчанию Dusk автоматически пытается запустить ChromeDriver. Если это не работает для вашей конкретной системы, вы можете вручную запустить ChromeDriver перед запуском команды dusk
. Если вы решили запустить ChromeDriver вручную, то вы должны закомментировать следующую строку вашего файла tests/DuskTestCase.php
:
/**
* Подготовить Dusk для выполнения теста.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
Кроме того, если вы запускаете ChromeDriver на порту, отличном от 9515
, то вам следует изменить метод driver
того же класса, чтобы указать необходимый порт:
/**
* Создать экземпляр RemoteWebDriver.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'https://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\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* Отвлеченный пример браузерного теста.
*
* @return void
*/
public function test_basic_example()
{
$user = User::factory()->create([
'email' => '[email protected]',
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
Как видно в приведенном выше примере, метод browse
принимает замыкание. Dusk автоматически передаст экземпляр браузера в это замыкание. Экземпляр браузера является основным объектом, используемым для взаимодействия с вашим приложением и создания утверждений.
Иногда для правильного проведения теста может потребоваться несколько браузеров. Например, для тестирования экрана чата, взаимодействующего с веб-сокетами, может потребоваться несколько браузеров. Чтобы создать несколько браузеров, просто добавьте больше аргументов браузера к сигнатуре замыкания, передаваемому методу browse
:
$this->browse(function ($first, $second) {
$first->loginAs(User::find(1))
->visit('/home')
->waitForText('Message');
$second->loginAs(User::find(2))
->visit('/home')
->waitForText('Message')
->type('message', 'Hey Taylor')
->press('Send');
$first->waitForText('Hey Taylor')
->assertSee('Jeffrey Way');
});
Метод 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.
*
* @return void
*/
public function boot()
{
Browser::macro('scrollToElement', function ($element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
return $this;
});
}
}
Метод macro
принимает имя в качестве первого аргумента и замыкание в качестве второго. Замыкание будет выполнено при вызове макрокоманды в качестве метода экземпляра Browser
:
$this->browse(function ($browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});
Часто вы будете тестировать страницы, требующие аутентификации. Вы можете использовать метод Dusk loginAs
, чтобы избежать взаимодействия с экраном входа в систему вашего приложения во время каждого теста. Метод loginAs
принимает первичный ключ аутентифицируемой модели или, непосредственно, экземпляр аутентифицируемой модели:
use App\Models\User;
$this->browse(function ($browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});
{note} После использования метода
loginAs
сессия пользователя будет поддерживаться для всех тестов, находящихся в файле.
Вы можете использовать метод cookie
для получения или установления зашифрованного значения cookie. По умолчанию все файлы cookie, созданные Laravel, зашифрованы:
$browser->cookie('name');
$browser->cookie('name', 'Taylor');
Вы можете использовать метод plainCookie
для получения или установления незашифрованного значения cookie:
$browser->plainCookie('name');
$browser->plainCookie('name', 'Taylor');
Вы можете использовать метод deleteCookie
для удаления конкретного файла cookie:
$browser->deleteCookie('name');
Вы можете использовать метод script
для выполнения произвольных выражений JavaScript в браузере:
$output = $browser->script('document.documentElement.scrollTop = 0');
$output = $browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
Вы можете использовать метод screenshot
, чтобы сделать снимок экрана и сохранить его с заданным именем файла. Все скриншоты будут храниться в каталоге tests/Browser/screenshots
:
$browser->screenshot('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. Чтобы определить селектор, добавьте к вашему элементу HTML-атрибут dusk
. Затем, при взаимодействии с браузером Dusk, добавьте к селектору префикс @
, чтобы управлять закрепленным элементом в вашем тесте:
// HTML-разметка ...
<button dusk="login-button">Login</button>
// Выполнение теста ...
$browser->click('@login-button');
Dusk содержит несколько методов для взаимодействия с текущим значением, отображаемым текстом и атрибутами элементов на странице. Например, чтобы получить «значение» элемента, которое соответствует указанному CSS или Dusk селектору, используйте метод value
:
// Получить значение ...
$value = $browser->value('selector');
// Установить значение ...
$browser->value('selector', 'value');
Вы можете использовать метод inputValue
для получения «значения» элемента ввода, имеющего указанное имя поля:
$value = $browser->inputValue('field');
Метод text
используется для получения отображаемого текста элемента, соответствующий указанному селектору:
$text = $browser->text('selector');
Наконец, метод attribute
может быть использован для получения значения атрибута элемента, соответствующий указанному селектору:
$attribute = $browser->attribute('selector', 'value');
Dusk содержит множество методов для взаимодействия с формами и элементами ввода. Во-первых, давайте взглянем на пример ввода текста в поле:
$browser->type('email', '[email protected]');
Обратите внимание, что, нам не требуется передавать селектор CSS в метод type
, хотя метод принимает его при необходимости. Если селектор CSS не указан, то Dusk будет искать поле input
или textarea
с указанным атрибутом name
.
Чтобы добавить текст в поле, не очищая его содержимое, вы можете использовать метод append
:
$browser->type('tags', 'foo')
->append('tags', ', bar, baz');
Вы можете очистить значение поля с помощью метода clear
:
$browser->clear('email');
Вы можете указать Dusk печатать медленно, используя метод typeSlowly
. По умолчанию Dusk будет делать паузу на 100
миллисекунд между нажатиями клавиш. Чтобы изменить время между нажатиями клавиш, вы можете передать соответствующее количество миллисекунд в качестве третьего аргумента метода:
$browser->typeSlowly('mobile', '+1 (202) 555-5555');
$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
Вы можете использовать метод appendSlowly
для медленного добавления текста:
$browser->type('tags', 'foo')
->appendSlowly('tags', ', bar, baz');
Чтобы выбрать значение, доступное для выпадающего списка, вы можете использовать метод select
. Как и метод type
, метод select
не требует полного селектора CSS. При передаче значения методу select
вы должны передать значение параметра value
вместо отображаемого текста:
$browser->select('size', 'Large');
Вы можете выбрать случайный вариант, опустив второй аргумент:
$browser->select('size');
Чтобы «отметить» флажок, вы можете использовать метод check
. Как и многие другие методы, связанные с вводом, полный селектор CSS не требуется. Если совпадение селектора CSS не найдено, то Dusk будет искать флажок с соответствующим атрибутом name
:
$browser->check('terms');
Метод uncheck
используется для «снятия галочки» с флажка:
$browser->uncheck('terms');
Чтобы «выбрать» вариант из радиокнопок, вы можете использовать метод radio
. Как и многие другие методы, связанные с вводом, полный селектор CSS не требуется. Если совпадение селектора CSS не найдено, то Dusk будет искать радиокнопку с соответствующими атрибутами name
и value
:
$browser->radio('size', 'large');
Метод attach
используется для прикрепления файла к элементу выбора файлов. Как и многие другие методы, связанные с вводом, полный селектор CSS не требуется. Если совпадение селектора CSS не найдено, то Dusk будет искать элемент выбора файлов с соответствующим атрибутом name
:
$browser->attach('photo', __DIR__.'/photos/mountains.png');
{note} Функционал прикрепления требует, чтобы на вашем сервере было установлено и включено расширение
Zip
PHP.
Метод press
используется для нажатия кнопки на странице. Первым аргументом, передаваемым методу press
, может быть либо отображаемый текст кнопки, либо CSS / Dusk селектор:
$browser->press('Login');
При отправке форм многие приложения отключают кнопку отправки формы после ее нажатия, а затем снова включают кнопку, когда HTTP-запрос отправки формы завершен. Чтобы нажать кнопку и дождаться ее повторного включения, вы можете использовать метод pressAndWaitFor
:
// Нажимаем кнопку и ждем ее активности не более 5 секунд ...
$browser->pressAndWaitFor('Save');
// Нажимаем кнопку и ждем ее активности не более 1 секунды ...
$browser->pressAndWaitFor('Save', 1);
Чтобы щелкнуть ссылку, вы можете использовать метод clickLink
экземпляра браузера. Метод clickLink
щелкнет ссылку с указанным видимым текстом:
$browser->clickLink($linkText);
Вы можете использовать метод seeLink
, чтобы определить, видна ли на странице ссылка с указанным видимым текстом:
if ($browser->seeLink($linkText)) {
// ...
}
{note} Эти методы взаимодействуют с библиотеками jQuery. Если jQuery недоступен на странице, то Dusk автоматически вставит его на страницу, чтобы он был доступен во время теста.
Метод keys
позволяет передавать более сложные последовательности ввода для указанного элемента, чем это обычно доступно при использовании метода type
. Например, при вводе значений можно поручить Dusk удерживать клавиши-модификаторы. В этом примере клавиша shift будет удерживаться, пока строка «taylor» вводится в элемент заданного селектора. После ввода «taylor», строка «swift» будет вводиться без модификаторов:
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
Другой значимый пример использования метода keys
– это отправка комбинации «горячих клавиш» основному селектору CSS вашего приложения:
$browser->keys('.app', ['{command}', 'j']);
{tip} Все модификаторы клавиш, такие как
{command}
заключены в символы{}
и соответствуют константам, определенным в классеFacebook\WebDriver\WebDriverKeys
, который можно найти на GitHub.
Метод 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();
Метод 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, нажав кнопку «ОК», вы можете вызвать метод acceptDialog
:
$browser->acceptDialog();
Чтобы закрыть открытое диалоговое окно JavaScript, нажав кнопку «Отмена», вы можете вызвать метод dismissDialog
:
$browser->dismissDialog();
Иногда требуется выполнить несколько операций, принадлежащих конкретному селектору. Например, вы можете утверждать, что некоторый текст существует только в таблице, а затем щелкнуть кнопку в этой таблице. Для этого можно использовать метод with
. Все операции, выполняемые в рамках замыкания, переданного методу with
, будут привязаны к исходному селектору:
$browser->with('.table', function ($table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});
Иногда требуется выполнить утверждения за пределами текущей области. Вы можете использовать для этого методы elsewhere
и elsewhereWhenAvailable
:
$browser->with('.table', function ($table) {
// Текущая область `body .table` ...
$browser->elsewhere('.page-title', function ($title) {
// Текущая область `body .page-title` ...
$title->assertSee('Hello World');
});
$browser->elsewhereWhenAvailable('.page-title', function ($title) {
// Текущая область `body .page-title` ...
$title->assertSee('Hello World');
});
});
При тестировании приложений, широко использующих JavaScript, часто возникает необходимость «подождать», пока не станут доступны определенные элементы или данные, прежде чем приступить к тесту. Dusk сделает это легко. Используя различные методы, вы можете подождать, пока элементы будут видны на странице, или даже дождаться, пока указанное выражение JavaScript не станет «истинным».
Если вам нужно просто приостановить тест на определенное количество миллисекунд, используйте метод pause
:
$browser->pause(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);
Иногда требуется дождаться появления элемента с указанным селектором, а затем взаимодействовать с этим элементом. Например, вы можете подождать, пока не станет доступно модальное окно, а затем нажать кнопку «ОК» в модальном окне. Для этого можно использовать метод whenAvailable
. Все операции с элементами, выполняемые в рамках замыкания, будут привязаны к исходному селектору:
$browser->whenAvailable('.modal', function ($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);
При утверждения пути, например, $browser->assertPathIs('/home')
, утверждение может завершиться ошибкой, если window.location.pathname
обновляется асинхронно. Вы можете использовать метод waitForLocation
, чтобы подождать, пока расположение не станет необходимым значением:
$browser->waitForLocation('/secret');
Вы также можете дождаться расположения именованного маршрута:
$browser->waitForRoute($routeName, $parameters);
Если вам нужно сделать утверждения после перезагрузки страницы, используйте метод waitForReload
:
$browser->click('.some-action')
->waitForReload()
->assertSee('something');
По желанию можно приостановить выполнение теста до тех пор, пока указанное выражение JavaScript не станет истинным. Вы можете легко сделать это, используя метод 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');
Многие из методов «ожидания» в 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
- assertRadioSelected
- assertRadioNotSelected
- assertSelected
- assertNotSelected
- assertSelectHasOptions
- assertSelectMissingOptions
- assertSelectHasOption
- assertSelectMissingOption
- assertValue
- assertAttribute
- assertAriaAttribute
- assertDataAttribute
- assertVisible
- assertPresent
- assertNotPresent
- assertMissing
- 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');
Утверждает, что переданный зашифрованный файл cookie присутствует:
$browser->assertHasCookie($name);
Утверждает, что переданный незашифрованный файл cookie присутствует:
$browser->assertHasPlainCookie($name);
Утверждает, что переданный зашифрованный файл cookie отсутствует:
$browser->assertCookieMissing($name);
Утверждает, что переданный незашифрованный файл cookie отсутствует:
$browser->assertPlainCookieMissing($name);
Утверждает, что зашифрованный файл cookie имеет указанное значение:
$browser->assertCookieValue($name, $value);
Утверждает, что незашифрованный файл cookie имеет указанное значение:
$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->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->assertAttribute($selector, $attribute, $value);
Утверждает, что элемент с указанным селектором, имеет переданное значение aria
-атрибута:
$browser->assertAriaAttribute($selector, $attribute, $value);
Например, учитывая разметку <button aria-label="Add"></button>
, вы можете выстроить утверждение относительно атрибута aria-label
следующим образом:
$browser->assertAriaAttribute('button', 'label', 'Add')
Утверждает, что элемент с указанным селектором, имеет переданное значение data
-атрибута:
$browser->assertDataAttribute($selector, $attribute, $value);
Например, учитывая разметку <tr id="row-1" data-content="attendees"></tr>
, вы можете выстроить утверждение относительно атрибута data-content
следующим образом:
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
Утверждает, что элемент с указанным селектором, видим:
$browser->assertVisible($selector);
Утверждает, что элемент с указанным селектором, присутствует:
$browser->assertPresent($selector);
Утверждает, что элемент с указанным селектором, отсутствует в исходном коде страницы:
$browser->assertNotPresent($selector);
Утверждает, что элемент с указанным селектором, не виден:
$browser->assertMissing($selector);
Утверждает, что был открыт диалог 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.
*
* @return void
*/
public function testVue()
{
$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 позволяют вам выразительно определять действия, которые затем могут быть выполнены на данной странице с помощью одного метода. Страницы также позволяют вам определять псевдонимы для общих селекторов всего приложения или отдельной страницы.
Чтобы сгенерировать класс страницы, выполните команду dusk:page
Artisan. Все классы страниц будут помещены в каталог tests/Browser/Pages
вашего приложения:
php artisan dusk:page Login
По умолчанию страницы имеют три метода: url
, assert
и elements
. Сейчас мы обсудим методы url
и assert
. Метод elements
будет более подробно описан ниже.
Метод url
должен возвращать путь URL-адреса, представляющего страницу. Dusk будет использовать этот URL-адрес при переходе на страницу в браузере:
/**
* Получить URL-адрес страницы.
*
* @return string
*/
public function url()
{
return '/login';
}
Метод assert
может делать любые утверждения, необходимые для подтверждения того, что браузер действительно находится на данной странице. На самом деле нет необходимости размещать что-либо в этом методе; однако вы можете сделать эти утверждения, если хотите. Эти утверждения будут запускаться автоматически при переходе на страницу:
/**
* Подтвердить, что браузер находится на странице.
*
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertPathIs($this->url());
}
После того, как страница определена, вы можете посетить ее с помощью метода visit
:
use Tests\Browser\Pages\Login;
$browser->visit(new Login);
Иногда, уже находясь на какой-либо странице, вам необходимо «загрузить» селекторы и методы страницы в текущий контекст теста. Это обычное явление, когда вы нажимаете кнопку и перенаправляетесь на указанную страницу без явного перехода к ней. В этой ситуации вы можете использовать метод on
для загрузки страницы:
use Tests\Browser\Pages\CreatePlaylist;
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');
Метод elements
внутри классов страниц позволяет вам определять быстрые, легко запоминающиеся псевдонимы для любого селектора CSS на вашей странице. Например, давайте определим псевдоним для поля ввода «электронная почта» на странице входа в приложение:
/**
* Получить псевдонимы элементов страницы.
*
* @return array
*/
public function elements()
{
return [
'@email' => 'input[name=email]',
];
}
После того, как псевдоним был определен, вы можете использовать сокращенный селектор в любом месте, где вы обычно используете полный селектор CSS:
$browser->type('@email', '[email protected]');
После установки Dusk базовый класс Page
будет помещен в ваш каталог tests/Browser/Pages
. Этот класс содержит метод siteElements
, который можно использовать для определения глобальных псевдонимов селекторов, которые должны быть доступны на каждой странице вашего приложения:
/**
* Получить глобальные псевдонимы элементов сайта.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
В дополнение к методам по умолчанию, определенным на тестовых страницах, вы можете определить дополнительные методы, которые могут использоваться в ваших тестах. Например, представим, что мы создаем приложение для управления музыкой. Обычным действием для одной страницы приложения может быть создание списка воспроизведения. Вместо того чтобы переписывать логику создания списка воспроизведения в каждом тесте, вы можете определить пользовательский метод createPlaylist
в классе страницы:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Dashboard extends Page
{
// Другие методы страницы ...
/**
* Создать новый список воспроизведения.
*
* @param \Laravel\Dusk\Browser $browser
* @param string $name
* @return void
*/
public function createPlaylist(Browser $browser, $name)
{
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
}
}
Как только метод определен, вы можете использовать его в любом тесте, использующем данную страницу. Экземпляр браузера будет автоматически внедрен в качестве первого аргумента пользовательским методам страницы:
use Tests\Browser\Pages\Dashboard;
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
Компоненты похожи на «классы страниц» Dusk, но предназначены для частей пользовательского интерфейса и функций, которые повторно используются в вашем приложении, таких как панель навигации или окно уведомлений. Таким образом, компоненты не привязаны к конкретным URL-адресам.
Чтобы сгенерировать компонент, выполните команду dusk:component
Artisan. Новые компоненты будут помещены в каталог tests/Browser/Components
:
php artisan dusk:component DatePicker
Компонент «выбора даты» является примером компонента, который может присутствовать в вашем приложении на различных страницах. Может оказаться обременительным вручную написать логику автоматизации браузера для выбора даты в десятках тестов. Вместо этого мы можем определить компонент Dusk для представления элемента выбора даты, что позволит нам инкапсулировать эту логику внутри компонента:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
{
/**
* Получить корневой селектор компонента.
*
* @return string
*/
public function selector()
{
return '.date-picker';
}
/**
* Подтвердить, что страница браузера содержит компонент.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertVisible($this->selector());
}
/**
* Получить псевдонимы элементов компонента.
*
* @return array
*/
public function elements()
{
return [
'@date-field' => 'input.datepicker-input',
'@year-list' => 'div > div.datepicker-years',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
];
}
/**
* Выбрать дату.
*
* @param \Laravel\Dusk\Browser $browser
* @param int $year
* @param int $month
* @param int $day
* @return void
*/
public function selectDate(Browser $browser, $year, $month, $day)
{
$browser->click('@date-field')
->within('@year-list', function ($browser) use ($year) {
$browser->click($year);
})
->within('@month-list', function ($browser) use ($month) {
$browser->click($month);
})
->within('@day-list', function ($browser) use ($day) {
$browser->click($day);
});
}
}
Как только компонент определен, мы можем легко выбрать дату с помощью элемента выбора даты из любого теста. И, если логика, необходимая для выбора даты, изменится, нам нужно будет только обновить компонент:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
/**
* Отвлеченный пример теста компонента.
*
* @return void
*/
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function ($browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
}
}
{note} Большинство конфигураций непрерывной интеграции Dusk предполагают, что ваше приложение Laravel будет обслуживаться с помощью встроенного сервера разработки PHP на порту
8000
. Поэтому, прежде чем продолжить, вы должны убедиться, что ваша среда непрерывной интеграции имеет значение переменной окруженияAPP_URL
, равноеhttps://127.0.0.1:8000
.
Чтобы запустить тесты Dusk на Heroku CI, добавьте следующий пакет сборки и скрипты Google Chrome в свой файл app.json
Heroku:
{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --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 --no-suggest
- php artisan key:generate
- php artisan dusk:chrome-driver
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 https://localhost &
- php artisan serve --no-reload &
script:
- php artisan dusk
Если вы используете Github Actions для запуска тестов Dusk, то вы можете использовать следующий конфигурационный файл в качестве отправной точки. Как и в случае с TravisCI, мы будем использовать команду php artisan serve
для запуска встроенного веб-сервера PHP:
name: CI
on: [push]
jobs:
dusk-php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- 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 --no-suggest --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1`
- 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
env:
APP_URL: "https://127.0.0.1:8000"
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