Saltar contenido

Laravel Dusk

Introducción

Laravel Dusk proporciona una API de automatización y prueba de navegador 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, puedes utilizar cualquier otro driver compatible con Selenium que desees.

Instalación

Para comenzar, deberías instalar Google Chrome y añadir la dependencia de Composer laravel/dusk a tu proyecto:

composer require laravel/dusk --dev

[!WARNING] Si estás registrando manualmente el proveedor de servicios de Dusk, nunca debes registrarlo en tu entorno de producción, ya que hacerlo podría permitir que usuarios arbitrarios se autentiquen con tu aplicación. Después de instalar el paquete Dusk, ejecuta el comando Artisan dusk:install. El comando dusk:install creará un directorio tests/Browser, un ejemplo de prueba Dusk, e instalará el binario del 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.

[!NOTE] Si estás utilizando Laravel Sail para gestionar tu entorno de desarrollo local, consulta también la documentación de Sail sobre configuración y ejecución de pruebas Dusk.

Gestión de Instalaciones de ChromeDriver

Si deseas instalar una versión diferente de ChromeDriver a la que se instala 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

[!WARNING] Dusk requiere que los binarios de chromedriver sean ejecutables. Si tienes problemas para ejecutar Dusk, debes asegurarte de que los binarios sean ejecutables utilizando el siguiente comando: chmod -R 0755 vendor/laravel/dusk/bin/.

Usando 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 en cualquier navegador que desees. Para empezar, abre tu archivo tests/DuskTestCase.php, que es el caso de prueba base de 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: A continuación, puedes modificar el método driver para conectarte a la URL y el puerto de tu elección. Además, puedes modificar las "capacidades deseadas" que deben pasarse al WebDriver:

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

Comenzando

Generando Pruebas

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

php artisan dusk:make LoginTest

Restableciendo 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 aprovecha las transacciones de base de datos, que no serán aplicables o disponibles a través de solicitudes HTTP. En su lugar, tienes dos opciones: el rasgo DatabaseMigrations y el rasgo DatabaseTruncation.

Usando Migraciones de Base de Datos

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

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

[!WARNING] Las bases de datos en memoria de SQLite no pueden usarse al ejecutar pruebas de Dusk. Dado que el navegador se ejecuta en su propio proceso, no podrá acceder a las bases de datos en memoria de otros procesos.

Uso de Truncamiento de Base de Datos

El trait 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, lo que proporcionará un aumento de velocidad en comparación con la ejecución de todas tus migraciones de base de datos nuevamente:

<?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 defecto, este trait 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:

[!NOTA] Si estás usando Pest, debes definir propiedades o métodos en la clase base DuskTestCase o en cualquier clase que tu archivo de prueba extienda.

/**
* Indicates which tables should be truncated.
*
* @var array
*/
protected $tablesToTruncate = ['users'];

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

/**
* Indicates which tables should be excluded from truncation.
*
* @var array
*/
protected $exceptTables = ['users'];

Para especificar las conexiones a la base de datos cuyas tablas deben ser truncadas, puedes definir una propiedad $connectionsToTruncate en tu clase de prueba:

/**
* Indicates which connections should have their tables truncated.
*
* @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:

/**
* Perform any work that should take place before the database has started truncating.
*/
protected function beforeTruncatingDatabase(): void
{
//
}
 
/**
* Perform any work that should take place after the database has finished truncating.
*/
protected function afterTruncatingDatabase(): void
{
//
}

Ejecutando Pruebas

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

php artisan dusk

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

php artisan dusk:fails

El comando dusk acepta cualquier argumento que normalmente sea aceptado por el runner de pruebas Pest / PHPUnit, como permitirte ejecutar solo las pruebas para 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 configuración y ejecución de pruebas Dusk.

Iniciando ChromeDriver Manualmente

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

/**
* Prepare for Dusk test execution.
*
* @beforeClass
*/
public static function prepare(): void
{
// static::startChromeDriver();
}

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

use Facebook\WebDriver\Remote\RemoteWebDriver;
 
/**
* Create the RemoteWebDriver instance.
*/
protected function driver(): RemoteWebDriver
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}

Manejo de Entornos

