1. Pruebas
  2. Laravel Dusk

Introducción

Laravel Dusk proporciona una API de automatización de navegadores y pruebas expresiva y fácil de usar. Por defecto, Dusk no requiere que instales JDK o Selenium en tu computadora local. En su lugar, Dusk utiliza una instalación independiente de ChromeDriver. Sin embargo, eres libre de utilizar cualquier otro controlador compatible con Selenium que prefieras.

Instalación

Para empezar, debes instalar Google Chrome y agregar la dependencia de Composer laravel/dusk a tu proyecto:

composer require --dev laravel/dusk

Advertencia Si estás registrando manualmente el proveedor de servicios de Dusk, nunca lo registres en tu entorno de producción, ya que esto podría permitir que usuarios arbitrarios autentiquen con tu aplicación.

Después de instalar el paquete Dusk, ejecuta el comando php artisan dusk:install. Este comando creará un directorio tests/Browser, un ejemplo de prueba Dusk e instalará el binario de Chrome Driver para tu sistema operativo:

php artisan dusk:install

A continuación, establece la variable de entorno APP_URL en el archivo .env de tu aplicación. Este valor debe coincidir con la URL que utilizas para acceder a tu aplicación en un navegador.

Nota Si estás utilizando Laravel Sail para gestionar tu entorno de desarrollo local, consulta también la documentación de Sail sobre configurar y ejecutar pruebas Dusk.

Gestión de Instalaciones de ChromeDriver

Si deseas instalar una versión diferente de ChromeDriver a la instalada por Laravel Dusk a través del comando dusk:install, puedes usar el comando dusk:chrome-driver:

# Install the latest version of ChromeDriver for your OS...
php artisan dusk:chrome-driver
 
# Install a given version of ChromeDriver for your OS...
php artisan dusk:chrome-driver 86
 
# Install a given version of ChromeDriver for all supported OSs...
php artisan dusk:chrome-driver --all
 
# Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...
php artisan dusk:chrome-driver --detect

Advertencia Dusk requiere que los binarios de chromedriver sean ejecutables. Si tienes problemas para ejecutar Dusk, asegúrate de que los binarios sean ejecutables con el siguiente comando: chmod -R 0755 vendor/laravel/dusk/bin/.

Uso de Otros Navegadores

Por defecto, Dusk utiliza Google Chrome y una instalación independiente de ChromeDriver para ejecutar tus pruebas de navegador. Sin embargo, puedes iniciar tu propio servidor Selenium y ejecutar tus pruebas contra cualquier navegador que desees.

Para comenzar, abre tu archivo tests/DuskTestCase.php, que es el caso base de prueba Dusk para tu aplicación. Dentro de este archivo, puedes eliminar la llamada al método startChromeDriver. Esto evitará que Dusk inicie automáticamente el ChromeDriver:

/**
* Preparar para la ejecución de la prueba Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}

A continuación, puedes modificar el método driver para conectarte a la URL y puerto de tu elección. Además, puedes modificar las "capacidades deseadas" que se deben pasar al WebDriver:

use Facebook\WebDriver\Remote\RemoteWebDriver;
 
/**
* Crea la instancia de RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}

Primeros Pasos

Generación de Pruebas

Para generar una prueba Dusk, utiliza el comando Artisan dusk:make. La prueba generada se ubicará en el directorio tests/Browser:

php artisan dusk:make LoginTest

Restablecer la Base de Datos Después de Cada Prueba

La mayoría de las pruebas que escribas interactuarán con páginas que recuperan datos de la base de datos de tu aplicación; sin embargo, tus pruebas Dusk nunca deben usar el rasgo RefreshDatabase. El rasgo RefreshDatabase utiliza transacciones de base de datos que no serán aplicables ni estarán disponibles en las solicitudes HTTP. En su lugar, tienes dos opciones: el rasgo DatabaseMigrations y el rasgo DatabaseTruncation.

Uso de Migraciones de Base de Datos

El rasgo DatabaseMigrations ejecutará tus migraciones de base de datos antes de cada prueba. Sin embargo, eliminar y volver a crear las tablas de tu base de datos para cada prueba suele ser más lento que truncar las tablas:

<?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;
}

Advertencia Las bases de datos en memoria de SQLite no se pueden utilizar al ejecutar pruebas de Dusk. Dado que el navegador se ejecuta dentro de su propio proceso, no podrá acceder a las bases de datos en memoria de otros procesos.

Uso de Truncamiento de Base de Datos

Antes de usar el rasgo DatabaseTruncation, debes instalar el paquete doctrine/dbal mediante el administrador de paquetes Composer:

composer require --dev doctrine/dbal

El rasgo DatabaseTruncation migrará tu base de datos en la primera prueba para asegurarse de que las tablas de tu base de datos se hayan creado correctamente. Sin embargo, en pruebas posteriores, las tablas de la base de datos simplemente se truncarán, proporcionando un impulso de velocidad sobre volver a ejecutar todas tus migraciones de base de datos:

<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
use DatabaseTruncation;
}

Por defecto, este rasgo truncará todas las tablas excepto la tabla migrations. Si deseas personalizar las tablas que se deben truncar, puedes definir una propiedad $tablesToTruncate en tu clase de prueba:

/**
* Indica qué tablas deben ser truncadas.
*
* @var array
*/
protected $tablesToTruncate = ['users'];

Alternativamente, puedes definir una propiedad $exceptTables en tu clase de prueba para especificar qué tablas deben excluirse de la truncación:

/**
* Indica qué tablas deben ser excluidas de la truncación.
*
* @var array
*/
protected $exceptTables = ['users'];

Para especificar las conexiones de base de datos que deben tener sus tablas truncadas, puedes definir una propiedad $connectionsToTruncate en tu clase de prueba:

/**
* Indica qué conexiones deben tener sus tablas truncadas.
*
* @var array
*/
protected $connectionsToTruncate = ['mysql'];

Si deseas ejecutar código antes o después de que se realice la truncación de la base de datos, puedes definir los métodos beforeTruncatingDatabase o afterTruncatingDatabase en tu clase de prueba:

/**
* Realiza cualquier trabajo que deba llevarse a cabo antes de que la base de datos comience a truncarse.
*/
protected function beforeTruncatingDatabase(): void
{
//
}
 
/**
* Realiza cualquier trabajo que deba llevarse a cabo después de que la base de datos haya terminado de truncarse.
*/
protected function afterTruncatingDatabase(): void
{
//
}

Ejecución de Pruebas

Para ejecutar tus pruebas de navegador, ejecuta el comando Artisan dusk:

php artisan dusk

