Laravel Dusk
Introdução
Laravel Dusk fornece uma API de automação e teste de navegador expressiva e fácil de usar. Por padrão, o Dusk não exige que você instale o JDK ou o Selenium no seu computador local. Em vez disso, o Dusk usa uma instalação autônoma ChromeDriver. No entanto, você é livre para utilizar qualquer outro driver compatível com Selenium que desejar.
Instalação
Para começar, você deve instalar o Google Chrome e adicionar a dependência do Composer laravel/dusk
ao seu projeto:
composer require laravel/dusk --dev
AVISO
Se você estiver registrando manualmente o provedor de serviços do Dusk, você nunca deve registrá-lo em seu ambiente de produção, pois isso pode levar usuários arbitrários a conseguirem autenticar com seu aplicativo.
Após instalar o pacote Dusk, execute o comando Artisan dusk:install
. O comando dusk:install
criará um diretório tests/Browser
, um teste Dusk de exemplo e instalará o binário do Chrome Driver para seu sistema operacional:
php artisan dusk:install
Em seguida, defina a variável de ambiente APP_URL
no arquivo .env
do seu aplicativo. Este valor deve corresponder à URL que você usa para acessar seu aplicativo em um navegador.
NOTA
Se você estiver usando Laravel Sail para gerenciar seu ambiente de desenvolvimento local, consulte também a documentação do Sail sobre configuração e execução de testes Dusk.
Gerenciando instalações do ChromeDriver
Se você quiser instalar uma versão diferente do ChromeDriver do que a instalada pelo Laravel Dusk por meio do comando dusk:install
, você pode usar o comando dusk:chrome-driver
:
# Instale a versão mais recente do ChromeDriver para seu sistema operacional...
php artisan dusk:chrome-driver
# Instale uma determinada versão do ChromeDriver para seu sistema operacional...
php artisan dusk:chrome-driver 86
# Instalar uma determinada versão do ChromeDriver para todos os sistemas operacionais suportados...
php artisan dusk:chrome-driver --all
# Instale a versão do ChromeDriver que corresponde à versão detectada do Chrome/Chromium para seu sistema operacional...
php artisan dusk:chrome-driver --detect
AVISO
O Dusk requer que os binários chromedriver
sejam executáveis. Se você estiver tendo problemas para executar o Dusk, você deve garantir que os binários sejam executáveis usando o seguinte comando: chmod -R 0755 vendor/laravel/dusk/bin/
.
Usando outros navegadores
Por padrão, o Dusk usa o Google Chrome e uma instalação autônoma ChromeDriver para executar seus testes de navegador. No entanto, você pode iniciar seu próprio servidor Selenium e executar seus testes em qualquer navegador que desejar.
Para começar, abra seu arquivo tests/DuskTestCase.php
, que é o caso de teste base do Dusk para seu aplicativo. Dentro deste arquivo, você pode remover a chamada para o método startChromeDriver
. Isso impedirá que o Dusk inicie automaticamente o ChromeDriver:
/**
* Prepare-se para a execução do teste Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}
Em seguida, você pode modificar o método driver
para conectar-se à URL e porta de sua escolha. Além disso, você pode modificar os "recursos desejados" que devem ser passados para o WebDriver:
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Crie a instância RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}
Começando
Gerando testes
Para gerar um teste Dusk, use o comando Artisan dusk:make
. O teste gerado será colocado no diretório tests/Browser
:
php artisan dusk:make LoginTest
Redefinindo o banco de dados após cada teste
A maioria dos testes que você escreve interagirá com páginas que recuperam dados do banco de dados do seu aplicativo; no entanto, seus testes Dusk nunca devem usar o trait RefreshDatabase
. O trait RefreshDatabase
aproveita as transações do banco de dados que não serão aplicáveis ou disponíveis em solicitações HTTP. Em vez disso, você tem duas opções: o trait DatabaseMigrations
e o trait DatabaseTruncation
.
Usando migrações de banco de dados
O trait DatabaseMigrations
executará suas migrações de banco de dados antes de cada teste. No entanto, remover e recriar suas tabelas de banco de dados para cada teste é normalmente mais lento do que truncar as tabelas:
<?php
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
//
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
//
}
AVISO
Bancos de dados SQLite na memória não podem ser usados ao executar testes Dusk. Como o navegador é executado dentro de seu próprio processo, ele não poderá acessar os bancos de dados na memória de outros processos.
Usando truncamento de banco de dados
O trait DatabaseTruncation
migrará seu banco de dados no primeiro teste para garantir que suas tabelas de banco de dados tenham sido criadas corretamente. No entanto, em testes subsequentes, as tabelas do banco de dados serão simplesmente truncadas - proporcionando um aumento de velocidade em relação à reexecução de todas as migrações do seu banco de dados:
<?php
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
uses(DatabaseTruncation::class);
//
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseTruncation;
//
}
Por padrão, essa característica truncará todas as tabelas, exceto a tabela migrations
. Se você quiser personalizar as tabelas que devem ser truncadas, você pode definir uma propriedade $tablesToTruncate
na sua classe de teste:
NOTA
Se você estiver usando o Pest, você deve definir propriedades ou métodos na classe base DuskTestCase
ou em qualquer classe que seu arquivo de teste estenda.
/**
* Indica quais tabelas devem ser truncadas.
*
* @var array
*/
protected $tablesToTruncate = ['users'];
Alternativamente, você pode definir uma propriedade $exceptTables
na sua classe de teste para especificar quais tabelas devem ser excluídas do truncamento:
/**
* Indica quais tabelas devem ser excluídas do truncamento.
*
* @var array
*/
protected $exceptTables = ['users'];
Para especificar as conexões de banco de dados que devem ter suas tabelas truncadas, você pode definir uma propriedade $connectionsToTruncate
na sua classe de teste:
/**
* Indica quais conexões devem ter suas tabelas truncadas.
*
* @var array
*/
protected $connectionsToTruncate = ['mysql'];
Se você quiser executar o código antes ou depois do truncamento do banco de dados ser realizado, você pode definir os métodos beforeTruncatingDatabase
ou afterTruncatingDatabase
na sua classe de teste:
/**
* Execute qualquer trabalho que deva ser feito antes que o banco de dados comece a truncar.
*/
protected function beforeTruncatingDatabase(): void
{
//
}
/**
* Execute qualquer trabalho que deva ser feito depois que o banco de dados terminar de truncar.
*/
protected function afterTruncatingDatabase(): void
{
//
}
Executando testes
Para executar os testes do seu navegador, execute o comando Artisan dusk
:
php artisan dusk
Se você teve falhas de teste na última vez que executou o comando dusk
, você pode economizar tempo executando novamente os testes com falha primeiro usando o comando dusk:fails
:
php artisan dusk:fails
O comando dusk
aceita qualquer argumento que seja normalmente aceito pelo executor de teste Pest / PHPUnit, como permitir que você execute apenas os testes para um determinado grupo:
php artisan dusk --group=foo
NOTA
Se você estiver usando Laravel Sail para gerenciar seu ambiente de desenvolvimento local, consulte a documentação do Sail sobre configuração e execução de testes Dusk.
Iniciando manualmente o ChromeDriver
Por padrão, o Dusk tentará iniciar o ChromeDriver automaticamente. Se isso não funcionar para seu sistema específico, você pode iniciar o ChromeDriver manualmente antes de executar o comando dusk
. Se você escolher iniciar o ChromeDriver manualmente, você deve comentar a seguinte linha do seu arquivo tests/DuskTestCase.php
:
/**
* Prepare-se para a execução do teste Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}
Além disso, se você iniciar o ChromeDriver em uma porta diferente de 9515, você deve modificar o método driver
da mesma classe para refletir a porta correta:
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Crie a instância RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}
Manipulação de ambiente
Para forçar o Dusk a usar seu próprio arquivo de ambiente ao executar testes, crie um arquivo .env.dusk.{environment}
na raiz do seu projeto. Por exemplo, se você for iniciar o comando dusk
do seu ambiente local
, você deve criar um arquivo .env.dusk.local
.
Ao executar testes, o Dusk fará backup do seu arquivo .env
e renomeará seu ambiente Dusk para .env
. Após a conclusão dos testes, seu arquivo .env
será restaurado.
Noções básicas do navegador
Criando navegadores
Para começar, vamos escrever um teste que verifica se podemos fazer login em nosso aplicativo. Após gerar um teste, podemos modificá-lo para navegar até a página de login, inserir algumas credenciais e clicar no botão "Login". Para criar uma instância do navegador, você pode chamar o método browse
de dentro do seu teste Dusk:
<?php
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
uses(DatabaseMigrations::class);
test('basic example', function () {
$user = User::factory()->create([
'email' => 'taylor@laravel.com',
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
});
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* Um exemplo básico de teste de navegador.
*/
public function test_basic_example(): void
{
$user = User::factory()->create([
'email' => 'taylor@laravel.com',
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
Como você pode ver no exemplo acima, o método browse
aceita um fechamento. Uma instância do navegador será automaticamente passada para esse fechamento pelo Dusk e é o objeto principal usado para interagir e fazer afirmações contra seu aplicativo.
Criando vários navegadores
Às vezes, você pode precisar de vários navegadores para executar um teste corretamente. Por exemplo, vários navegadores podem ser necessários para testar uma tela de bate-papo que interage com websockets. Para criar vários navegadores, basta adicionar mais argumentos do navegador à assinatura do fechamento fornecido ao método 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');
});
Navegação
O método visit
pode ser usado para navegar para um URI fornecido dentro do seu aplicativo:
$browser->visit('/login');
Você pode usar o método visitRoute
para navegar para uma rota nomeada:
$browser->visitRoute('login');
Você pode navegar "para trás" e "para frente" usando os métodos back
e forward
:
$browser->back();
$browser->forward();
Você pode usar o método refresh
para atualizar a página:
$browser->refresh();
Redimensionando janelas do navegador
Você pode usar o método resize
para ajustar o tamanho da janela do navegador:
$browser->resize(1920, 1080);
O método maximize
pode ser usado para maximizar a janela do navegador:
$browser->maximize();
O método fitContent
redimensionará a janela do navegador para corresponder ao tamanho do seu conteúdo:
$browser->fitContent();
Quando um teste falha, o Dusk redimensiona automaticamente o navegador para ajustar o conteúdo antes de tirar uma captura de tela. Você pode desabilitar esse recurso chamando o método disableFitOnFailure
dentro do seu teste:
$browser->disableFitOnFailure();
Você pode usar o método move
para mover a janela do navegador para uma posição diferente na tela:
$browser->move($x = 100, $y = 100);
Macros do navegador
Se você quiser definir um método de navegador personalizado que possa ser reutilizado em uma variedade de seus testes, você pode usar o método macro
na classe Browser
. Normalmente, você deve chamar esse método do método boot
de um provedor de serviços:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
class DuskServiceProvider extends ServiceProvider
{
/**
* Registre as macros do navegador do Dusk.
*/
public function boot(): void
{
Browser::macro('scrollToElement', function (string $element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
return $this;
});
}
}
A função macro
aceita um nome como seu primeiro argumento e um fechamento como seu segundo. O fechamento da macro será executado ao chamar a macro como um método em uma instância Browser
:
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});
Autenticação
Frequentemente, você testará páginas que exigem autenticação. Você pode usar o método loginAs
do Dusk para evitar interagir com a tela de login do seu aplicativo durante cada teste. O método loginAs
aceita uma chave primária associada ao seu modelo autenticável ou a uma instância de modelo autenticável:
use App\Models\User;
use Laravel\Dusk\Browser;
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});
AVISO
Após usar o método loginAs
, a sessão do usuário será mantida para todos os testes dentro do arquivo.
Cookies
Você pode usar o método cookie
para obter ou definir o valor de um cookie criptografado. Por padrão, todos os cookies criados pelo Laravel são criptografados:
$browser->cookie('name');
$browser->cookie('name', 'Taylor');
Você pode usar o método plainCookie
para obter ou definir o valor de um cookie não criptografado:
$browser->plainCookie('name');
$browser->plainCookie('name', 'Taylor');
Você pode usar o método deleteCookie
para excluir o cookie fornecido:
$browser->deleteCookie('name');
Executando JavaScript
Você pode usar o método script
para executar instruções JavaScript arbitrárias dentro do navegador:
$browser->script('document.documentElement.scrollTop = 0');
$browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
$output = $browser->script('return window.location.pathname');
Tirando uma captura de tela
Você pode usar o método screenshot
para tirar uma captura de tela e armazená-la com o nome de arquivo fornecido. Todas as capturas de tela serão armazenadas no diretório tests/Browser/screenshots
:
$browser->screenshot('filename');
O método responsiveScreenshots
pode ser usado para tirar uma série de capturas de tela em vários pontos de interrupção:
$browser->responsiveScreenshots('filename');
O método screenshotElement
pode ser usado para tirar uma captura de tela de um elemento específico na página:
$browser->screenshotElement('#selector', 'filename');
Armazenando a saída do console no disco
Você pode usar o método storeConsoleLog
para gravar a saída do console do navegador atual no disco com o nome de arquivo fornecido. A saída do console será armazenada no diretório tests/Browser/console
:
$browser->storeConsoleLog('filename');
Armazenando o código-fonte da página no disco
Você pode usar o método storeSource
para gravar o código-fonte da página atual no disco com o nome de arquivo fornecido. O código-fonte da página será armazenado no diretório tests/Browser/source
:
$browser->storeSource('filename');
Interagindo com elementos
Seletores Dusk
Escolher bons seletores CSS para interagir com elementos é uma das partes mais difíceis de escrever testes Dusk. Com o tempo, mudanças no frontend podem fazer com que seletores CSS como os seguintes quebrem seus testes:
// HTML...
<button>Login</button>
// Teste...
$browser->click('.login-page .container div > button');
Os seletores Dusk permitem que você se concentre em escrever testes eficazes em vez de lembrar dos seletores CSS. Para definir um seletor, adicione um atributo dusk
ao seu elemento HTML. Então, ao interagir com um navegador Dusk, prefixe o seletor com @
para manipular o elemento anexado dentro do seu teste:
// HTML...
<button dusk="login-button">Login</button>
// Teste...
$browser->click('@login-button');
Se desejar, você pode personalizar o atributo HTML que o seletor Dusk utiliza por meio do método selectorHtmlAttribute
. Normalmente, esse método deve ser chamado do método boot
do AppServiceProvider
do seu aplicativo:
use Laravel\Dusk\Dusk;
Dusk::selectorHtmlAttribute('data-dusk');
Texto, valores e atributos
Recuperando e definindo valores
O Dusk fornece vários métodos para interagir com o valor atual, texto de exibição e atributos de elementos na página. Por exemplo, para obter o "valor" de um elemento que corresponde a um determinado seletor CSS ou Dusk, use o método value
:
// Retrieve the value...
$value = $browser->value('selector');
// Set the value...
$browser->value('selector', 'value');
Você pode usar o método inputValue
para obter o "valor" de um elemento de entrada que tem um determinado nome de campo:
$value = $browser->inputValue('field');
Recuperando texto
O método text
pode ser usado para recuperar o texto de exibição de um elemento que corresponde ao seletor fornecido:
$text = $browser->text('selector');
Recuperando atributos
Finalmente, o método attribute
pode ser usado para recuperar o valor de um atributo de um elemento que corresponde ao seletor fornecido:
$attribute = $browser->attribute('selector', 'value');
Interagindo com formulários
Digitando valores
O Dusk fornece uma variedade de métodos para interagir com formulários e elementos de entrada. Primeiro, vamos dar uma olhada em um exemplo de digitação de texto em um campo de entrada:
$browser->type('email', 'taylor@laravel.com');
Observe que, embora o método aceite um se necessário, não somos obrigados a passar um seletor CSS para o método type
. Se um seletor CSS não for fornecido, o Dusk pesquisará um campo input
ou textarea
com o atributo name
fornecido.
Para anexar texto a um campo sem limpar seu conteúdo, você pode usar o método append
:
$browser->type('tags', 'foo')
->append('tags', ', bar, baz');
Você pode limpar o valor de uma entrada usando o método clear
:
$browser->clear('email');
Você pode instruir o Dusk a digitar lentamente usando o método typeSlowly
. Por padrão, o Dusk pausará por 100 milissegundos entre os pressionamentos de tecla. Para personalizar a quantidade de tempo entre os pressionamentos de tecla, você pode passar o número apropriado de milissegundos como o terceiro argumento para o método:
$browser->typeSlowly('mobile', '+1 (202) 555-5555');
$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
Você pode usar o método appendSlowly
para anexar texto lentamente:
$browser->type('tags', 'foo')
->appendSlowly('tags', ', bar, baz');
Dropdowns
Para selecionar um valor disponível em um elemento select
, você pode usar o método select
. Assim como o método type
, o método select
não requer um seletor CSS completo. Ao passar um valor para o método select
, você deve passar o valor da opção subjacente em vez do texto de exibição:
$browser->select('size', 'Large');
Você pode selecionar uma opção aleatória omitindo o segundo argumento:
$browser->select('size');
Ao fornecer uma matriz como o segundo argumento para o método select
, você pode instruir o método a selecionar várias opções:
$browser->select('categories', ['Art', 'Music']);
Caixas de seleção
Para "marcar" uma entrada de caixa de seleção, você pode usar o método check
. Como muitos outros métodos relacionados a entrada, um seletor CSS completo não é necessário. Se uma correspondência de seletor CSS não puder ser encontrada, o Dusk procurará uma caixa de seleção com um atributo name
correspondente:
$browser->check('terms');
O método uncheck
pode ser usado para "desmarcar" uma entrada de caixa de seleção:
$browser->uncheck('terms');
Botões de opção
Para "selecionar" uma opção de entrada radio
, você pode usar o método radio
. Como muitos outros métodos relacionados a entrada, um seletor CSS completo não é necessário. Se uma correspondência de seletor CSS não puder ser encontrada, o Dusk procurará uma entrada radio
com atributos name
e value
correspondentes:
$browser->radio('size', 'large');
Anexando arquivos
O método attach
pode ser usado para anexar um arquivo a um elemento de entrada file
. Como muitos outros métodos relacionados a entrada, um seletor CSS completo não é necessário. Se uma correspondência de seletor CSS não puder ser encontrada, o Dusk pesquisará uma entrada file
com um atributo name
correspondente:
$browser->attach('photo', __DIR__.'/photos/mountains.png');
ATENÇÃO
A função attach
requer que a extensão PHP Zip
esteja instalada e habilitada no seu servidor.
Pressionando botões
O método press
pode ser usado para clicar em um elemento de botão na página. O argumento fornecido ao método press
pode ser o texto de exibição do botão ou um seletor CSS/Dusk:
$browser->press('Login');
Ao enviar formulários, muitos aplicativos desabilitam o botão de envio do formulário após ele ser pressionado e, em seguida, reabilitam o botão quando a solicitação HTTP do envio do formulário é concluída. Para pressionar um botão e esperar que ele seja reativado, você pode usar o método pressAndWaitFor
:
// Pressione o botão e aguarde no máximo 5 segundos para que ele seja habilitado...
$browser->pressAndWaitFor('Save');
// Pressione o botão e aguarde no máximo 1 segundo para que ele seja habilitado...
$browser->pressAndWaitFor('Save', 1);
Clicando em Links
Para clicar em um link, você pode usar o método clickLink
na instância do navegador. O método clickLink
clicará no link que tem o texto de exibição fornecido:
$browser->clickLink($linkText);
Você pode usar o método seeLink
para determinar se um link com o texto de exibição fornecido está visível na página:
if ($browser->seeLink($linkText)) {
// ...
}
AVISO
Esses métodos interagem com o jQuery. Se o jQuery não estiver disponível na página, o Dusk o injetará automaticamente na página para que fique disponível durante o teste.
Usando o teclado
O método keys
permite que você forneça sequências de entrada mais complexas para um dado elemento do que normalmente permitido pelo método type
. Por exemplo, você pode instruir o Dusk a segurar teclas modificadoras enquanto insere valores. Neste exemplo, a tecla shift
será segurada enquanto taylor
for inserido no elemento que corresponde ao seletor dado. Depois que taylor
for digitado, swift
será digitado sem nenhuma tecla modificadora:
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
Outro caso de uso valioso para o método keys
é enviar uma combinação de "atalho de teclado" para o seletor CSS primário do seu aplicativo:
$browser->keys('.app', ['{command}', 'j']);
NOTA
Todas as teclas modificadoras, como {command}
, são encapsuladas em caracteres {}
e correspondem às constantes definidas na classe Facebook\WebDriver\WebDriverKeys
, que pode ser encontrada no GitHub.
Interações Fluentes de Teclado
Dusk também fornece um método withKeyboard
, permitindo que você execute interações complexas de teclado fluentemente por meio da classe Laravel\Dusk\Keyboard
. A classe Keyboard
fornece os métodos press
, release
, type
e pause
:
use Laravel\Dusk\Keyboard;
$browser->withKeyboard(function (Keyboard $keyboard) {
$keyboard->press('c')
->pause(1000)
->release('c')
->type(['c', 'e', 'o']);
});
Macros de Teclado
Se você quiser definir interações de teclado personalizadas que você pode reutilizar facilmente em todo o seu conjunto de testes, você pode usar o método macro
fornecido pela classe Keyboard
. Normalmente, você deve chamar este método a partir do método boot
de um provedor de serviços:
<?php
namespace App\Providers;
use Facebook\WebDriver\WebDriverKeys;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Keyboard;
use Laravel\Dusk\OperatingSystem;
class DuskServiceProvider extends ServiceProvider
{
/**
* Registre as macros do navegador do 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;
});
}
}
A função macro
aceita um nome como seu primeiro argumento e um encerramento como seu segundo. O fechamento da macro será executado ao chamar a macro como um método em uma instância Keyboard
:
$browser->click('@textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy())
->click('@another-textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());
Usando o Mouse
Clicando em Elementos
O método click
pode ser usado para clicar em um elemento que corresponda ao seletor CSS ou Dusk fornecido:
$browser->click('.selector');
O método clickAtXPath
pode ser usado para clicar em um elemento que corresponda à expressão XPath fornecida:
$browser->clickAtXPath('//div[@class = "selector"]');
O método clickAtPoint
pode ser usado para clicar no elemento mais alto em um determinado par de coordenadas relativas à área visível do navegador:
$browser->clickAtPoint($x = 0, $y = 0);
O método doubleClick
pode ser usado para simular o clique duplo de um mouse:
$browser->doubleClick();
$browser->doubleClick('.selector');
O método rightClick
pode ser usado para simular o clique direito de um mouse:
$browser->rightClick();
$browser->rightClick('.selector');
O método clickAndHold
pode ser usado para simular um botão do mouse sendo clicado e mantido pressionado. Uma chamada subsequente ao método releaseMouse
desfará esse comportamento e liberará o botão do mouse:
$browser->clickAndHold('.selector');
$browser->clickAndHold()
->pause(1000)
->releaseMouse();
O método controlClick
pode ser usado para simular o evento ctrl+click
dentro do navegador:
$browser->controlClick();
$browser->controlClick('.selector');
Mouseover
O método mouseover
pode ser usado quando você precisa mover o mouse sobre um elemento que corresponde ao seletor CSS ou Dusk fornecido:
$browser->mouseover('.selector');
Arrastar e soltar
O método drag
pode ser usado para arrastar um elemento que corresponde ao seletor fornecido para outro elemento:
$browser->drag('.from-selector', '.to-selector');
Ou você pode arrastar um elemento em um único direction:
$browser->dragLeft('.selector', $pixels = 10);
$browser->dragRight('.selector', $pixels = 10);
$browser->dragUp('.selector', $pixels = 10);
$browser->dragDown('.selector', $pixels = 10);
Finalmente, você pode arrastar um elemento por um deslocamento dado:
$browser->dragOffset('.selector', $x = 10, $y = 10);
Diálogos JavaScript
O Dusk fornece vários métodos para interagir com Diálogos JavaScript. Por exemplo, você pode usar o método waitForDialog
para esperar que um diálogo JavaScript apareça. Este método aceita um argumento opcional indicando quantos segundos esperar para que o diálogo apareça:
$browser->waitForDialog($seconds = null);
O método assertDialogOpened
pode ser usado para afirmar que um diálogo foi exibido e contém a mensagem fornecida:
$browser->assertDialogOpened('Dialog message');
Se o diálogo JavaScript contiver um prompt, você pode usar o método typeInDialog
para digitar um valor no prompt:
$browser->typeInDialog('Hello World');
Para fechar um diálogo JavaScript aberto clicando no botão "OK", você pode invocar o método acceptDialog
:
$browser->acceptDialog();
Para fechar um diálogo JavaScript aberto clicando no botão "Cancelar", você pode invocar o método dismissDialog
:
$browser->dismissDialog();
Interagindo com quadros embutidos
Se você precisa interagir com elementos dentro de um iframe, você pode usar o método withinFrame
. Todas as interações de elementos que ocorrem dentro do fechamento fornecido ao método withinFrame
serão delimitadas para o contexto do iframe especificado:
$browser->withinFrame('#credit-card-details', function ($browser) {
$browser->type('input[name="cardnumber"]', '4242424242424242')
->type('input[name="exp-date"]', '1224')
->type('input[name="cvc"]', '123')
->press('Pay');
});
Escopo de seletores
Às vezes você pode querer executar várias operações enquanto delimita todas as operações dentro de um determinado seletor. Por exemplo, você pode querer afirmar que algum texto existe somente dentro de uma tabela e então clicar em um botão dentro dessa tabela. Você pode usar o método with
para fazer isso. Todas as operações realizadas dentro do fechamento dado ao método with
serão delimitadas para o seletor original:
$browser->with('.table', function (Browser $table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});
Você pode ocasionalmente precisar executar asserções fora do escopo atual. Você pode usar os métodos elsewhere
e elsewhereWhenAvailable
para fazer isso:
$browser->with('.table', function (Browser $table) {
// O escopo atual é `body .table`...
$browser->elsewhere('.page-title', function (Browser $title) {
// O escopo atual é `body .page-title`...
$title->assertSee('Hello World');
});
$browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
// O escopo atual é `body .page-title`...
$title->assertSee('Hello World');
});
});
Aguardando Elementos
Ao testar aplicativos que usam JavaScript extensivamente, muitas vezes se torna necessário "esperar" que certos elementos ou dados estejam disponíveis antes de prosseguir com um teste. O Dusk torna isso moleza. Usando uma variedade de métodos, você pode esperar que os elementos se tornem visíveis na página ou até mesmo esperar até que uma determinada expressão JavaScript seja avaliada como true
.
Aguardando
Se você só precisa pausar o teste por um número determinado de milissegundos, use o método pause
:
$browser->pause(1000);
Se você precisa pausar o teste somente se uma condição dada for true
, use o método pauseIf
:
$browser->pauseIf(App::environment('production'), 1000);
Da mesma forma, se você precisa pausar o teste a menos que uma condição dada seja true
, você pode usar o método pauseUnless
:
$browser->pauseUnless(App::environment('testing'), 1000);
Aguardando seletores
O método waitFor
pode ser usado para pausar a execução do teste até que o elemento correspondente ao seletor CSS ou Dusk dado seja exibido na página. Por padrão, isso pausará o teste por no máximo cinco segundos antes de lançar uma exceção. Se necessário, você pode passar um limite de tempo limite personalizado como o segundo argumento para o método:
// Aguarde no máximo cinco segundos para o seletor...
$browser->waitFor('.selector');
// Aguarde no máximo um segundo para o seletor...
$browser->waitFor('.selector', 1);
Você também pode esperar até que o elemento correspondente ao seletor fornecido contenha o texto fornecido:
// Aguarde no máximo cinco segundos para que o seletor contenha o texto fornecido...
$browser->waitForTextIn('.selector', 'Hello World');
// Aguarde no máximo um segundo para que o seletor contenha o texto fornecido...
$browser->waitForTextIn('.selector', 'Hello World', 1);
Você também pode esperar até que o elemento correspondente ao seletor fornecido esteja ausente da página:
// Aguarde no máximo cinco segundos até que o seletor desapareça...
$browser->waitUntilMissing('.selector');
// Aguarde no máximo um segundo até que o seletor desapareça...
$browser->waitUntilMissing('.selector', 1);
Ou você pode esperar até que o elemento correspondente ao seletor fornecido seja habilitado ou desabilitado:
// Aguarde no máximo cinco segundos até que o seletor seja habilitado...
$browser->waitUntilEnabled('.selector');
// Aguarde no máximo um segundo até que o seletor seja habilitado...
$browser->waitUntilEnabled('.selector', 1);
// Aguarde no máximo cinco segundos até que o seletor seja desabilitado...
$browser->waitUntilDisabled('.selector');
// Aguarde no máximo um segundo até que o seletor seja desabilitado...
$browser->waitUntilDisabled('.selector', 1);
Escopo de seletores quando disponíveis
Ocasionalmente, você pode desejar esperar que um elemento apareça que corresponda a um determinado seletor e então interagir com o elemento. Por exemplo, você pode desejar esperar até que uma janela modal esteja disponível e então pressionar o botão "OK" dentro do modal. O método whenAvailable
pode ser usado para fazer isso. Todas as operações de elemento realizadas dentro do fechamento fornecido serão delimitadas para o seletor original:
$browser->whenAvailable('.modal', function (Browser $modal) {
$modal->assertSee('Hello World')
->press('OK');
});
Aguardando texto
O método waitForText
pode ser usado para esperar até que o texto fornecido seja exibido na página:
// Aguarde no máximo cinco segundos pelo texto...
$browser->waitForText('Hello World');
// Aguarde no máximo um segundo pelo texto...
$browser->waitForText('Hello World', 1);
Você pode usar o método waitUntilMissingText
para esperar até que o texto exibido seja removido da página:
// Aguarde no máximo cinco segundos para que o texto seja removido...
$browser->waitUntilMissingText('Hello World');
// Aguarde no máximo um segundo para que o texto seja removido...
$browser->waitUntilMissingText('Hello World', 1);
Aguardando links
O método waitForLink
pode ser usado para esperar até que o texto do link fornecido seja exibido na página:
// Aguarde no máximo cinco segundos pelo link...
$browser->waitForLink('Create');
// Aguarde no máximo um segundo pelo link...
$browser->waitForLink('Create', 1);
Aguardando entradas
O método waitForInput
pode ser usado para esperar até que o campo de entrada fornecido esteja visível na página:
// Aguarde no máximo cinco segundos pela entrada...
$browser->waitForInput($field);
// Aguarde no máximo um segundo pela entrada...
$browser->waitForInput($field, 1);
Aguardando a localização da página
Ao fazer uma asserção de caminho como $browser->assertPathIs('/home')
, a asserção pode falhar se window.location.pathname
estiver sendo atualizado de forma assíncrona. Você pode usar o método waitForLocation
para esperar que o local seja um valor fornecido:
$browser->waitForLocation('/secret');
O método waitForLocation
também pode ser usado para esperar que o local da janela atual seja uma URL totalmente qualificada:
$browser->waitForLocation('https://example.com/path');
Você também pode esperar pelo local de uma rota nomeada:
$browser->waitForRoute($routeName, $parameters);
Aguardando recarregamentos de página
Se você precisar esperar que uma página seja recarregada após executar uma ação, use o método waitForReload
:
use Laravel\Dusk\Browser;
$browser->waitForReload(function (Browser $browser) {
$browser->press('Submit');
})
->assertSee('Success!');
Como a necessidade de esperar que a página seja recarregada normalmente ocorre após clicar em um botão, você pode usar o Método clickAndWaitForReload
para conveniência:
$browser->clickAndWaitForReload('.selector')
->assertSee('something');
Aguardando Expressões JavaScript
Às vezes, você pode querer pausar a execução de um teste até que uma dada expressão JavaScript seja avaliada como true
. Você pode facilmente fazer isso usando o método waitUntil
. Ao passar uma expressão para este método, você não precisa incluir a palavra-chave return
ou um ponto e vírgula final:
// Aguarde no máximo cinco segundos para que a expressão seja verdadeira...
$browser->waitUntil('App.data.servers.length > 0');
// Aguarde no máximo um segundo para que a expressão seja verdadeira...
$browser->waitUntil('App.data.servers.length > 0', 1);
Aguardando expressões Vue
Os métodos waitUntilVue
e waitUntilVueIsNot
podem ser usados para esperar até que um atributo componente Vue tenha um valor fornecido:
// Aguarde até que o atributo do componente contenha o valor fornecido...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
// Aguarde até que o atributo do componente não contenha o valor fornecido...
$browser->waitUntilVueIsNot('user.name', null, '@user');
Aguardando eventos JavaScript
O método waitForEvent
pode ser usado para pausar a execução de um teste até que um evento JavaScript ocorra:
$browser->waitForEvent('load');
O ouvinte de eventos é anexado ao escopo atual, que é o elemento body
por padrão. Ao usar um seletor com escopo, o ouvinte de eventos será anexado ao elemento correspondente:
$browser->with('iframe', function (Browser $iframe) {
// Wait for the iframe's load event...
$iframe->waitForEvent('load');
});
Você também pode fornecer um seletor como o segundo argumento para o método waitForEvent
para anexar o ouvinte de eventos a um elemento específico:
$browser->waitForEvent('load', '.selector');
Você também pode esperar por eventos nos objetos document
e window
:
// Aguarde até que o documento seja rolado...
$browser->waitForEvent('scroll', 'document');
// Aguarde no máximo cinco segundos até que a janela seja redimensionada...
$browser->waitForEvent('resize', 'window', 5);
Esperando com um retorno de chamada
Muitos dos métodos "wait" no Dusk dependem do método subjacente waitUsing
. Você pode usar este método diretamente para esperar que um determinado fechamento retorne true
. O método waitUsing
aceita o número máximo de segundos para esperar, o intervalo no qual o fechamento deve ser avaliado, o fechamento e uma mensagem de falha opcional:
$browser->waitUsing(10, 1, function () use ($something) {
return $something->isReady();
}, "Something wasn't ready in time.");
Rolando um elemento para a visualização
Às vezes, você pode não conseguir clicar em um elemento porque ele está fora da área visível do navegador. O método scrollIntoView
rolará a janela do navegador até que o elemento no seletor fornecido esteja dentro da visualização:
$browser->scrollIntoView('.selector')
->click('.selector');
Asserções disponíveis
O Dusk fornece uma variedade de asserções que você pode fazer em seu aplicativo. Todas as asserções disponíveis estão documentadas na lista abaixo:
assertTitleassertTitleContainsassertUrlIsassertSchemeIsassertSchemeIsNotassertHostIsassertHostIsNotassertPortIsassertPortIsNotassertPathBeginsWithassertPathEndsWithassertPathContainsassertPathIsassertPathIsNotassertRouteIsassertQueryStringHasassertQueryStringMissingassertFragmentIsassertFragmentBeginsWithassertFragmentIsNotassertHasCookieassertHasPlainCookieassertCookieMissingassertPlainCookieMissingassertCookieValueassertPlainCookieValueassertSeeassertDontSeeassertSeeInassertDontSeeInassertSeeAnythingInassertSeeNothingInassertScriptassertSourceHasassertSourceMissingassertSeeLinkassertDontSeeLinkassertInputValueassertInputValueIsNotassertCheckedassertNotCheckedassertIndeterminateassertRadioSelectedassertRadioNotSelectedassertSelectedassertNotSelectedassertSelectHasOptionsassertSelectMissingOptionsassertSelectHasOptionassertSelectMissingOptionassertValueassertValueIsNotassertAttributeassertAttributeContainsassertAttributeDoesntContainassertAriaAttributeassertDataAttributeassertVisibleassertPresentassertNotPresentassertMissingassertInputPresentassertInputMissingassertDialogOpenedassertEnabledassertDisabledassertButtonEnabledassertButtonDisabledassertFocusedassertNotFocusedassertAuthenticatedassertGuestassertAuthenticatedAsassertVueassertVueIsNotassertVueContainsassertVueDoesntContain
assertTitle
Afirme que o título da página corresponde ao texto fornecido:
$browser->assertTitle($title);
assertTitleContains
Afirme que o título da página contém o texto fornecido:
$browser->assertTitleContains($title);
assertUrlIs
Afirme que a URL atual (sem a string de consulta) corresponde à string fornecida:
$browser->assertUrlIs($url);
assertSchemeIs
Afirme que o esquema de URL atual corresponde ao esquema fornecido:
$browser->assertSchemeIs($scheme);
assertSchemeIsNot
Declara que o esquema de URL atual não corresponde ao esquema fornecido:
$browser->assertSchemeIsNot($scheme);
assertHostIs
Declara que o host de URL atual corresponde ao host fornecido:
$browser->assertHostIs($host);
assertHostIsNot
Declara que o host de URL atual não corresponde ao host fornecido:
$browser->assertHostIsNot($host);
assertPortIs
Declara que a porta de URL atual corresponde à porta fornecida:
$browser->assertPortIs($port);
assertPortIsNot
Declara que a porta atual da URL não corresponde à porta fornecida:
$browser->assertPortIsNot($port);
assertPathBeginsWith
Declara que o caminho atual da URL começa com o caminho fornecido:
$browser->assertPathBeginsWith('/home');
assertPathEndsWith
Declara que o caminho atual da URL termina com o caminho fornecido:
$browser->assertPathEndsWith('/home');
assertPathContains
Declara que o caminho atual da URL contém o caminho fornecido caminho:
$browser->assertPathContains('/home');
assertPathIs
Declara que o caminho atual corresponde ao caminho fornecido:
$browser->assertPathIs('/home');
assertPathIsNot
Declara que o caminho atual não corresponde ao caminho fornecido:
$browser->assertPathIsNot('/home');
assertRouteIs
Declara que a URL atual corresponde à URL da rota nomeada fornecida:
$browser->assertRouteIs($name, $parameters);
assertQueryStringHas
Afirme que o parâmetro de string de consulta fornecido está presente:
$browser->assertQueryStringHas($name);
Afirme que o parâmetro de string de consulta fornecido está presente e tem um valor fornecido:
$browser->assertQueryStringHas($name, $value);
assertQueryStringMissing
Afirme que o parâmetro de string de consulta fornecido está ausente:
$browser->assertQueryStringMissing($name);
assertFragmentIs
Afirme que o fragmento de hash atual da URL corresponde ao fragmento fornecido:
$browser->assertFragmentIs('anchor');
assertFragmentBeginsWith
Afirme que o parâmetro de string de consulta atual da URL fragmento hash começa com o fragmento fornecido:
$browser->assertFragmentBeginsWith('anchor');
assertFragmentIsNot
Declara que o fragmento hash atual da URL não corresponde ao fragmento fornecido:
$browser->assertFragmentIsNot('anchor');
assertHasCookie
Declara que o cookie criptografado fornecido está presente:
$browser->assertHasCookie($name);
assertHasPlainCookie
Declara que o cookie não criptografado fornecido está presente:
$browser->assertHasPlainCookie($name);
assertCookieMissing
Declara que o cookie criptografado fornecido não está presente:
$browser->assertCookieMissing($name);
assertPlainCookieMissing
Declara que o cookie não criptografado fornecido não está presente:
$browser->assertPlainCookieMissing($name);
assertCookieValue
Declara que um cookie criptografado tem um valor fornecido:
$browser->assertCookieValue($name, $value);
assertPlainCookieValue
Declara que um cookie não criptografado tem um valor fornecido:
$browser->assertPlainCookieValue($name, $value);
assertSee
Declara que o texto fornecido está presente na página:
$browser->assertSee($text);
assertDontSee
Declara que o texto fornecido não está presente na página:
$browser->assertDontSee($text);
assertSeeIn
Declara que o texto fornecido está presente no seletor:
$browser->assertSeeIn($selector, $text);
assertDontSeeIn
Declara que o texto fornecido não está presente no seletor:
$browser->assertDontSeeIn($selector, $text);
assertSeeAnythingIn
Declara que qualquer texto está presente no seletor:
$browser->assertSeeAnythingIn($selector);
assertSeeNothingIn
Declara que nenhum texto está presente no seletor:
$browser->assertSeeNothingIn($selector);
assertScript
Declara que a expressão JavaScript fornecida é avaliada como o valor fornecido:
$browser->assertScript('window.isLoaded')
->assertScript('document.readyState', 'complete');
assertSourceHas
Declara que o código-fonte fornecido está presente na página:
$browser->assertSourceHas($code);
assertSourceMissing
Declara que o código-fonte fornecido não está presente na página:
$browser->assertSourceMissing($code);
assertSeeLink
Afirme que o link fornecido está presente na página:
$browser->assertSeeLink($linkText);
assertDontSeeLink
Afirme que o link fornecido não está presente na página:
$browser->assertDontSeeLink($linkText);
assertInputValue
Afirme que o campo de entrada fornecido tem o valor fornecido:
$browser->assertInputValue($field, $value);
assertInputValueIsNot
Afirme que o campo de entrada fornecido não tem o valor fornecido:
$browser->assertInputValueIsNot($field, $value);
assertChecked
Declara que a caixa de seleção fornecida está marcada:
$browser->assertChecked($field);
assertNotChecked
Declara que a caixa de seleção fornecida não está marcada:
$browser->assertNotChecked($field);
assertIndeterminate
Declara que a caixa de seleção fornecida está em um estado indeterminado:
$browser->assertIndeterminate($field);
assertRadioSelected
Declara que o campo de rádio fornecido está selecionado:
$browser->assertRadioSelected($field, $value);
assertRadioNotSelected
Declara que o campo de rádio fornecido não está selecionado:
$browser->assertRadioNotSelected($field, $value);
assertSelected
Declara que o menu suspenso fornecido tem o valor fornecido selecionado:
$browser->assertSelected($field, $value);
assertNotSelected
Declara que o menu suspenso fornecido não tem o valor fornecido selecionado:
$browser->assertNotSelected($field, $value);
assertSelectHasOptions
Declara que a matriz de valores fornecida está disponível para ser selecionada:
$browser->assertSelectHasOptions($field, $values);
assertSelectMissingOptions
Afirme que a matriz de valores fornecida não está disponível para seleção:
$browser->assertSelectMissingOptions($field, $values);
assertSelectHasOption
Afirme que o valor fornecido está disponível para seleção no campo fornecido:
$browser->assertSelectHasOption($field, $value);
assertSelectMissingOption
Afirme que o valor fornecido não está disponível para seleção:
$browser->assertSelectMissingOption($field, $value);
assertValue
Afirme que o elemento que corresponde ao seletor fornecido tem o valor:
$browser->assertValue($selector, $value);
assertValueIsNot
Afirma que o elemento que corresponde ao seletor fornecido não tem o valor fornecido:
$browser->assertValueIsNot($selector, $value);
assertAttribute
Afirma que o elemento que corresponde ao seletor fornecido tem o valor fornecido no atributo fornecido:
$browser->assertAttribute($selector, $attribute, $value);
assertAttributeContains
Afirme que o elemento que corresponde ao seletor fornecido contém o valor fornecido no atributo fornecido:
$browser->assertAttributeContains($selector, $attribute, $value);
assertAttributeDoesntContain
Afirme que o elemento que corresponde ao seletor fornecido não contém o valor fornecido no atributo fornecido:
$browser->assertAttributeDoesntContain($selector, $attribute, $value);
assertAriaAttribute
Afirme que o elemento que corresponde ao seletor fornecido tem o valor fornecido no atributo aria fornecido:
$browser->assertAriaAttribute($selector, $attribute, $value);
Por exemplo, dada a marcação <button aria-label="Add"></button>
, você pode afirmar contra o atributo aria-label
como então:
$browser->assertAriaAttribute('button', 'label', 'Add')
assertDataAttribute
Afirme que o elemento que corresponde ao seletor fornecido tem o valor fornecido no atributo de dados fornecido:
$browser->assertDataAttribute($selector, $attribute, $value);
Por exemplo, dada a marcação <tr id="row-1" data-content="attendees"></tr>
, você pode afirmar contra o atributo data-label
assim:
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
assertVisible
Afirme que o elemento que corresponde ao seletor fornecido é visível:
$browser->assertVisible($selector);
assertPresent
Afirme que o elemento que corresponde ao seletor fornecido está presente em a fonte:
$browser->assertPresent($selector);
assertNotPresent
Afirma que o elemento que corresponde ao seletor fornecido não está presente na fonte:
$browser->assertNotPresent($selector);
assertMissing
Afirma que o elemento que corresponde ao seletor fornecido não está visível:
$browser->assertMissing($selector);
assertInputPresent
Afirma que uma entrada com o nome fornecido está presente:
$browser->assertInputPresent($name);
assertInputMissing
Afirma que uma entrada com o nome fornecido não está presente na fonte:
$browser->assertInputMissing($name);
assertDialogOpened
Declara que um diálogo JavaScript com a mensagem fornecida foi aberto:
$browser->assertDialogOpened($message);
assertEnabled
Declara que o campo fornecido está habilitado:
$browser->assertEnabled($field);
assertDisabled
Declara que o campo fornecido está desabilitado:
$browser->assertDisabled($field);
assertButtonEnabled
Declara que o botão fornecido está habilitado:
$browser->assertButtonEnabled($button);
assertButtonDisabled
Declara que o botão fornecido está desabilitado:
$browser->assertButtonDisabled($button);
assertFocused
Declara que o campo fornecido está focado:
$browser->assertFocused($field);
assertNotFocused
Declara que o campo fornecido não está focado:
$browser->assertNotFocused($field);
assertAuthenticated
Declara que o usuário está autenticado:
$browser->assertAuthenticated();
assertGuest
Declara que o usuário não está autenticado:
$browser->assertGuest();
assertAuthenticatedAs
Declara que o usuário está autenticado como o usuário fornecido:
$browser->assertAuthenticatedAs($user);
assertVue
O Dusk ainda permite que você faça afirmações sobre o estado dos dados do componente Vue. Por exemplo, imagine que seu aplicativo contém o seguinte componente Vue:
// HTML...
<profile dusk="profile-component"></profile>
// Component Definition...
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
data: function () {
return {
user: {
name: 'Taylor'
}
};
}
});
Você pode afirmar o estado do componente Vue assim:
test('vue', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
});
/**
* Um exemplo básico de teste do Vue.
*/
public function test_vue(): void
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}
assertVueIsNot
Afirme que uma determinada propriedade de dados do componente Vue não corresponde ao valor fornecido:
$browser->assertVueIsNot($property, $value, $componentSelector = null);
assertVueContains
Afirme que uma determinada propriedade de dados do componente Vue é uma matriz e contém o valor fornecido:
$browser->assertVueContains($property, $value, $componentSelector = null);
assertVueDoesntContain
Afirma que uma dada propriedade de dados do componente Vue é uma matriz e não contém o valor dado:
$browser->assertVueDoesntContain($property, $value, $componentSelector = null);
Páginas
Às vezes, os testes exigem que várias ações complicadas sejam executadas em sequência. Isso pode tornar seus testes mais difíceis de ler e entender. As Páginas Dusk permitem que você defina ações expressivas que podem ser executadas em uma determinada página por meio de um único método. As Páginas também permitem que você defina atalhos para seletores comuns para seu aplicativo ou para uma única página.
Gerando Páginas
Para gerar um objeto de página, execute o comando Artisan dusk:page
. Todos os objetos de página serão colocados no diretório tests/Browser/Pages
do seu aplicativo:
php artisan dusk:page Login
Configurando páginas
Por padrão, as páginas têm três métodos: url
, assert
e elements
. Discutiremos os métodos url
e assert
agora. O método elements
será discutido em mais detalhes abaixo.
O método url
O método url
deve retornar o caminho da URL que representa a página. O Dusk usará esta URL ao navegar para a página no navegador:
/**
* Obtenha o URL da página.
*/
public function url(): string
{
return '/login';
}
O método assert
O método assert
pode fazer quaisquer asserções necessárias para verificar se o navegador está realmente na página fornecida. Na verdade, não é necessário colocar nada dentro deste método; no entanto, você é livre para fazer essas asserções se desejar. Essas asserções serão executadas automaticamente ao navegar para a página:
/**
* Afirme que o navegador está na página.
*/
public function assert(Browser $browser): void
{
$browser->assertPathIs($this->url());
}
Navegando para páginas
Depois que uma página for definida, você pode navegar até ela usando o método visit
:
use Tests\Browser\Pages\Login;
$browser->visit(new Login);
Às vezes, você pode já estar em uma determinada página e precisar "carregar" os seletores e métodos da página no contexto de teste atual. Isso é comum ao pressionar um botão e ser redirecionado para uma determinada página sem navegar explicitamente para ela. Nessa situação, você pode usar o método on
para carregar a página:
use Tests\Browser\Pages\CreatePlaylist;
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');
Seletores de atalhos
O método elements
dentro das classes de página permite que você defina atalhos rápidos e fáceis de lembrar para qualquer seletor CSS na sua página. Por exemplo, vamos definir um atalho para o campo de entrada "email" da página de login do aplicativo:
/**
* Obtenha os atalhos dos elementos para a página.
*
* @return array<string, string>
*/
public function elements(): array
{
return [
'@email' => 'input[name=email]',
];
}
Depois que o atalho for definido, você pode usar o seletor de atalhos em qualquer lugar que você normalmente usaria um seletor CSS completo:
$browser->type('@email', 'taylor@laravel.com');
Seletores de atalhos globais
Após instalar o Dusk, uma classe base Page
será colocada no seu diretório tests/Browser/Pages
. Esta classe contém um método siteElements
que pode ser usado para definir seletores de taquigrafia globais que devem estar disponíveis em todas as páginas do seu aplicativo:
/**
* Obtenha os atalhos de elementos globais para o site.
*
* @return array<string, string>
*/
public static function siteElements(): array
{
return [
'@element' => '#selector',
];
}
Métodos de página
Além dos métodos padrão definidos nas páginas, você pode definir métodos adicionais que podem ser usados em seus testes. Por exemplo, vamos imaginar que estamos construindo um aplicativo de gerenciamento de música. Uma ação comum para uma página do aplicativo pode ser criar uma lista de reprodução. Em vez de reescrever a lógica para criar uma lista de reprodução em cada teste, você pode definir um método createPlaylist
em uma classe de página:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Page;
class Dashboard extends Page
{
// Outros métodos de página...
/**
* Crie uma nova playlist.
*/
public function createPlaylist(Browser $browser, string $name): void
{
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
}
}
Depois que o método for definido, você pode usá-lo em qualquer teste que utilize a página. A instância do navegador será automaticamente passada como o primeiro argumento para métodos de página personalizados:
use Tests\Browser\Pages\Dashboard;
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
Componentes
Os componentes são semelhantes aos "objetos de página" do Dusk, mas são destinados a partes da IU e funcionalidade que são reutilizadas em todo o seu aplicativo, como uma barra de navegação ou janela de notificação. Como tal, os componentes não são vinculados a URLs específicos.
Gerando componentes
Para gerar um componente, execute o comando Artisan dusk:component
. Novos componentes são colocados no diretório tests/Browser/Components
:
php artisan dusk:component DatePicker
Como mostrado acima, um "seletor de data" é um exemplo de um componente que pode existir em todo o seu aplicativo em uma variedade de páginas. Pode se tornar complicado escrever manualmente a lógica de automação do navegador para selecionar uma data em dezenas de testes em todo o seu conjunto de testes. Em vez disso, podemos definir um componente Dusk para representar o seletor de data, permitindo-nos encapsular essa lógica dentro do componente:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
{
/**
* Obtenha o seletor raiz para o componente.
*/
public function selector(): string
{
return '.date-picker';
}
/**
* Afirme que a página do navegador contém o componente.
*/
public function assert(Browser $browser): void
{
$browser->assertVisible($this->selector());
}
/**
* Obtenha os atalhos de elementos para o componente.
*
* @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',
];
}
/**
* Selecione a data fornecida.
*/
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);
});
}
}
Usando componentes
Depois que o componente for definido, podemos facilmente selecionar uma data dentro do seletor de data de qualquer teste. E, se a lógica necessária para selecionar uma data mudar, precisamos apenas atualizar o componente:
<?php
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
uses(DatabaseMigrations::class);
test('basic example', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function (Browser $browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
});
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
/**
* Um exemplo básico de teste de componente.
*/
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');
});
}
}
Integração Contínua
AVISO
A maioria das configurações de integração contínua do Dusk espera que seu aplicativo Laravel seja servido usando o servidor de desenvolvimento PHP integrado na porta 8000. Portanto, antes de continuar, você deve garantir que seu ambiente de integração contínua tenha um valor de variável de ambiente APP_URL
de http://127.0.0.1:8000
.
Heroku CI
Para executar os testes Dusk no Heroku CI, adicione o seguinte buildpack e scripts do Google Chrome ao seu arquivo Heroku app.json
:
{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk"
}
}
}
}
Travis CI
Para executar seus testes Dusk no Travis CI, use a seguinte configuração .travis.yml
. Como o Travis CI não é um ambiente gráfico, precisaremos tomar algumas medidas extras para iniciar um navegador Chrome. Além disso, usaremos php artisan serve
para iniciar o servidor web integrado do 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
Ações do GitHub
Se você estiver usando Ações do GitHub para executar seus testes Dusk, você pode usar o seguinte arquivo de configuração como ponto de partida. Assim como o TravisCI, usaremos o comando php artisan serve
para iniciar o servidor web integrado do PHP:
name: CI
on: [push]
jobs:
dusk-php:
runs-on: ubuntu-latest
env:
APP_URL: "http://127.0.0.1:8000"
DB_USERNAME: root
DB_PASSWORD: root
MAIL_MAILER: log
steps:
- uses: actions/checkout@v4
- name: Prepare The Environment
run: cp .env.example .env
- name: Create Database
run: |
sudo systemctl start mysql
mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;"
- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver --detect
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux &
- name: Run Laravel Server
run: php artisan serve --no-reload &
- name: Run Dusk Tests
run: php artisan dusk
- name: Upload Screenshots
if: failure()
uses: actions/upload-artifact@v2
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v2
with:
name: console
path: tests/Browser/console
Chipper CI
Se você estiver usando Chipper CI para executar seus testes Dusk, você pode usar o seguinte arquivo de configuração como ponto de partida. Usaremos o servidor interno do PHP para executar o Laravel para que possamos ouvir solicitações:
# file .chipperci.yml
version: 1
environment:
php: 8.2
node: 16
# Include Chrome in the build environment
services:
- dusk
# Build all commits
on:
push:
branches: .*
pipeline:
- name: Setup
cmd: |
cp -v .env.example .env
composer install --no-interaction --prefer-dist --optimize-autoloader
php artisan key:generate
# Create a dusk env file, ensuring APP_URL uses BUILD_HOST
cp -v .env .env.dusk.ci
sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci
- name: Compile Assets
cmd: |
npm ci --no-audit
npm run build
- name: Browser Tests
cmd: |
php -S [::0]:8000 -t public 2>server.log &
sleep 2
php artisan dusk:chrome-driver $CHROME_DRIVER
php artisan dusk --env=ci
Para saber mais sobre como executar testes Dusk no Chipper CI, incluindo como usar bancos de dados, consulte a documentação oficial do Chipper CI.