Para forzar a Dusk a que utilice su propio archivo de entorno al ejecutar pruebas, crea un archivo .env.dusk.{environment} en la raíz de tu proyecto. Por ejemplo, si iniciarás el comando dusk desde tu entorno local, deberías crear un archivo .env.dusk.local. Al ejecutar pruebas, Dusk respaldará tu archivo .env y renombrará tu entorno Dusk a .env. Una vez que se hayan completado las pruebas, se restaurará tu archivo .env.

Conceptos básicos del navegador

Creando 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
 
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;
 
/**
* A basic browser test example.
*/
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 puedes ver en el ejemplo anterior, el método browse acepta una función anónima. Una instancia del navegador se pasará automáticamente a esta función anónima por Dusk y es el objeto principal utilizado para interactuar y hacer afirmaciones sobre tu aplicación.

Creando Múltiples Navegadores

A veces es posible que necesites múltiples navegadores para llevar a cabo una prueba de manera correcta. Por ejemplo, se pueden necesitar múltiples navegadores para probar una pantalla de chat que interactúa con websockets. Para crear múltiples navegadores, simplemente añade más argumentos de navegador a la firma de la función anónima dada 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 nombrada:

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

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

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

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

$browser->refresh();

Redimensionando 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 ajustará el tamaño de la ventana del navegador para que coincida con el tamaño de su contenido:

$browser->fitContent();