Si tuviste fallas en las pruebas la última vez que ejecutaste el comando dusk, puedes ahorrar tiempo volviendo a ejecutar primero las pruebas fallidas usando el comando dusk:fails:

php artisan dusk:fails

El comando dusk acepta cualquier argumento que normalmente acepta el ejecutor de pruebas PHPUnit, como permitirte ejecutar solo las pruebas de un grupo dado:

php artisan dusk --group=foo

Nota Si estás utilizando Laravel Sail para gestionar tu entorno de desarrollo local, consulta la documentación de Sail sobre configurar y ejecutar pruebas Dusk.

Inicio Manual de ChromeDriver

Por defecto, Dusk intentará iniciar automáticamente ChromeDriver. Si esto no funciona para tu sistema en particular, puedes iniciar manualmente ChromeDriver antes de ejecutar el comando dusk. Si eliges iniciar ChromeDriver manualmente, debes comentar la siguiente línea de tu archivo tests/DuskTestCase.php:

/**
* Preparar para la ejecución de la prueba Dusk.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}

Además, si inicias ChromeDriver en un puerto que no sea 9515, debes modificar el método driver de la misma clase para reflejar el puerto correcto:

use Facebook\WebDriver\Remote\RemoteWebDriver;
 
/**
* Crea la instancia de RemoteWebDriver.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}

Manipulación del Entorno

Para forzar a Dusk a usar su propio archivo de entorno al ejecutar pruebas, crea un archivo .env.dusk.{entorno} en la raíz de tu proyecto. Por ejemplo, si vas a iniciar el comando dusk desde tu entorno local, debes crear un archivo .env.dusk.local.

Cuando se ejecutan las pruebas, Dusk realizará una copia de seguridad de tu archivo .env y renombrará tu entorno de Dusk a .env. Una vez que las pruebas hayan terminado, tu archivo .env se restaurará.

Conceptos Básicos del Navegador

Creación de Navegadores

Para empezar, escribamos una prueba que verifique que podemos iniciar sesión en nuestra aplicación. Después de generar una prueba, podemos modificarla para navegar a la página de inicio de sesión, ingresar algunas credenciales y hacer clic en el botón "Iniciar sesión". Para crear una instancia del navegador, puedes llamar al método browse desde dentro de tu prueba Dusk:

<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
 
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
 
/**
* Un ejemplo básico de prueba del navegador.
*/
public function test_basic_example(): void
{
$user = User::factory()->create([
'email' => '[email protected]',
]);
 
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}

Como puedes ver en el ejemplo anterior, el método browse acepta un cierre. Dusk pasará automáticamente una instancia del navegador a este cierre y es el objeto principal utilizado para interactuar y hacer afirmaciones contra tu aplicación.

Creación de Múltiples Navegadores

A veces, es posible que necesites varios navegadores para llevar a cabo una prueba correctamente. Por ejemplo, puede ser necesario utilizar varios navegadores para probar una pantalla de chat que interactúa con websockets. Para crear varios navegadores, simplemente agrega más argumentos de navegador a la firma del cierre dado al 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');
});

Navegación

El método visit se puede utilizar para navegar a una URI dada dentro de tu aplicación:

$browser->visit('/login');

Puedes usar el método visitRoute para navegar a una ruta con nombre:

$browser->visitRoute('login');

Puedes navegar hacia atrás y hacia adelante utilizando los métodos back y forward:

$browser->back();
 
$browser->forward();

Puedes usar el método refresh para actualizar la página:

$browser->refresh();

Cambiar el Tamaño de las Ventanas del Navegador

Puedes usar el método resize para ajustar el tamaño de la ventana del navegador:

$browser->resize(1920, 1080);

El método maximize se puede utilizar para maximizar la ventana del navegador:

$browser->maximize();

El método fitContent redimensionará la ventana del navegador para que coincida con el tamaño de su contenido:

$browser->fitContent();

Cuando una prueba falla, Dusk redimensionará automáticamente el navegador para que se ajuste al contenido antes de tomar una captura de pantalla. Puedes deshabilitar esta función llamando al método disableFitOnFailure dentro de tu prueba:

$browser->disableFitOnFailure();

Puedes usar el método move para mover la ventana del navegador a una posición diferente en tu pantalla:

$browser->move($x = 100, $y = 100);

Macros del Navegador

Si deseas definir un método de navegador personalizado que puedas reutilizar en varias de tus pruebas, puedes usar el método macro en la clase Browser. Normalmente, debes llamar a este método desde el método boot de un proveedor de servicios:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
 
class DuskServiceProvider extends ServiceProvider
{
/**
* Registra las macros del navegador Dusk.
*/
public function boot(): void
{
Browser::macro('scrollToElement', function (string $element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
 
return $this;
});
}
}

La función macro acepta un nombre como su primer argumento y un cierre como su segundo. El cierre de la macro se ejecutará al llamar a la macro como un método en una instancia de Browser:

$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});

Autenticación

A menudo, estarás probando páginas que requieren autenticación. Puedes usar el método loginAs de Dusk para evitar interactuar con la pantalla de inicio de sesión de tu aplicación durante cada prueba. El método loginAs acepta una clave primaria asociada con tu modelo autenticable o una instancia de modelo autenticable:

use App\Models\User;
use Laravel\Dusk\Browser;
 
$this->browse(function (Browser $browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});

Advertencia Después de usar el método loginAs, la sesión de usuario se mantendrá para todas las pruebas dentro del archivo.

Cookies

Puedes usar el método cookie para obtener o establecer el valor de una cookie cifrada. Por defecto, todas las cookies creadas por Laravel están cifradas:

$browser->cookie('name');
 
$browser->cookie('name', 'Taylor');

Puedes usar el método plainCookie para obtener o establecer el valor de una cookie no cifrada:

$browser->plainCookie('name');
 
$browser->plainCookie('name', 'Taylor');

Puedes usar el método deleteCookie para eliminar la cookie dada:

$browser->deleteCookie('name');

Ejecución de JavaScript

Puedes usar el método script para ejecutar declaraciones de JavaScript arbitrarias dentro del navegador:

$browser->script('document.documentElement.scrollTop = 0');
 
$browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
 
$output = $browser->script('return window.location.pathname');

Captura de Pantalla

Puedes usar el método screenshot para tomar una captura de pantalla y almacenarla con el nombre de archivo dado. Todas las capturas de pantalla se almacenarán en el directorio tests/Browser/screenshots:

$browser->screenshot('filename');

El método responsiveScreenshots se puede utilizar para tomar una serie de capturas de pantalla en varios puntos de interrupción:

$browser->responsiveScreenshots('filename');

Almacenar la Salida de la Consola en Disco

Puedes usar el método storeConsoleLog para escribir la salida actual de la consola del navegador en disco con el nombre de archivo proporcionado. La salida de la consola se almacenará en el directorio tests/Browser/console:

$browser->storeConsoleLog('filename');

Almacenar el Código Fuente de la Página en Disco

Puedes usar el método storeSource para escribir el origen actual de la página en disco con el nombre de archivo proporcionado. El origen de la página se almacenará en el directorio tests/Browser/source:

$browser->storeSource('filename');

Interacción con Elementos

Selectores Dusk

Elegir buenos selectores CSS para interactuar con elementos es una de las partes más difíciles de escribir pruebas Dusk. Con el tiempo, los cambios en el frontend pueden hacer que selectores CSS como los siguientes rompan tus pruebas:

// HTML...
 
<button>Login</button>
 
// Prueba...
 
$browser->click('.login-page .container div > button');

Los selectores Dusk te permiten centrarte en escribir pruebas efectivas en lugar de recordar selectores CSS. Para definir un selector, agrega un atributo dusk a tu elemento HTML. Luego, al interactuar con un navegador Dusk, agrega el prefijo @ al selector para manipular el elemento adjunto dentro de tu prueba:

// HTML...
 
<button dusk="login-button">Login</button>
 
// Prueba...
 
$browser->click('@login-button');

Si lo deseas, puedes personalizar el atributo HTML que utiliza el selector Dusk mediante el método selectorHtmlAttribute. Normalmente, este método debería llamarse desde el método boot del AppServiceProvider de tu aplicación:

use Laravel\Dusk\Dusk;
 
Dusk::selectorHtmlAttribute('data-dusk');

Texto, Valores y Atributos

Recuperar y Establecer Valores

Dusk proporciona varios métodos para interactuar con el valor actual, el texto de visualización y los atributos de los elementos en la página. Por ejemplo, para obtener el "valor" de un elemento que coincide con un selector CSS o Dusk dado, usa el método value:

// Recupera el valor...
$value = $browser->value('selector');
 
// Establece el valor...
$browser->value('selector', 'value');

Puedes usar el método inputValue para obtener el "valor" de un elemento de entrada que tiene un nombre de campo dado:

$value = $browser->inputValue('field');

Recuperar Texto

El método text se puede usar para recuperar el texto de visualización de un elemento que coincide con el selector dado:

$text = $browser->text('selector');

Recuperar Atributos

Finalmente, el método attribute se puede usar para recuperar el valor de un atributo de un elemento que coincide con el selector dado:

$attribute = $browser->attribute('selector', 'value');

Interacción con Formularios

Escribir Valores

Dusk proporciona una variedad de métodos para interactuar con formularios y elementos de entrada. Primero, echemos un vistazo a un ejemplo de cómo escribir texto en un campo de entrada:

$browser->type('email', '[email protected]');

Observa que, aunque el método acepta uno si es necesario, no estamos obligados a pasar un selector CSS completo al método type. Si no se proporciona un selector CSS, Dusk buscará un campo input o textarea con el atributo name dado.

Para agregar texto a un campo sin borrar su contenido, puedes usar el método append:

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

Puedes borrar el valor de una entrada usando el método clear:

$browser->clear('email');

Puedes indicarle a Dusk que escriba lentamente usando el método typeSlowly. Por defecto, Dusk esperará 100 milisegundos entre pulsaciones de teclas. Para personalizar la cantidad de tiempo entre pulsaciones de teclas, puedes pasar el número adecuado de milisegundos como tercer argumento al método:

$browser->typeSlowly('mobile', '+1 (202) 555-5555');
 
$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);

Puedes usar el método appendSlowly para agregar texto lentamente:

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

Desplegables

Para seleccionar un valor disponible en un elemento select, puedes usar el método select. Al igual que el método type, el método select no requiere un selector CSS completo. Al pasar un valor al método select, debes pasar el valor de opción subyacente en lugar del texto de visualización:

$browser->select('size', 'Large');

Puedes seleccionar una opción aleatoria omitiendo el segundo argumento:

$browser->select('size');

Al proporcionar una matriz como segundo argumento al método select, puedes indicarle al método que seleccione varias opciones:

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

Casillas de Verificación

Para "marcar" una casilla de verificación, puedes usar el método check. Al igual que muchos otros métodos relacionados con la entrada, no se requiere un selector CSS completo. Si no se puede encontrar una coincidencia de selector CSS, Dusk buscará una casilla de verificación con un atributo name coincidente:

$browser->check('terms');

Se puede utilizar el método uncheck para "desmarcar" una casilla de verificación:

$browser->uncheck('terms');

Botones de Radio

Para "seleccionar" una opción de entrada radio, puedes usar el método radio. Al igual que muchos otros métodos relacionados con la entrada, no se requiere un selector CSS completo. Si no se puede encontrar una coincidencia de selector CSS, Dusk buscará una entrada radio con atributos name y value coincidentes:

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

Adjuntar Archivos

El método attach se puede utilizar para adjuntar un archivo a un elemento de entrada de tipo file. Al igual que muchos otros métodos relacionados con la entrada, no se requiere un selector CSS completo. Si no se puede encontrar una coincidencia de selector CSS, Dusk buscará una entrada file con un atributo name coincidente:

$browser->attach('photo', __DIR__.'/photos/mountains.png');

Advertencia La función attach requiere que la extensión PHP Zip esté instalada y habilitada en tu servidor.

Presionar Botones

El método press se puede utilizar para hacer clic en un elemento de botón en la página. El argumento dado al método press puede ser tanto el texto de visualización del botón como un selector CSS / Dusk:

$browser->press('Login');

Al enviar formularios, muchas aplicaciones desactivan el botón de envío del formulario después de presionarlo y luego vuelven a habilitar el botón cuando se completa la solicitud HTTP del envío del formulario. Para presionar un botón y esperar a que se vuelva a habilitar, puedes usar el método pressAndWaitFor:

// Presiona el botón y espera un máximo de 5 segundos para que esté habilitado...
$browser->pressAndWaitFor('Save');
 
// Presiona el botón y espera un máximo de 1 segundo para que esté habilitado...
$browser->pressAndWaitFor('Save', 1);

Hacer Clic en Enlaces

Para hacer clic en un enlace, puedes usar el método clickLink en la instancia del navegador. El método clickLink hará clic en el enlace que tiene el texto de visualización dado:

$browser->clickLink($linkText);

Puedes usar el método seeLink para determinar si hay un enlace con el texto de visualización dado visible en la página:

if ($browser->seeLink($linkText)) {
// ...
}