Cuando una prueba falla, Dusk automáticamente cambiará el tamaño del navegador para ajustarse al contenido antes de tomar una captura de pantalla. Puedes desactivar 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 una variedad de tus pruebas, puedes usar el método macro en la clase Browser. Típicamente, deberías 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
{
/**
* Register Dusk's browser macros.
*/
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 una función anónima como su segundo. La función anónima del macro se ejecutará al llamar al 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 del modelo autenticable:

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

[!WARNING] Después de usar el método loginAs, la sesión del 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 encriptada. Por defecto, todas las cookies creadas por Laravel están encriptadas:

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

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

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

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

$browser->deleteCookie('name');

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

Tomando una 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');

El método screenshotElement se puede utilizar para tomar una captura de pantalla de un elemento específico en la página:

$browser->screenshotElement('#selector', 'filename');

Almacenando la Salida de la Consola en el Disco

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

$browser->storeConsoleLog('filename');

Almacenando el Código de la Página en Disco

Puedes usar el método storeSource para escribir el código fuente de la página actual en el disco con el nombre de archivo dado. El código fuente de la página se almacenará dentro del directorio tests/Browser/source:

$browser->storeSource('filename');

Interactuando Con Elementos

Selectores de 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>
 
// Test...
 
$browser->click('.login-page .container div > button');

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

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

Si lo deseas, puedes personalizar el atributo HTML que utiliza el selector de Dusk a través del método selectorHtmlAttribute. Típicamente, este método debe ser llamado desde el método boot del AppServiceProvider de tu aplicación:

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

Texto, Valores y Atributos

Recuperando y Estableciendo 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, utiliza el método value:

// Retrieve the value...
$value = $browser->value('selector');
 
// Set the value...
$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');

Recuperando Texto

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

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

Recuperando Atributos

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

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

Interactuando con Formularios

Escribiendo 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', 'taylor@laravel.com');

Ten en cuenta que, aunque el método acepta uno si es necesario, no estamos obligados a pasar un selector CSS en el método type. Si no se proporciona un selector CSS, Dusk buscará un campo input o textarea con el atributo name dado. Para añadir texto a un campo sin borrar su contenido, puedes usar el método append:

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

Puedes limpiar el valor de una entrada utilizando el método clear:

$browser->clear('email');

Puedes instruir a Dusk para que escriba lentamente utilizando el método typeSlowly. Por defecto, Dusk pausará 100 milisegundos entre las pulsaciones de teclas. Para personalizar la cantidad de tiempo entre las pulsaciones de teclas, puedes pasar el número adecuado de milisegundos como el 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 añadir texto de manera gradual:

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

Dropdowns

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 la 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 un array como segundo argumento al método select, puedes instruir al método para que seleccione múltiples opciones:

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

Casillas de verificación

Para "marcar" una entrada de 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 con un selector CSS, Dusk buscará una casilla de verificación con un atributo name coincidente:

$browser->check('terms');

El método uncheck se puede utilizar para "desmarcar" una entrada de casilla de verificación:

$browser->uncheck('terms');

Botones de Opción

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

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

Adjuntando Archivos

El método attach se puede utilizar para adjuntar un archivo a un elemento de entrada 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 que coincida:

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

[!WARNING] La función attach requiere que la extensión Zip de PHP esté instalada y habilitada en tu servidor.

Presionando 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 ya sea el texto en pantalla del botón o un selector CSS / Dusk:

$browser->press('Login');

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

// Press the button and wait a maximum of 5 seconds for it to be enabled...
$browser->pressAndWaitFor('Save');
 
// Press the button and wait a maximum of 1 second for it to be enabled...
$browser->pressAndWaitFor('Save', 1);

Haciendo 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 un enlace con el texto de visualización dado es visible en la página:

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

[!WARNING] 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.

Usando el 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 instruir a Dusk para que mantenga teclas modificadoras mientras ingresa valores. En este ejemplo, se mantendrá la tecla shift mientras se ingresa taylor en el elemento que coincide con el selector dado. Después de que se escriba taylor, se escribirá swift sin ninguna tecla modificadora:

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

Otro caso de uso valioso para el método keys es enviar una combinación de "atajos de teclado" 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 puede encontrar en GitHub.

Interacciones de Teclado Fluentes

Dusk también proporciona un método withKeyboard, lo que te permite realizar interacciones complejas con el teclado de forma fluida a través de la clase Laravel\Dusk\Keyboard. La clase Keyboard ofrece 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 de Teclado

Si deseas definir interacciones de teclado personalizadas que puedas reutilizar fácilmente a lo largo de tu suite de pruebas, puedes usar el método macro proporcionado por la clase Keyboard. Típicamente, 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
{
/**
* Register Dusk's browser macros.
*/
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 una función anónima como su segundo. La función anónima del macro se ejecutará al llamar al 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());

Usando el Ratón

Haciendo 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 usar para hacer clic en un elemento que coincide con la expresión XPath dada:

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

El método clickAtPoint se puede usar para hacer clic en el elemento más alto 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();
 
$browser->doubleClick('.selector');

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

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

El método clickAndHold se puede utilizar para simular un botón del mouse siendo clicado y mantenido presionado. Un llamado posterior al método releaseMouse deshará este comportamiento y soltará el botón del mouse:

$browser->clickAndHold('.selector');
 
$browser->clickAndHold()
->pause(1000)
->releaseMouse();

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

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

Mouseover

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

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

Arrastrar y soltar

El método drag se puede usar para arrastrar un elemento que coincide con el selector dado a otro elemento:

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

O bien, 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 de JavaScript

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

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

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

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

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

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

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

$browser->acceptDialog();

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

$browser->dismissDialog();

Interactuando con Marcos en Línea

Si necesitas interactuar con elementos dentro de un iframe, puedes usar el método withinFrame. Todas las interacciones con elementos que tengan lugar dentro de la función anónima proporcionada 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"]', '1224')
->type('input[name="cvc"]', '123')
->press('Pay');
});

Selectores de Alcance

A veces es posible que desees realizar varias operaciones mientras limites todas las operaciones a un selector dado. Por ejemplo, es posible que desees afirmar que algún 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 de la función anónima dada al método with estarán limitadas al selector original:

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

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

$browser->with('.table', function (Browser $table) {
// Current scope is `body .table`...
 
$browser->elsewhere('.page-title', function (Browser $title) {
// Current scope is `body .page-title`...
$title->assertSee('Hello World');
});
 
$browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
// Current scope is `body .page-title`...
$title->assertSee('Hello World');
});
});

Esperando Elementos

Al probar aplicaciones que utilizan JavaScript de manera extensiva, a menudo es necesario "esperar" a que ciertos elementos o datos estén disponibles antes de continuar con una prueba. Dusk lo hace muy fácil. Usando una variedad de métodos, puedes esperar a que los elementos se vuelvan visibles en la página o incluso esperar hasta que una expresión JavaScript dada evalúe a true.

Esperando

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, usa el método pauseIf:

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

Del mismo modo, 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);

Esperando Selectores

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

// Wait a maximum of five seconds for the selector...
$browser->waitFor('.selector');
 
// Wait a maximum of one second for the selector...
$browser->waitFor('.selector', 1);

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

// Wait a maximum of five seconds for the selector to contain the given text...
$browser->waitForTextIn('.selector', 'Hello World');
 
// Wait a maximum of one second for the selector to contain the given text...
$browser->waitForTextIn('.selector', 'Hello World', 1);

También puedes esperar hasta que el elemento que coincide con el selector dado esté ausente de la página:

// Wait a maximum of five seconds until the selector is missing...
$browser->waitUntilMissing('.selector');
 
// Wait a maximum of one second until the selector is missing...
$browser->waitUntilMissing('.selector', 1);

O, puedes esperar hasta que el elemento que coincide con el selector dado sea habilitado o deshabilitado:

// Wait a maximum of five seconds until the selector is enabled...
$browser->waitUntilEnabled('.selector');
 
// Wait a maximum of one second until the selector is enabled...
$browser->waitUntilEnabled('.selector', 1);
 
// Wait a maximum of five seconds until the selector is disabled...
$browser->waitUntilDisabled('.selector');
 
// Wait a maximum of one second until the selector is disabled...
$browser->waitUntilDisabled('.selector', 1);

Selectores de Ámbito 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 una ventana modal esté disponible y luego presionar el botón "OK" dentro de la modal. El método whenAvailable se puede usar para lograr esto. Todas las operaciones de elementos realizadas dentro de la función anónima dada se limitarán al selector original:

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

Esperando Texto

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

// Wait a maximum of five seconds for the text...
$browser->waitForText('Hello World');
 
// Wait a maximum of one second for the text...
$browser->waitForText('Hello World', 1);

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

// Wait a maximum of five seconds for the text to be removed...
$browser->waitUntilMissingText('Hello World');
 
// Wait a maximum of one second for the text to be removed...
$browser->waitUntilMissingText('Hello World', 1);

Esperando Enlaces

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

// Wait a maximum of five seconds for the link...
$browser->waitForLink('Create');
 
// Wait a maximum of one second for the link...
$browser->waitForLink('Create', 1);

Esperando entradas

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

// Wait a maximum of five seconds for the input...
$browser->waitForInput($field);
 
// Wait a maximum of one second for the input...
$browser->waitForInput($field, 1);

Esperando la Ubicación de la Página

Al hacer 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 utilizar para esperar a que la ubicación de la ventana actual sea una URL completamente calificada:

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

También puedes esperar la ubicación de una ruta nombrada:

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

Esperando Reinicios de Página

Si necesitas esperar a que una página se recargue después de realizar una acción, utiliza 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 la página se recargue generalmente ocurre después de hacer clic en un botón, puedes usar el método clickAndWaitForReload por conveniencia:

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

Esperando Expresiones de 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 lograr esto fácilmente utilizando el método waitUntil. Al pasar una expresión a este método, no necesitas incluir la palabra clave return o un punto y coma al final:

// Wait a maximum of five seconds for the expression to be true...
$browser->waitUntil('App.data.servers.length > 0');
 
// Wait a maximum of one second for the expression to be true...
$browser->waitUntil('App.data.servers.length > 0', 1);

Esperando Expresiones de Vue

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

// Wait until the component attribute contains the given value...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
 
// Wait until the component attribute doesn't contain the given value...
$browser->waitUntilVueIsNot('user.name', null, '@user');

Esperando Eventos de JavaScript

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

$browser->waitForEvent('load');

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

$browser->with('iframe', function (Browser $iframe) {
// Wait for the iframe's load event...
$iframe->waitForEvent('load');
});

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

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

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

// Wait until the document is scrolled...
$browser->waitForEvent('scroll', 'document');
 
// Wait a maximum of five seconds until the window is resized...
$browser->waitForEvent('resize', 'window', 5);

Esperando con una Callback

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

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

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

Afirmaciones Disponibles

Dusk ofrece una variedad de afirmaciones que puedes hacer contra tu aplicación. Todas las afirmaciones disponibles están documentadas en la lista a continuación:

assertTitle

Asegúrate de que el título de la página coincida con el texto dado:

$browser->assertTitle($title);

assertTitleContains

Afirmar que el título de la página contiene el texto dado:

$browser->assertTitleContains($title);