Advertencia Estos métodos interactúan con jQuery. Si jQuery no está disponible en la página, Dusk lo inyectará automáticamente en la página para que esté disponible durante la duración de la prueba.

Uso del Teclado

El método keys te permite proporcionar secuencias de entrada más complejas a un elemento dado de lo que normalmente permite el método type. Por ejemplo, puedes indicarle a Dusk que mantenga presionadas las teclas modificadoras al ingresar valores. En este ejemplo, se mantendrá presionada la tecla shift mientras se ingresa taylor en el elemento que coincide con el selector dado. Después de escribir taylor, se escribirá swift sin ninguna tecla modificadoras:

$browser->keys('selector', ['{shift}', 'taylor'], 'swift');

Otro caso de uso valioso para el método keys es enviar una combinación de "teclas de acceso directo" al selector CSS principal de tu aplicación:

$browser->keys('.app', ['{command}', 'j']);

Nota Todas las teclas modificadoras como {command} están envueltas en {} caracteres y coinciden con las constantes definidas en la clase Facebook\WebDriver\WebDriverKeys, que se pueden encontrar en GitHub.

Interacciones Fluidas con el Teclado

Dusk también proporciona un método withKeyboard, que te permite realizar interacciones avanzadas con el teclado de forma fluida a través de la clase Laravel\Dusk\Keyboard. La clase Keyboard proporciona los métodos press, release, type y pause:

use Laravel\Dusk\Keyboard;
 
$browser->withKeyboard(function (Keyboard $keyboard) {
$keyboard->press('c')
->pause(1000)
->release('c')
->type(['c', 'e', 'o']);
});

Macros del Teclado

Si deseas definir interacciones de teclado personalizadas que puedas reutilizar fácilmente en tu conjunto de pruebas, puedes usar el método macro proporcionado por la clase Keyboard. Normalmente, deberías llamar a este método desde el método boot de un proveedor de servicios:

<?php
 
namespace App\Providers;
 
use Facebook\WebDriver\WebDriverKeys;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Keyboard;
use Laravel\Dusk\OperatingSystem;
 
class DuskServiceProvider extends ServiceProvider
{
/**
* Registra las macros del navegador 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;
});
}
}

La función macro acepta un nombre como su primer argumento y un cierre como su segundo. El cierre de la macro se ejecutará al llamar a la macro como un método en una instancia de Keyboard:

$browser->click('@textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy())
->click('@another-textarea')
->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste());

Uso del Ratón

Hacer Clic en Elementos

El método click se puede utilizar para hacer clic en un elemento que coincide con el selector CSS o Dusk dado:

$browser->click('.selector');

El método clickAtXPath se puede utilizar para hacer clic en un elemento que coincida con la expresión XPath proporcionada:

$browser->clickAtXPath('//div[@class = "selector"]');

El método clickAtPoint se puede utilizar para hacer clic en el elemento más superior en un par de coordenadas dadas en relación con el área visible del navegador:

$browser->clickAtPoint($x = 0, $y = 0);

El método doubleClick se puede utilizar para simular el doble clic de un ratón:

$browser->doubleClick();

El método rightClick se puede utilizar para simular el clic derecho de un ratón:

$browser->rightClick();
 
$browser->rightClick('.selector');

El método clickAndHold se puede utilizar para simular el clic y mantenimiento de un botón del ratón. Una llamada subsiguiente al método releaseMouse deshará este comportamiento y liberará el botón del ratón:

$browser->clickAndHold()
->pause(1000)
->releaseMouse();

El método controlClick se puede utilizar para simular el evento ctrl+click dentro del navegador:

$browser->controlClick();

Mouseover

El método mouseover se puede utilizar cuando necesitas mover el ratón sobre un elemento que coincida con el selector CSS o Dusk dado:

$browser->mouseover('.selector');

Arrastrar y Soltar

El método drag se puede utilizar para arrastrar un elemento que coincida con el selector a otro elemento:

$browser->drag('.from-selector', '.to-selector');

O puedes arrastrar un elemento en una sola dirección:

$browser->dragLeft('.selector', $pixels = 10);
$browser->dragRight('.selector', $pixels = 10);
$browser->dragUp('.selector', $pixels = 10);
$browser->dragDown('.selector', $pixels = 10);

Finalmente, puedes arrastrar un elemento por un desplazamiento dado:

$browser->dragOffset('.selector', $x = 10, $y = 10);

Diálogos JavaScript

Dusk proporciona varios métodos para interactuar con los cuadros de diálogo de JavaScript. Por ejemplo, puedes usar el método waitForDialog para esperar a que aparezca un cuadro de diálogo de JavaScript. Este método acepta un argumento opcional que indica cuántos segundos esperar a que aparezca el cuadro de diálogo:

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

El método assertDialogOpened se puede utilizar para afirmar que se ha mostrado un cuadro de diálogo y contiene el mensaje dado:

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

Si el cuadro de diálogo de JavaScript contiene una solicitud, puedes usar el método typeInDialog para escribir un valor en la solicitud:

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

Para cerrar un cuadro de diálogo de JavaScript abierto haciendo clic en el botón "OK", puedes invocar el método acceptDialog:

$browser->acceptDialog();

Para cerrar un cuadro de diálogo de JavaScript abierto haciendo clic en el botón "Cancelar", puedes invocar el método dismissDialog:

$browser->dismissDialog();

Interacción con Marcos Inline

Si necesitas interactuar con elementos dentro de un iframe, puedes usar el método withinFrame. Todas las interacciones con elementos que tienen lugar dentro del cierre proporcionado al método withinFrame estarán limitadas al contexto del iframe especificado:

$browser->withinFrame('#credit-card-details', function ($browser) {
$browser->type('input[name="cardnumber"]', '4242424242424242')
->type('input[name="exp-date"]', '12/24')
->type('input[name="cvc"]', '123');
})->press('Pay');
});

Seleccionar Selectores

A veces, es posible que desees realizar varias operaciones al limitar todas las operaciones dentro de un selector dado. Por ejemplo, es posible que desees afirmar que un texto existe solo dentro de una tabla y luego hacer clic en un botón dentro de esa tabla. Puedes usar el método with para lograr esto. Todas las operaciones realizadas dentro del cierre proporcionado al método with estarán limitadas al selector original:

$browser->with('.table', function (Browser $table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});

Ocasionalmente, es posible que necesites ejecutar afirmaciones fuera del alcance actual. Puedes usar los métodos elsewhere y elsewhereWhenAvailable para lograr esto:

$browser->with('.table', function (Browser $table) {
// El alcance actual es `body .table`...
 
$browser->elsewhere('.page-title', function (Browser $title) {
// El alcance actual es `body .page-title`...
$title->assertSee('Hello World');
});
 
$browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
// El alcance actual es `body .page-title`...
$title->assertSee('Hello World');
});
});

Espera de Elementos

Al probar aplicaciones que utilizan JavaScript extensivamente, a menudo es necesario "esperar" a que ciertos elementos o datos estén disponibles antes de continuar con una prueba. Dusk facilita esto. Utilizando varios métodos, puedes esperar a que los elementos sean visibles en la página o incluso esperar hasta que se evalúe a true una expresión JavaScript dada.

Espera

Si solo necesitas pausar la prueba durante un número dado de milisegundos, utiliza el método pause:

$browser->pause(1000);

Si necesitas pausar la prueba solo si una condición dada es true, utiliza el método pauseIf:

$browser->pauseIf(App::environment('production'), 1000);

De manera similar, si necesitas pausar la prueba a menos que una condición dada sea true, puedes usar el método pauseUnless:

$browser->pauseUnless(App::environment('testing'), 1000);

Esperar Selectores

El método waitFor se puede utilizar para pausar la ejecución de la prueba hasta que se muestre en la página el elemento que coincida con el selector CSS o Dusk dado. De forma predeterminada, esto pausará la prueba durante un máximo de cinco segundos antes de lanzar una excepción. Si es necesario, puedes pasar un umbral de tiempo personalizado como segundo argumento al método:

// Espera un máximo de cinco segundos para el selector...
$browser->waitFor('.selector');
 
// Espera un máximo de un segundo para el selector...
$browser->waitFor('.selector', 1);

También puedes esperar hasta que el elemento que coincida con el selector dado contenga el texto dado:

// Espera un máximo de cinco segundos para que el selector contenga el texto dado...
$browser->waitForTextIn('.selector', 'Hello World');
 
// Espera un máximo de un segundo para que el selector contenga el texto dado...
$browser->waitForTextIn('.selector', 'Hello World', 1);

También puedes esperar hasta que el elemento que coincida con el selector dado falte en la página:

// Espera un máximo de cinco segundos hasta que el selector falte...
$browser->waitUntilMissing('.selector');
 
// Espera un máximo de un segundo hasta que el selector falte...
$browser->waitUntilMissing('.selector', 1);

O puedes esperar hasta que el elemento que coincida con el selector dado esté habilitado o deshabilitado:

// Espera un máximo de cinco segundos hasta que el selector esté habilitado...
$browser->waitUntilEnabled('.selector');
 
// Espera un máximo de un segundo hasta que el selector esté habilitado...
$browser->waitUntilEnabled('.selector', 1);
 
// Espera un máximo de cinco segundos hasta que el selector esté deshabilitado...
$browser->waitUntilDisabled('.selector');
 
// Espera un máximo de un segundo hasta que el selector esté deshabilitado...
$browser->waitUntilDisabled('.selector', 1);

Seleccionar Selectores Cuando Estén Disponibles

Ocasionalmente, es posible que desees esperar a que aparezca un elemento que coincida con un selector dado y luego interactuar con el elemento. Por ejemplo, es posible que desees esperar hasta que esté disponible una ventana modal y luego hacer clic en el botón "OK" dentro de la ventana modal. El método whenAvailable se puede utilizar para lograr esto. Todas las operaciones de elementos realizadas dentro del cierre dado estarán limitadas al selector original:

$browser->whenAvailable('.modal', function (Browser $modal) {
$modal->assertSee('Hello World')
->press('OK');
});

Esperar Texto

El método waitForText se puede utilizar para esperar hasta que se muestre en la página el texto dado:

// Espera un máximo de cinco segundos para el texto...
$browser->waitForText('Hello World');
 
// Espera un máximo de un segundo para el texto...
$browser->waitForText('Hello World', 1);

Puedes usar el método waitUntilMissingText para esperar hasta que el texto mostrado se haya eliminado de la página:

// Espera un máximo de cinco segundos para que el texto sea eliminado...
$browser->waitUntilMissingText('Hello World');
 
// Espera un máximo de un segundo para que el texto sea eliminado...
$browser->waitUntilMissingText('Hello World', 1);

Esperar Enlaces

El método waitForLink se puede utilizar para esperar hasta que el texto del enlace dado se muestre en la página:

// Espera un máximo de cinco segundos para el enlace...
$browser->waitForLink('Create');
 
// Espera un máximo de un segundo para el enlace...
$browser->waitForLink('Create', 1);

Esperar Entradas

El método waitForInput se puede utilizar para esperar hasta que el campo de entrada dado sea visible en la página:

// Espera un máximo de cinco segundos para el input...
$browser->waitForInput($field);
 
// Espera un máximo de un segundo para el input...
$browser->waitForInput($field, 1);

Esperando la Ubicación de la Página

Al realizar una afirmación de ruta como $browser->assertPathIs('/home'), la afirmación puede fallar si window.location.pathname se está actualizando de forma asíncrona. Puedes usar el método waitForLocation para esperar a que la ubicación sea un valor dado:

$browser->waitForLocation('/secret');

El método waitForLocation también se puede usar para esperar a que la ubicación actual de la ventana sea una URL completamente calificada:

$browser->waitForLocation('https://example.com/path');

También puedes esperar a la ubicación de una ruta con nombre:

$browser->waitForRoute($routeName, $parameters);

Esperando Recargas de Página

Si necesitas esperar a que se recargue una página después de realizar una acción, usa el método waitForReload:

use Laravel\Dusk\Browser;
 
$browser->waitForReload(function (Browser $browser) {
$browser->press('Submit');
})
->assertSee('Success!');

Dado que la necesidad de esperar a que se recargue la página suele ocurrir después de hacer clic en un botón, puedes usar el método clickAndWaitForReload por conveniencia:

$browser->clickAndWaitForReload('.selector')
->assertSee('something');

Esperando Expresiones JavaScript

A veces, es posible que desees pausar la ejecución de una prueba hasta que una expresión JavaScript dada se evalúe como true. Puedes lograrlo fácilmente usando el método waitUntil. Al pasar una expresión a este método, no es necesario incluir la palabra clave return ni un punto y coma al final:

// Espera un máximo de cinco segundos a que la expresión sea verdadera...
$browser->waitUntil('App.data.servers.length > 0');
 
// Espera un máximo de un segundo a que la expresión sea verdadera...
$browser->waitUntil('App.data.servers.length > 0', 1);

Esperando Expresiones Vue

Los métodos waitUntilVue y waitUntilVueIsNot se pueden usar para esperar hasta que un atributo de un componente Vue tenga un valor dado:

// Espera hasta que el atributo del componente contenga el valor dado...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
 
// Espera hasta que el atributo del componente no contenga el valor dado...
$browser->waitUntilVueIsNot('user.name', null, '@user');

Esperando Eventos JavaScript

El método waitForEvent se puede usar para pausar la ejecución de una prueba hasta que ocurra un evento JavaScript:

$browser->waitForEvent('load');

El escucha de eventos está adjunto al alcance actual, que es el elemento body por defecto. Al usar un selector con alcance, el escucha de eventos se adjuntará al elemento coincidente:

$browser->with('iframe', function (Browser $iframe) {
// Espera el evento de carga del iframe...
$iframe->waitForEvent('load');
});

También puedes proporcionar un selector como segundo argumento al método waitForEvent para adjuntar el escucha de eventos a un elemento específico:

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

También puedes esperar eventos en los objetos document y window:

// Espera hasta que se haga scroll en el documento...
$browser->waitForEvent('scroll', 'document');
 
// Espera un máximo de cinco segundos hasta que la ventana cambie de tamaño...
$browser->waitForEvent('resize', 'window', 5);

Esperando con un Callback

Muchos de los métodos de "espera" en Dusk dependen del método subyacente waitUsing. Puedes usar este método directamente para esperar a que un cierre dado devuelva true. El método waitUsing acepta el número máximo de segundos para esperar, el intervalo en el que se debe evaluar el cierre, el cierre y un mensaje de error opcional:

$browser->waitUsing(10, 1, function () use ($something) {
return $something->isReady();
}, "Something wasn't ready in time.");

Desplazamiento de un Elemento a la Vista

A veces, es posible que no puedas hacer clic en un elemento porque está fuera del área visible del navegador. El método scrollIntoView desplazará la ventana del navegador hasta que el elemento en el selector dado esté dentro de la vista:

$browser->scrollIntoView('.selector')
->click('.selector');

Asertos Disponibles

Dusk proporciona una variedad de afirmaciones que puedes hacer contra tu aplicación. Todas las afirmaciones disponibles se documentan en la siguiente lista:

assertTitle

Asegura que el título de la página coincida con el texto dado:

$browser->assertTitle($title);

assertTitleContains

Asegura que el título de la página contenga el texto dado:

$browser->assertTitleContains($title);

assertUrlIs

Asegura que la URL actual (sin la cadena de consulta) coincida con la cadena dada:

$browser->assertUrlIs($url);

assertSchemeIs

Asegura que el esquema de la URL actual coincida con el esquema dado:

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

Asegura que el esquema de la URL actual no coincida con el esquema dado:

$browser->assertSchemeIsNot($scheme);

assertHostIs

Asegura que el host de la URL actual coincida con el host dado:

$browser->assertHostIs($host);

assertHostIsNot

Asegura que el host de la URL actual no coincida con el host dado:

$browser->assertHostIsNot($host);

assertPortIs

Asegura que el puerto de la URL actual coincida con el puerto dado:

$browser->assertPortIs($port);

assertPortIsNot

Asegura que el puerto de la URL actual no coincida con el puerto dado:

$browser->assertPortIsNot($port);

assertPathBeginsWith

Asegura que la ruta de la URL actual comience con la ruta dada:

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

assertPathIs

Asegura que la ruta actual coincida con la ruta dada:

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

assertPathIsNot

Asegura que la ruta actual no coincida con la ruta dada:

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

assertRouteIs

Asegura que la URL actual coincida con la URL de la ruta con nombre dada:

$browser->assertRouteIs($name, $parameters);

assertQueryStringHas

Asegura que el parámetro de cadena de consulta dado esté presente:

$browser->assertQueryStringHas($name);

Asegura que el parámetro de cadena de consulta dado esté presente y tenga un valor dado:

$browser->assertQueryStringHas($name, $value);

assertQueryStringMissing

Asegura que el parámetro de cadena de consulta dado esté ausente:

$browser->assertQueryStringMissing($name);

assertFragmentIs

Asegura que el fragmento hash actual de la URL coincida con el fragmento dado:

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

Asegura que el fragmento hash actual de la URL comience con el fragmento dado:

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

Asegura que el fragmento hash actual de la URL no coincida con el fragmento dado:

$browser->assertFragmentIsNot('anchor');

assertHasCookie

Asegura que la cookie cifrada dada esté presente:

$browser->assertHasCookie($name);

assertHasPlainCookie

Asegura que la cookie no cifrada dada esté presente:

$browser->assertHasPlainCookie($name);

assertCookieMissing

Asegura que la cookie cifrada dada no esté presente:

$browser->assertCookieMissing($name);

assertPlainCookieMissing

Asegura que la cookie no cifrada dada no esté presente:

$browser->assertPlainCookieMissing($name);

assertCookieValue

Asegura que una cookie cifrada tenga un valor dado:

$browser->assertCookieValue($name, $value);

assertPlainCookieValue

Asegura que una cookie no cifrada tenga un valor dado:

$browser->assertPlainCookieValue($name, $value);

assertSee

Asegura que el texto dado esté presente en la página:

$browser->assertSee($text);

assertDontSee

Asegura que el texto dado no esté presente en la página:

$browser->assertDontSee($text);

assertSeeIn

Asegura que el texto dado esté presente dentro del selector:

$browser->assertSeeIn($selector, $text);

assertDontSeeIn

Asegura que el texto dado no esté presente dentro del selector:

$browser->assertDontSeeIn($selector, $text);

assertSeeAnythingIn

Asegura que hay algún texto presente dentro del selector:

$browser->assertSeeAnythingIn($selector);

assertSeeNothingIn

Asegura que no hay texto presente dentro del selector:

$browser->assertSeeNothingIn($selector);

assertScript

Asegura que la expresión JavaScript dada se evalúa como el valor dado:

$browser->assertScript('window.isLoaded')
->assertScript('document.readyState', 'complete');

assertSourceHas

Asegura que el código fuente dado esté presente en la página:

$browser->assertSourceHas($code);

assertSourceMissing

Asegura que el código fuente dado no esté presente en la página:

$browser->assertSourceMissing($code);

assertSeeLink

Asegura que el enlace dado esté presente en la página:

$browser->assertSeeLink($linkText);

assertDontSeeLink

Asegura que el enlace dado no esté presente en la página:

$browser->assertDontSeeLink($linkText);

assertInputValue

Asegura que el campo de entrada dado tenga el valor dado:

$browser->assertInputValue($field, $value);

assertInputValueIsNot

Asegura que el campo de entrada dado no tenga el valor dado:

$browser->assertInputValueIsNot($field, $value);

assertChecked

Asegura que la casilla de verificación dada esté marcada:

$browser->assertChecked($field);

assertNotChecked

Asegura que la casilla de verificación dada no esté marcada:

$browser->assertNotChecked($field);

assertIndeterminate

Asegura que la casilla de verificación dada esté en un estado indeterminado:

$browser->assertIndeterminate($field);

assertRadioSelected

Asegura que el campo de opción de radio dado esté seleccionado:

$browser->assertRadioSelected($field, $value);

assertRadioNotSelected

Asegura que el campo de opción de radio dado no esté seleccionado:

$browser->assertRadioNotSelected($field, $value);

assertSelected

Asegura que la lista desplegable dada tenga el valor dado seleccionado:

$browser->assertSelected($field, $value);

assertNotSelected

Asegura que la lista desplegable dada no tenga el valor dado seleccionado:

$browser->assertNotSelected($field, $value);

assertSelectHasOptions

Asegura que la lista de valores dada esté disponible para ser seleccionada:

$browser->assertSelectHasOptions($field, $values);

assertSelectMissingOptions

Asegura que la lista de valores dada no esté disponible para ser seleccionada:

$browser->assertSelectMissingOptions($field, $values);

assertSelectHasOption

Asegura que el valor dado esté disponible para ser seleccionado en el campo dado:

$browser->assertSelectHasOption($field, $value);

assertSelectMissingOption

Asegura que el valor dado no esté disponible para ser seleccionado:

$browser->assertSelectMissingOption($field, $value);

assertValue

Asegura que el elemento que coincide con el selector dado tenga el valor dado:

$browser->assertValue($selector, $value);

assertValueIsNot

Asegura que el elemento que coincide con el selector dado no tenga el valor dado:

$browser->assertValueIsNot($selector, $value);

assertAttribute

Asegura que el elemento que coincide con el selector dado tenga el valor dado en el atributo proporcionado:

$browser->assertAttribute($selector, $attribute, $value);

assertAttributeContains

Asegura que el elemento que coincide con el selector dado contenga el valor dado en el atributo proporcionado:

$browser->assertAttributeContains($selector, $attribute, $value);

assertAriaAttribute

Asegura que el elemento que coincide con el selector dado tenga el valor dado en el atributo aria proporcionado:

$browser->assertAriaAttribute($selector, $attribute, $value);

Por ejemplo, dado el marcado <button aria-label=\"Añadir\"></button>, puedes afirmar contra el atributo aria-label así:

$browser->assertAriaAttribute('button', 'label', 'Add')

assertDataAttribute

Asegura que el elemento que coincide con el selector dado tenga el valor dado en el atributo de datos proporcionado:

$browser->assertDataAttribute($selector, $attribute, $value);

Por ejemplo, dado el marcado <tr id=\"fila-1\" data-content=\"asistentes\"></tr>, puedes afirmar contra el atributo data-label así:

$browser->assertDataAttribute('#row-1', 'content', 'attendees')

assertVisible

Asegura que el elemento que coincide con el selector dado sea visible:

$browser->assertVisible($selector);

assertPresent

Asegura que el elemento que coincide con el selector dado esté presente en el origen:

$browser->assertPresent($selector);

assertNotPresent

Asegura que el elemento que coincide con el selector dado no esté presente en el origen:

$browser->assertNotPresent($selector);

assertMissing

Asegura que el elemento que coincide con el selector dado no sea visible:

$browser->assertMissing($selector);

assertInputPresent

Asegura que un campo con el nombre dado esté presente:

$browser->assertInputPresent($name);

assertInputMissing

Asegura que un campo con el nombre dado no esté presente en el origen:

$browser->assertInputMissing($name);

assertDialogOpened

Asegura que se haya abierto un cuadro de diálogo de JavaScript con el mensaje dado:

$browser->assertDialogOpened($message);

assertEnabled

Asegura que el campo dado esté habilitado:

$browser->assertEnabled($field);

assertDisabled

Asegura que el campo dado esté deshabilitado:

$browser->assertDisabled($field);

assertButtonEnabled

Asegura que el botón dado esté habilitado:

$browser->assertButtonEnabled($button);

assertButtonDisabled

Asegura que el botón dado esté deshabilitado:

$browser->assertButtonDisabled($button);

assertFocused

Asegura que el campo dado esté enfocado:

$browser->assertFocused($field);

assertNotFocused

Asegura que el campo dado no esté enfocado:

$browser->assertNotFocused($field);

assertAuthenticated

Asegura que el usuario esté autenticado:

$browser->assertAuthenticated();

assertGuest

Asegura que el usuario no esté autenticado:

$browser->assertGuest();

assertAuthenticatedAs

Asegura que el usuario esté autenticado como el usuario dado:

$browser->assertAuthenticatedAs($user);

assertVue

Dusk incluso te permite hacer afirmaciones sobre el estado de los datos de componentes Vue. Por ejemplo, imagina que tu aplicación contiene el siguiente componente Vue:

// HTML...
 
<profile dusk="profile-component"></profile>
 
// Definición del componente...
 
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
 
data: function () {
return {
user: {
name: 'Taylor'
}
};
}
});

Puedes afirmar sobre el estado del componente Vue de la siguiente manera:

/**
* Un ejemplo básico de prueba Vue.
*/
public function test_vue(): void
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}