assertUrlIs

Asegúrate de que la URL actual (sin la cadena de consulta) coincida con la cadena dada:

$browser->assertUrlIs($url);

assertSchemeIs

Asegúrate de que el esquema de URL actual coincida con el esquema dado:

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

Asegúrate de que el esquema de URL actual no coincida con el esquema dado:

$browser->assertSchemeIsNot($scheme);

assertHostIs

Asegúrate de que el host de la URL actual coincida con el host dado:

$browser->assertHostIs($host);

assertHostIsNot

Asegúrate de que el host de la URL actual no coincida con el host dado:

$browser->assertHostIsNot($host);

assertPortIs

Asegúrate de que el puerto de la URL actual coincida con el puerto dado:

$browser->assertPortIs($port);

assertPortIsNot

Asegúrate de que el puerto de la URL actual no coincida con el puerto dado:

$browser->assertPortIsNot($port);

assertPathBeginsWith

Afirmar que la ruta de URL actual comienza con la ruta dada:

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

assertPathEndsWith

Afirmar que la ruta de URL actual termina con la ruta dada:

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

assertPathContains

Asegúrate de que la ruta de URL actual contenga la ruta dada:

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

assertPathIs

Afirmar que la ruta actual coincide con la ruta dada:

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

assertPathIsNot

Asegúrate de que la ruta actual no coincide con la ruta dada:

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

assertRouteIs

Afirmar que la URL actual coincide con la URL de la ruta nombrada dada:

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

assertQueryStringHas

Asegúrate de que el parámetro de cadena de consulta dado esté presente:

$browser->assertQueryStringHas($name);

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

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

assertQueryStringMissing

Asegúrate de que el parámetro de cadena de consulta dado esté ausente:

$browser->assertQueryStringMissing($name);

assertFragmentIs

Afirmar que el fragmento hash actual de la URL coincide con el fragmento dado:

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

Asegúrate de que el fragmento hash actual de la URL comience con el fragmento dado:

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

Asegúrate de que el fragmento hash actual de la URL no coincida con el fragmento dado:

$browser->assertFragmentIsNot('anchor');

assertHasCookie

Asegúrate de que la cookie encriptada dada esté presente:

$browser->assertHasCookie($name);

assertHasPlainCookie

Asegúrate de que la cookie no encriptada dada esté presente:

$browser->assertHasPlainCookie($name);

assertCookieMissing

Asegúrate de que la cookie cifrada dada no esté presente:

$browser->assertCookieMissing($name);

assertPlainCookieMissing

Asegúrate de que la cookie no encriptada dada no esté presente:

$browser->assertPlainCookieMissing($name);

assertCookieValue

Asegúrate de que una cookie encriptada tenga un valor dado:

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

assertPlainCookieValue

Afirmar que una cookie no encriptada tiene un valor dado:

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

assertSee

Asegúrate de que el texto dado esté presente en la página:

$browser->assertSee($text);

assertDontSee

Asegúrate de que el texto dado no esté presente en la página:

$browser->assertDontSee($text);

assertSeeIn

Asegúrate de que el texto dado esté presente dentro del selector:

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

assertDontSeeIn

Afirma que el texto dado no está presente dentro del selector:

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

assertSeeAnythingIn

Asegúrate de que cualquier texto esté presente dentro del selector:

$browser->assertSeeAnythingIn($selector);

assertSeeNothingIn

Asegúrate de que no haya texto presente dentro del selector:

$browser->assertSeeNothingIn($selector);

assertScript

Afirmar que la expresión de JavaScript dada evalúa el valor dado:

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

assertSourceHas

Asegúrate de que el código fuente dado esté presente en la página:

$browser->assertSourceHas($code);

assertSourceMissing

Asegúrate de que el código fuente dado no esté presente en la página:

$browser->assertSourceMissing($code);

assertSeeLink

Asegúrate de que el enlace dado esté presente en la página:

$browser->assertSeeLink($linkText);

assertDontSeeLink

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

$browser->assertDontSeeLink($linkText);

assertInputValue

Afirmar que el campo de entrada dado tiene el valor dado:

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

assertInputValueIsNot

Afirmar que el campo de entrada dado no tiene el valor dado:

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

assertChecked

Afirmar que la casilla de verificación dada está marcada:

$browser->assertChecked($field);

assertNotChecked

Asegúrate de que la casilla de verificación dada no esté marcada:

$browser->assertNotChecked($field);

assertIndeterminate

Asegúrate de que la casilla de verificación dada esté en un estado indeterminado:

$browser->assertIndeterminate($field);

assertRadioSelected

Afirmar que el campo de radio dado está seleccionado:

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

assertRadioNotSelected

Asegúrate de que el campo de radio dado no esté seleccionado:

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

assertSelected

Afirmar que el dropdown dado tiene el valor dado seleccionado:

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

assertNotSelected

Asegúrate de que el menú desplegable dado no tiene el valor dado seleccionado:

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

assertSelectHasOptions

Asegúrate de que laArray de los valores dados estén disponibles para ser seleccionados:

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

assertSelectMissingOptions

Asegúrate de que el array dado de valores no esté disponible para ser seleccionado:

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

assertSelectHasOption

Asegúrate de que el valor dado esté disponible para ser seleccionado en el campo dado:

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

assertSelectMissingOption

Asegúrate de que el valor dado no esté disponible para ser seleccionado:

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

assertValue

Asegúrate de que el elemento que coincide con el selector dado tenga el valor dado:

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

assertValueIsNot

Afirma que el elemento que coincide con el selector dado no tiene el valor dado:

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

assertAttribute

Asegúrate de que el elemento que coincide con el selector dado tenga el valor dado en el atributo proporcionado:

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

assertAttributeContains

Asegúrate de que el elemento que coincide con el selector dado contiene el valor dado en el atributo proporcionado:

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

assertAttributeDoesntContain

Asegúrate de que el elemento que coincide con el selector dado no contenga el valor dado en el atributo proporcionado:

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

assertAriaAttribute

Asegúrate de que el elemento que coincide con el selector dado tiene el valor dado en el atributo aria proporcionado:

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

Por ejemplo, dada la marca <button aria-label="Add"></button>, puedes hacer una afirmación sobre el atributo aria-label de la siguiente manera:

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

assertDataAttribute

Asegúrate de 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="row-1" data-content="attendees"></tr>, puedes hacer una afirmación contra el atributo data-label de la siguiente manera:

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

assertVisible

Afirmar que el elemento que coincide con el selector dado es visible:

$browser->assertVisible($selector);

assertPresent

Asegúrate de que el elemento que coincide con el selector dado esté presente en la fuente:

$browser->assertPresent($selector);

assertNotPresent

Asegúrate de que el elemento que coincide con el selector dado no esté presente en la fuente:

$browser->assertNotPresent($selector);

assertMissing

Asegúrate de que el elemento que coincide con el selector dado no sea visible:

$browser->assertMissing($selector);

assertInputPresent

Asegúrate de que un input con el nombre dado esté presente:

$browser->assertInputPresent($name);

assertInputMissing

Afirma que una entrada con el nombre dado no está presente en la fuente:

$browser->assertInputMissing($name);

assertDialogOpened

Asegúrate de que se ha abierto un diálogo de JavaScript con el mensaje dado:

$browser->assertDialogOpened($message);

assertEnabled

Asegúrate de que el campo dado esté habilitado:

$browser->assertEnabled($field);

assertDisabled

Asegúrate de que el campo dado esté deshabilitado:

$browser->assertDisabled($field);

assertButtonEnabled

Afirmar que el botón dado está habilitado:

$browser->assertButtonEnabled($button);

assertButtonDisabled

Asegúrate de que el botón dado esté deshabilitado:

$browser->assertButtonDisabled($button);

assertFocused

Asegura que el campo dado esté enfocado:

$browser->assertFocused($field);

assertNotFocused

Asegúrate de que el campo dado no esté enfocado:

$browser->assertNotFocused($field);

assertAuthenticated

Asegúrate de que el usuario esté autenticado:

$browser->assertAuthenticated();

assertGuest

Asegúrate de que el usuario no esté autenticado:

$browser->assertGuest();

assertAuthenticatedAs

Asegúrate de 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 del componente Vue. Por ejemplo, imagina que tu aplicación contiene el siguiente componente Vue:

// HTML...
 
<profile dusk="profile-component"></profile>
 