assertVueIsNot

Asegura que una propiedad de datos dada del componente Vue no coincida con el valor dado:

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

assertVueContains

Asegura que una propiedad de datos dada de un componente Vue sea un array y contenga el valor proporcionado:

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

assertVueDoesNotContain

Asegura que una propiedad de datos dada del componente Vue sea una matriz y no contenga el valor dado:

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

Páginas

A veces, las pruebas requieren que se realicen varias acciones complicadas en secuencia. Esto puede hacer que tus pruebas sean más difíciles de leer y entender. Las páginas de Dusk te permiten definir acciones expresivas que luego se pueden realizar en una página dada a través de un solo método. Las páginas también te permiten definir accesos directos a selectores comunes para tu aplicación o para una sola página.

Generación de Páginas

Para generar un objeto de página, ejecuta el comando Artisan dusk:page. Todos los objetos de página se colocarán en el directorio tests/Browser/Pages de tu aplicación:

php artisan dusk:page Login

Configuración de Páginas

Por defecto, las páginas tienen tres métodos: url, assert, y elements. Discutiremos los métodos url y assert ahora. El método elements se discutirá con más detalle a continuación.

El Método url

El método url debe devolver la ruta de la URL que representa la página. Dusk usará esta URL al navegar a la página en el navegador:

/**
* Obtiene la URL de la página.
*/
public function url(): string
{
return '/login';
}

El Método assert

El método assert puede realizar cualquier afirmación necesaria para verificar que el navegador esté realmente en la página dada. En realidad, no es necesario colocar nada dentro de este método; sin embargo, eres libre de hacer estas afirmaciones si lo deseas. Estas afirmaciones se ejecutarán automáticamente al navegar a la página:

/**
* Asegura que el navegador esté en la página.
*/
public function assert(Browser $browser): void
{
$browser->assertPathIs($this->url());
}

Navegación a Páginas

Una vez que se haya definido una página, puedes navegar a ella usando el método visit:

use Tests\Browser\Pages\Login;
 
$browser->visit(new Login);

A veces ya puedes estar en una página dada y necesitar "cargar" los selectores y métodos de la página en el contexto de la prueba actual. Esto es común cuando presionas un botón y te redirigen a una página dada sin navegar explícitamente a ella. En esta situación, puedes usar el método on para cargar la página:

use Tests\Browser\Pages\CreatePlaylist;
 
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');

Selectores Abreviados

El método elements dentro de las clases de página te permite definir accesos directos rápidos y fáciles de recordar para cualquier selector CSS en tu página. Por ejemplo, definamos un acceso directo para el campo de entrada "correo electrónico" de la página de inicio de sesión de la aplicación:

/**
* Obtiene los atajos de elementos para la página.
*
* @return array<string, string>
*/
public function elements(): array
{
return [
'@email' => 'input[name=email]',
];
}

Una vez que se haya definido el acceso directo, puedes usar el selector abreviado en cualquier lugar donde normalmente usarías un selector CSS completo:

$browser->type('@email', '[email protected]');

Selectores Globales Abreviados

Después de instalar Dusk, se colocará una clase base Page en tu directorio tests/Browser/Pages. Esta clase contiene un método siteElements que se puede utilizar para definir accesos directos globales que deberían estar disponibles en cada página de tu aplicación:

/**
* Obtiene los atajos de elementos globales para el sitio.
*
* @return array<string, string>
*/
public static function siteElements(): array
{
return [
'@element' => '#selector',
];
}

Métodos de Página

Además de los métodos predeterminados definidos en las páginas, puedes definir métodos adicionales que se pueden utilizar en tus pruebas. Por ejemplo, imaginemos que estás construyendo una aplicación de gestión de música. Una acción común para una página de la aplicación podría ser crear una lista de reproducción. En lugar de volver a escribir la lógica para crear una lista de reproducción en cada prueba, puedes definir un método createPlaylist en una clase de página:

<?php
 
namespace Tests\Browser\Pages;
 
use Laravel\Dusk\Browser;
 
class Dashboard extends Page
{
// Otros métodos de la página...
 
/**
* Crea una nueva lista de reproducción.
*/
public function createPlaylist(Browser $browser, string $name): void
{
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
}
}

Una vez que se haya definido el método, puedes usarlo en cualquier prueba que utilice la página. La instancia del navegador se pasará automáticamente como el primer argumento a los métodos personalizados de la página:

use Tests\Browser\Pages\Dashboard;
 
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');

Componentes

Los componentes son similares a los "objetos de página" de Dusk, pero están destinados a piezas de interfaz de usuario y funcionalidades que se reutilizan en toda tu aplicación, como una barra de navegación o una ventana de notificación. Como tal, los componentes no están vinculados a URL específicas.

Generación de Componentes

Para generar un componente, ejecuta el comando Artisan dusk:component. Los nuevos componentes se colocan en el directorio tests/Browser/Components:

php artisan dusk:component DatePicker

Como se muestra arriba, un "selector de fecha" es un ejemplo de un componente que podría existir en toda tu aplicación en una variedad de páginas. Puede resultar engorroso escribir manualmente la lógica de automatización del navegador para seleccionar una fecha en docenas de pruebas en toda tu suite de pruebas. En su lugar, podemos definir un componente de Dusk para representar el selector de fecha, lo que nos permite encapsular esa lógica dentro del componente:

<?php
 
namespace Tests\Browser\Components;
 
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
 
class DatePicker extends BaseComponent
{
/**
* Obtiene el selector raíz para el componente.
*/
public function selector(): string
{
return '.date-picker';
}
 
/**
* Asegura que la página del navegador contenga el componente.
*/
public function assert(Browser $browser): void
{
$browser->assertVisible($this->selector());
}
 
/**
* Obtiene los atajos de elementos para el 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',
];
}
 
/**
* Selecciona la fecha dada.
*/
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);
});
}
}

Uso de Componentes

Una vez que se haya definido el componente, podemos seleccionar fácilmente una fecha dentro del selector de fecha desde cualquier prueba. Y, si la lógica necesaria para seleccionar una fecha cambia, solo necesitamos actualizar el componente:

<?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
{
/**
* Un ejemplo básico de prueba 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');
});
}
}

Integración Continua

Advertencia La mayoría de las configuraciones de integración continua de Dusk esperan que tu aplicación Laravel se sirva utilizando el servidor de desarrollo PHP incorporado en el puerto 8000. Por lo tanto, antes de continuar, asegúrate de que tu entorno de integración continua tenga un valor de variable de entorno APP_URL de http://127.0.0.1:8000.

Heroku CI

Para ejecutar pruebas Dusk en Heroku CI, agrega el siguiente buildpack de Google Chrome y scripts a tu archivo app.json de Heroku:

{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk"
}
}
}
}

Travis CI

Para ejecutar tus pruebas Dusk en Travis CI, utiliza la siguiente configuración de .travis.yml. Dado que Travis CI no es un entorno gráfico, deberemos tomar algunos pasos adicionales para lanzar un navegador Chrome. Además, utilizaremos php artisan serve para iniciar el servidor web incorporado de PHP:

language: php
 
php:
- 7.3
 
addons:
chrome: stable
 
install:
- cp .env.testing .env
- travis_retry composer install --no-interaction --prefer-dist
- php artisan key:generate
- php artisan dusk:chrome-driver
 
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- php artisan serve --no-reload &
 
script:
- php artisan dusk

GitHub Actions

Si estás utilizando GitHub Actions para ejecutar tus pruebas Dusk, puedes usar el siguiente archivo de configuración como punto de partida. Al igual que TravisCI, utilizaremos el comando php artisan serve para iniciar el servidor web incorporado de 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

Si estás utilizando Chipper CI para ejecutar tus pruebas Dusk, puedes usar el siguiente archivo de configuración como punto de partida. Utilizaremos el servidor incorporado de PHP para ejecutar Laravel y escuchar las solicitudes:

# 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 obtener más información sobre cómo ejecutar pruebas Dusk en Chipper CI, incluido el uso de bases de datos, consulta la documentación oficial de Chipper CI.