// Component Definition...
 
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:

test('vue', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
});
/**
* A basic Vue test example.
*/
public function test_vue(): void
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}

assertVueIsNot

Asegúrate de que una propiedad de datos de componente Vue dada no coincida con el valor dado:

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

assertVueContains

Asegúrate de que una propiedad de datos del componente Vue dado sea un array y contenga el valor dado:

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

assertVueDoesntContain

Asegúrate de que una propiedad de datos del componente Vue dado sea un array y no contenga el valor dado:

$browser->assertVueDoesntContain($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. Dusk Pages 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.

Generando 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 será discutido en más detalle a continuación.

El Método url

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

/**
* Get the URL for the page.
*/
public function url(): string
{
return '/login';
}

El Método assert

El método assert puede hacer las afirmaciones necesarias para verificar que el navegador esté realmente en la página dada. No es necesario colocar nada dentro de este método; sin embargo, puedes hacer estas afirmaciones si lo deseas. Estas afirmaciones se ejecutarán automáticamente al navegar a la página:

/**
* Assert that the browser is on the page.
*/
public function assert(Browser $browser): void
{
$browser->assertPathIs($this->url());
}

Navegando a Páginas

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

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

A veces es posible que ya estés en una página dada y necesites "cargar" los selectores y métodos de la página en el contexto de prueba actual. Esto es común al presionar un botón y ser redirigido 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 atajos rápidos y fáciles de recordar para cualquier selector CSS en tu página. Por ejemplo, definamos un atajo para el campo de entrada "email" de la página de inicio de sesión de la aplicación:

/**
* Get the element shortcuts for the page.
*
* @return array<string, string>
*/
public function elements(): array
{
return [
'@email' => 'input[name=email]',
];
}

Una vez que se ha definido el atajo, puedes usar el selector abreviado en cualquier lugar donde típicamente usarías un selector CSS completo:

$browser->type('@email', 'taylor@laravel.com');

Selectores Abreviados Globales

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 usar para definir selectores abreviados globales que deben estar disponibles en cada página a lo largo de tu aplicación:

/**
* Get the global element shortcuts for the site.
*
* @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 usar a lo largo de tus pruebas. Por ejemplo, imaginemos que estamos 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 reescribir 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;
use Laravel\Dusk\Page;
 
class Dashboard extends Page
{
// Other page methods...
 
/**
* Create a new playlist.
*/
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 dentro de cualquier prueba que utilice la página. La instancia del navegador será pasada automáticamente como el primer argumento a los métodos de página personalizados:

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 UI y funcionalidad que se utilizan 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 URLs específicas.

Generando 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 puede existir en toda tu aplicación en una variedad de páginas. Puede volverse engorroso escribir manualmente la lógica de automatización del navegador para seleccionar una fecha en docenas de pruebas a lo largo de tu suite de pruebas. En su lugar, podemos definir un componente 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
{
/**
* Get the root selector for the component.
*/
public function selector(): string
{
return '.date-picker';
}
 
/**
* Assert that the browser page contains the component.
*/
public function assert(Browser $browser): void
{
$browser->assertVisible($this->selector());
}
 
/**
* Get the element shortcuts for the component.
*
* @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',
];
}
 
/**
* Select the given date.
*/
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

Una vez que se ha definido el componente, podemos seleccionar fácilmente una fecha dentro del selector de fechas desde cualquier prueba. Y, si la lógica necesaria para seleccionar una fecha cambia, solo necesitamos actualizar el 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
{
/**
* A basic component test example.
*/
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

[!WARNING] 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, debes asegurarte 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, añade 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-chrome-for-testing" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /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 de Dusk en Travis CI, utiliza la siguiente configuración de .travis.yml. Dado que Travis CI no es un entorno gráfico, necesitaremos tomar algunos pasos adicionales para lanzar un navegador Chrome. Además, utilizaremos php artisan serve para iniciar el servidor web integrado de PHP:

language: php
 
php:
- 8.2
 
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 de 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 --port=9515 &
- 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@v4
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v4
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. Usaremos el servidor integrado de PHP para ejecutar Laravel, de modo que podamos 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 la ejecución de pruebas Dusk en Chipper CI, incluyendo cómo usar bases de datos, consulta la documentación oficial de Chipper CI.