# Laravel Dusk

# Introducción

Laravel Dusk proporciona una API de automatización y prueba para navegador expresiva y fácil de usar. De forma predeterminada, Dusk no requiere que instales JDK o Selenium en tu computador. En su lugar, Dusk usa una instalación de ChromeDriver independiente. Sin embargo, siéntete libre de utilizar cualquier otro driver compatible con Selenium que desees.

# Instalación

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

composer require --dev laravel/dusk

Nota

Si estás registrando manualmente el proveedor de servicio de Dusk, nunca deberías registrarlo en tu entorno de producción, ya que hacerlo así podría conducir a que usuarios arbitrarios sean capaces de autenticarse en tu aplicación.

Después de la instalación del paquete Dusk, ejecuta el comando Artisan dusk:install:

php artisan dusk:install

Un directorio Browser será creado dentro de tu directorio tests y contendrá una prueba de ejemplo. Seguido, establece la variable de entorno APP_URL en tu archivo .env. Este valor debería coincidir con la URL que uses para acceder a tu aplicación en un navegador.

Para ejecutar tus pruebas, usa el comando de Artisan dusk. El comando dusk acepta cualquier argumento que también sea aceptado por el comando phpunit:

php artisan dusk

Si tuviste fallos en las pruebas la última vez que se ejecutó el comando dusk, puedes ahorrar tiempo volviendo a ejecutar las pruebas fallidas usando el comando dusk: fail:

php artisan dusk:fails

# Administrando las instalaciones de ChromeDriver

Si te gustaria instalar una versión diferente de ChromeDriver a la incluida con Laravel Dusk, 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 74

# Install a given version of ChromeDriver for all supported OSs...
php artisan dusk:chrome-driver --all

Nota

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

# Usando otros navegadores

De forma predeterminada, Dusk usa Google Chrome y una instalación de ChromeDriver independiente 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, el cual es el caso de prueba de Dusk básico para tu aplicación. Dentro de este archivo, puedes remover la ejecución del método startChromeDriver. Esto evitará que Dusk inicie automáticamente ChromeDriver:

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

Luego de esto, puedes modificar el método driver para conectar a la URL y puerto de tu preferencia. Además, puedes modificar las "capacidades deseadas" que deberían ser pasadas al WebDriver:

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

# Primeros pasos

# Generando pruebas

Para generar una prueba de Dusk, usa el comando de Artisan dusk:make. La prueba generada será colocada en el directorio tests/Browser:

php artisan dusk:make LoginTest

# Ejecutando pruebas

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

php artisan dusk

Si tuviste fallos en las pruebas la última vez que se ejecutó el comando dusk, puedes ahorrar tiempo volviendo a ejecutar las pruebas fallidas usando el comando dusk: fail:

php artisan dusk:fails

El comando dusk acepta cualquier argumento que sea aceptado normalmente por el administrador de pruebas de PHPUnit, permitiendo que ejecutes solamente las pruebas para un grupo dado, etc:

php artisan dusk --group=foo

# Iniciando manualmente ChromeDriver

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

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

Además, si inicias ChromeDriver en un puerto diferente a 9515, deberías modificar el método driver de la misma clase:

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

# Manejo de entorno

Para forzar que Dusk use su propio archivo de entorno al momento de ejecutar las pruebas, crea un archivo .env.dusk.{environment} en el directorio raíz de tu proyecto. Por ejemplo, si estás iniciando el comando dusk desde tu entorno local, deberías crear un archivo .env.dusk.local.

Al momento de ejecutar pruebas, Dusk respaldará tu archivo .env y renombrará tu entorno Dusk a .env. Una vez que las pruebas han sido completadas, tu archivo .env será restaurado.

# Creando navegadores

Para empezar, vamos a escribir una prueba que verifica que podemos entrar a nuestra aplicación. Después de generar una prueba, podemos modificarla para visitar la página de login, introducir algunas credenciales y presionar el botón "Login". Para crear una instancia del navegador, ejecuta el método browse:

<?php

namespace Tests\Browser;

use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;

    /**
    * A basic browser test example.
    *
    * @return void
    */
    public function testBasicExample()
    {
        $user = factory(User::class)->create([
            'email' => 'taylor@laravel.com',
        ]);

        $this->browse(function ($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 callback. Una instancia de navegador será pasada automáticamente a esta función de retorno por Dusk y es el objeto principal utilizado para interactuar y hacer aserciones en la aplicación.

# Creando múltiples navegadores

Algunas veces puedes necesitar múltiples navegadores con el propósito de ejecutar apropiadamente una prueba. Por ejemplo, múltiples navegadores pueden ser necesitados para probar una pantalla de conversaciones que interactúa con websockets. Para crear múltiples navegadores, "solicita" más de un navegador en la firma del callback dado al método browse:

$this->browse(function ($first, $second) {
    $first->loginAs(User::find(1))
            ->visit('/home')
            ->waitForText('Message');

    $second->loginAs(User::find(2))
            ->visit('/home')
            ->waitForText('Message')
            ->type('message', 'Hey Taylor')
            ->press('Send');

    $first->waitForText('Hey Taylor')
            ->assertSee('Jeffrey Way');
});

# Redimensionando 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 puede ser usado para maximizar la ventana del navegador:

$browser->maximize();

# Macros de navegador

Si desea definir un método de navegador personalizado que puedas reutilizar en una variedad de tus pruebas, puedes usar el método macro en la claseBrowser. Normalmente, deberías llamar a este método desde el método boot del proveedor de servicios:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;

class DuskServiceProvider extends ServiceProvider
{
    /**
    * Register the Dusk's browser macros.
    *
    * @return void
    */
    public function boot()
    {
        Browser::macro('scrollToElement', function ($element = null) {
            $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");

            return $this;
        });
    }
}

La función macro acepta un nombre como primer argumento y un Closure como segundo. El Closure del macro se ejecutará cuando se llame al macro como un método en una implementación de Browser:

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

# Autenticación

Frecuentemente, estarás probando páginas que requieren autenticación. Puedes usar el método loginAs de Dusk con el propósito de evitar interactuar con la pantalla de login durante cada prueba. El método loginAs acepta un ID de usuario o una instancia de modelo de usuario:

$this->browse(function ($first, $second) {
    $first->loginAs(User::find(1))
            ->visit('/home');
});

Nota

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

# Migraciones de bases de datos

Cuando tu prueba requiere migraciones, como el ejemplo de autenticación visto antes, nunca deberías usar el trait RefreshDatabase. El trait RefreshDatabase se apoya en transacciones de base de datos, las cuales no serán aplicables a traves de las solicitudes HTTP. En su lugar, usa el trait DatabaseMigrations:

<?php

namespace Tests\Browser;

use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;
}

# 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 las pruebas de Dusk. Con el tiempo, los cambios del diseño frontend pueden causar que los selectores CSS como los siguientes dañen tus pruebas:

// HTML...

<button>Login</button>

// Test...

$browser->click('.login-page .container div > button');

Los selectores de Dusk permiten que te enfoques en la escritura de pruebas efectivas en vez de recordar selectores CSS. Para definir un selector, agrega un atributo dusk a tu elemento HTML. Después, agrega un prefijo al selector con @ para manipular el elemento conectado dentro de una prueba de Dusk:

// HTML...

<button dusk="login-button">Login</button>

// Test...

$browser->click('@login-button');

# Haciendo clic en enlaces

Para hacer clic sobre 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 dado en la pantalla:

$browser->clickLink($linkText);

Nota

Este método interactúa con jQuery. Si jQuery no está disponible en la página, Dusk lo inyectará automáticamente de modo que esté disponible por la duración de la prueba.

# Texto, Valores y Atributos

# Obteniendo y estableciendo valores

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

// Retrieve the value...
$value = $browser->value('selector');

// Set the value...
$browser->value('selector', 'value');

# Obteniendo texto

El método text puede ser usado para obtener el texto de pantalla de un elemento que coincida con el selector dado:

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

# Obteniendo atributos

Finalmente, el método attribute puede ser usado para obtener un atributo de un elemento que coincida con el selector dado:

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

# Usando Formularios

# Escribiendo valores

Dusk proporciona una variedad de métodos para interactuar con formularios y elementos de entrada. Primero, vamos a echar un vistazo a un ejemplo de escribir texto dentro de un campo de entrada:

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

Nota que, aunque el método acepta uno si es necesario, no estamos obligados a pasar un selector CSS dentro del método type. Si un selector CSS no es proporcionado, Dusk buscará un campo de entrada con el atributo name dado. Finalmente, Dusk intentará encontrar un textarea con el atributo name dado.

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

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

Puedes limpiar el valor de un campo usando el método clear:

$browser->clear('email');

# Listas desplegables

Para seleccionar un valor en un cuadro de selección de lista desplegable, puedes usar el método select. Al momento de pasar un valor al método select, deberías pasar el valor de opción a resaltar en lugar del texto mostrado en pantalla:

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

Puedes seleccionar una opción aleatoria al omitir el segundo parámetro:

$browser->select('size');

# Casillas de verificación

Para "marcar" un campo de casilla de verificación, puedes usar el método check. Al igual que muchos otros métodos relacionados con entradas, un selector CSS completo no es obligatorio. Si un selector que coincida exactamente no puede ser encontrado, Dusk buscará una casilla de verificación con un atributo name coincidente.

$browser->check('terms');

$browser->uncheck('terms');

# Botones de radio

Para "seleccionar" una opción de botón de radio, puedes usar el método radio. Al igual que muchos otros métodos relacionados con campos, un selector CSS completo no es obligatorio. Si un selector que coincida exactamente no puede ser encontrado, Dusk buscará un radio con atributos name y value coincidentes:

$browser->radio('version', 'php7');

# Adjuntando archivos

El método attach puede ser usado para adjuntar un archivo a un elemento file. Al igual que muchos otros métodos relacionados con campos, un selector CSS completo no es obligatorio. Si un selector que coincida exactamente no puede ser encontrado, Dusk buscará un campo de archivo con atributo name coincidente:

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

Nota

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

# Usando el teclado

El método keys permite que proporciones secuencias de entrada más complejas para un elemento dado que lo permitido normalmente por el método type. Por ejemplo, puedes mantener presionada las teclas modificadoras al introducir valores. En este ejemplo, la tecla shift será mantenida presionada mientras la palabra taylor es introducida dentro del elemento que coincida con el selector dado. Después de que la palabra taylor sea tipeada, la palabra otwell será tipeada sin alguna tecla modificadora:

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

Incluso puedes enviar una "tecla de función" al selector CSS principal que contiene tu aplicación:

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

TIP TIP

Todas las teclas modificadoras se envuelven entre corchetes {} y coinciden con las constantes definidas en la clase Facebook\WebDriver\WebDriverKeys, la cual puede ser encontrada en GitHub.

# Usando el Ratón

# Haciendo clic sobre elementos

El método click puede ser usado para "clickear" sobre un elemento que coincida con el selector dado:

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

# Mouseover

El método mouseover puede ser usado cuando necesitas mover el ratón sobre un elemento que coincida con el selector dado:

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

# Arrastrar y soltar

El método drag puede ser usado para arrastrar un elemento que coincida con el selector dado hasta otro elemento:

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

O, puedes arrastrar un elemento en una única dirección:

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

# Diálogos de JavaScript

Dusk provee de varios métodos para interactuar con Diálogos de JavaScript:

// Wait for a dialog to appear:
$browser->waitForDialog($seconds = null);

// Assert that a dialog has been displayed and that its message matches the given value:
$browser->assertDialogOpened('value');

// Type the given value in an open JavaScript prompt dialog:
$browser->typeInDialog('Hello World');

Para cerrar un Diálogo de JavaScript abierto, haga clic en el botón Aceptar o OK:

$browser->acceptDialog();

Para cerrar un Diálogo de JavaScript abierto, haga clic en el botón Cancelar (solo para un diálogo de confirmación):

$browser->dismissDialog();

# Alcance de selectores

Algunas veces puedes querer ejecutar varias operaciones dentro del alcance de un selector dado. Por ejemplo, puedes querer comprobar que algunos textos existen unicamente dentro de una tabla y después presionar un botón dentro de la tabla. Puedes usar el método with para completar esta tarea. Todas las operaciones ejecutadas dentro de la función de retorno dada al método with serán exploradas en el selector original:

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

# Esperando por elementos

Al momento de probar aplicaciones que usan JavaScript de forma extensiva, frecuentemente se vuelve necesario "esperar" por ciertos elementos o datos estén disponibles antes de proceder con una prueba. Dusk hace esto fácilmente. Usando una variedad de métodos, puedes esperar que los elementos estén visibles en la página e incluso esperar hasta que una expresión de JavaScript dada se evalúe como true.

# Esperando

Si necesitas pausar la prueba por un número de milisegundos dado, usa el método pause:

$browser->pause(1000);

# Esperando por selectores

El método waitFor puede ser usado para pausar la ejecución de la prueba hasta que el elemento que coincida con el selector CSS dado sea mostrado en la página. De forma predeterminada, esto pausará la prueba por un máximo de cinco segundos antes de arrojar una excepción. Si es necesario, puedes pasar un umbral de tiempo de expiración personalizado como segundo argumento del 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 puede esperar hasta que el selector dado no se encuentre en la página:

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

$browser->waitUntilMissing('.selector', 1);

# Estableciendo el alcance de selectores cuando estén disponibles

Ocasionalmente, puedes querer esperar por un selector dado y después interactuar con el elemento que coincida con el selector. Por ejemplo, puedes querer esperar hasta que una ventana modal esté disponible y después presionar el botón "OK" dentro de esa ventana. El método whenAvailable puede ser usado en este caso. Todas las operaciones de elementos ejecutadas dentro de la función de retorno dada serán ejecutadas dentro del selector original:

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

# Esperando por texto

El método waitForText puede ser usado para esperar hasta que el texto dado sea mostrado 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);

# Esperando por enlaces

El método waitForLink puede ser usado para esperar hasta que un enlace dado sea mostrada 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 por la localización de la página

Al momento de hacer una comprobación de ruta tal como $browser->assertPathIs('/home'), la comprobación puede fallar si window.location.pathname está siendo actualizada asincrónicamente. Puedes usar el método waitForLocation para esperar por la localización que tenga un valor dado:

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

También puede esperar la localización de una ruta con nombre:

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

# Esperando por recargas de página

Si necesita hacer aserciones después de que se ha recargado una página, usa el método waitForReload:

$browser->click('.some-action')
        ->waitForReload()
        ->assertSee('something');

# Esperando por expresiones de JavaScript

Algunas veces puedes querer pausar la ejecución de una prueba hasta que una expresión de JavaScript dada se evalúe a true. Puedes completar fácilmente esto usando el método waitUntil. Al momento de pasar una expresión a este método, no necesitas incluir al final la palabra clave return o un punto y coma ;:

// Wait a maximum of five seconds for the expression to be true...
$browser->waitUntil('App.dataLoaded');

$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 por expresiones de Vue

Los siguientes métodos puedes ser usados para esperar hasta que un atributo de componente de Vue dado tenga un determinado valor:

// 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 por una función de retorno

Muchos de los métodos de "espera" en Dusk confían en el método waitUsing subyacente. Puedes usar este método directamente para esperar por una función de retorno dada que devuelva true. El método waitUsing acepta el máximo número de segundos para esperar la Closure, el intervalo en el cual la Closure debería ser evaluada y un mensaje opcional de falla:

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

# Haciendo aserciones de Vue

Inclusive Dusk permite que hagas comprobaciones en el estado de componente de datos de Vue. Por ejemplo, imagina que tu aplicación contiene el siguiente componente de Vue:

// HTML...

<profile dusk="profile-component"></profile>

// Component Definition...

Vue.component('profile', {
    template: '<div>{{ user.name }}</div>',

    data: function () {
        return {
            user: {
                name: 'Taylor'
            }
        };
    }
});

Puedes comprobar el estado del componente de Vue de esta manera:

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

# Aserciones disponibles

Dusk proporciona una variedad de aserciones que puedes hacer en tu apliación. Todas las aserciones disponibles están documentadas en la tabla de abajo:

# assertTitle

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

$browser->assertTitle($title);

# assertTitleContains

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

$browser->assertTitleContains($title);

# assertUrlIs

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

$browser->assertUrlIs($url);

# assertSchemeIs

Comprueba que el esquema de la URL actual coincide con el esquema dado:

$browser->assertSchemeIs($scheme);

# assertSchemeIsNot

Comprueba que el esquema de la URL actual no coincide con el esquema dado:

$browser->assertSchemeIsNot($scheme);

# assertHostIs

Comprueba que el Host de la URL actual coincide con el Host dado:

$browser->assertHostIs($host);

# assertHostIsNot

Comprueba que el Host de la URL actual no coincide con el Host dado:

$browser->assertHostIsNot($host);

# assertPortIs

Comprueba que el puerto de la URL actual coincide con el puerto dado:

$browser->assertPortIs($port);

# assertPortIsNot

Comprueba que el puerto de la URL actual no coincide con el puerto dado:

$browser->assertPortIsNot($port);

# assertPathBeginsWith

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

$browser->assertPathBeginsWith($path);

# assertPathIs

Comprueba que la ruta actual coincida con la ruta dada:

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

# assertPathIsNot

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

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

# assertRouteIs

Comprueba que la URL actual coincida con la URL de ruta nombrada dada:

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

# assertQueryStringHas

Comprueba que el parámetro de cadena para una consulta dada está presente:

$browser->assertQueryStringHas($name);

Comprueba que el parámetro de cadena para una consulta dada está presente y tiene un valor dado:

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

# assertQueryStringMissing

Comprueba que el parámetro de cadena para una consulta dada está ausente:

$browser->assertQueryStringMissing($name);

# assertFragmentIs

Comprueba que el fragmento actual coincide con el fragmento dado:

$browser->assertFragmentIs('anchor');

# assertFragmentBeginsWith

Comprueba que el fragmento actual comienza con el fragmento dado:

$browser->assertFragmentBeginsWith('anchor');

# assertFragmentIsNot

AComprueba que el fragmento actual no coincide con el fragmento dado:

$browser->assertFragmentIsNot('anchor');

# assertHasCookie

Comprueba que el cookie dado está presente:

$browser->assertHasCookie($name);

# assertCookieMissing

Comprueba que el cookie dado no está presente:

$browser->assertCookieMissing($name);

# assertCookieValue

Comprueba que un cookie tenga un valor dado:

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

# assertPlainCookieValue

Comprueba que un cookie desencriptado tenga un valor dado:

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

# assertSee

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

$browser->assertSee($text);

# assertDontSee

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

$browser->assertDontSee($text);

# assertSeeIn

Comprueba que el texto dado está presente dentro del selector:

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

# assertDontSeeIn

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

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

# assertSourceHas

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

$browser->assertSourceHas($code);

# assertSourceMissing

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

$browser->assertSourceMissing($code);

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

$browser->assertSeeLink($linkText);

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

$browser->assertDontSeeLink($linkText);

# assertInputValue

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

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

# assertInputValueIsNot

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

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

# assertChecked

Comprueba que la casilla de verificación está marcada:

$browser->assertChecked($field);

# assertNotChecked

Comprueba que la casilla de verificación no está marcada:

$browser->assertNotChecked($field);

# assertRadioSelected

Comprueba que el campo de radio está seleccionado:

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

# assertRadioNotSelected

Comprueba que el campo de radio no está seleccionado:

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

# assertSelected

Comprueba que la lista desplegable tiene seleccionado el valor dado:

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

# assertNotSelected

Comprueba que la lista desplegable no tiene seleccionado el valor dado:

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

# assertSelectHasOptions

Comprueba que el arreglo dado de valores están disponibles para ser seleccionados:

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

# assertSelectMissingOptions

Comprueba que el arreglo dado de valores no están disponibles para ser seleccionados:

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

# assertSelectHasOption

Comprueba que el valor dado está disponible para ser seleccionado en el campo dado:

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

# assertValue

Comprueba que el elemento que coincida con el selector dado tenga el valor dado:

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

# assertVisible

Comprueba que el elemento que coincida con el selector dado sea visible:

$browser->assertVisible($selector);

# assertPresent

Comprueba que el elemento que coincida con el selector dado está presente:

$browser->assertPresent($selector);

# assertMissing

Comprueba que el elemento que coincida con el selector dado no sea visible:

$browser->assertMissing($selector);

# assertDialogOpened

Comprueba que un diálogo JavaScript con un mensaje dado ha sido abierto:

$browser->assertDialogOpened($message);

# assertEnabled

Comprueba que el campo dado está activado:

$browser->assertEnabled($field);

# assertDisabled

Comprueba que el campo dado está desactivado:

$browser->assertDisabled($field);

# assertFocused

Comprueba que el campo dado está enfocado:

$browser->assertFocused($field);

# assertNotFocused

Comprueba que el campo dado no está enfocado:

$browser->assertNotFocused($field);

# assertVue

Comprueba que una propiedad de datos de un componente de Vue dado coincida con el valor dado:

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

# assertVueIsNot

Comprueba que una propiedad de datos de un componente de Vue dado no coincida con el valor dado:

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

# assertVueContains

Comprueba que una propiedad de datos de un componente de Vue dado es un arreglo y contiene el valor dado:

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

# assertVueDoesNotContain

Comprueba que una propiedad de datos de un componente de Vue dado es un arreglo y no contiene el valor dado:

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

# Páginas

Alguna veces, las pruebas requieren que varias acciones complicadas sean ejecutadas en secuencia. Esto puede hacer tus pruebas más difíciles de leer y entender. Las páginas permiten que definas acciones expresivas que entonces pueden ser ejecutadas en una página dada usando un solo método. Las páginas también permiten que definas abreviaturas para selectores comunes para tu aplicación o una página única.

# Generando páginas

Para generar un objeto de página, usa el comando Artisan dusk:page. Todos los objetos de página serán colocados en el directorio tests/Browser/Pages:

php artisan dusk:page Login

# Configurando páginas

De forma predeterminada, 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 con más detalle debajo.

# El método url

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

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

# El método assert

El método assert puede hacer algunas aserciones necesarias para verificar que el navegador en realidad está en la página dada. Completar este método no es necesario; sin embargo, eres libre de hacer estas aserciones si lo deseas. Estas aserciones serán ejecutadas automáticamente al momento de navegar hacia la página:

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

Una vez que se ha configurado 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 determinada y necesitas "cargar" los selectores y métodos dentro del contexto de prueba actual. Esto es común al momento de presionar un botón y ser redireccionado a una página dada sin navegar explícitamente a ésta. 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 de páginas permite que definas abreviaturas rápidas, fáciles de recordar para cualquier selector CSS en tu página. Por ejemplo, vamos a definir una abreviación para el campo "email" de la página login de la aplicación:

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

Ahora, puedes usar este selector de abreviación en cualquier lugar que usarías un selector de CSS completo:

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

# Selectores de abreviaturas globales

Después de instalar Dusk, una clase Page básica será colocada en tu directorio tests/Browser/Pages. Esta clase contiene un método siteElements el cual puede ser usado para definir selectores de abreviaturas globales que deberían estar disponibles en cada página en cada parte de tu aplicación:

/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
    return [
        '@element' => '#selector',
    ];
}

# Métodos de página

Además de los métodos predeterminados definidos en páginas, puedes definir métodos adicionales, los cuales pueden ser usados en cualquier parte de tus pruebas. Por ejemplo, vamos a imaginar que estamos construyendo una aplicación para administració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
{
    // Other page methods...

    /**
    * Create a new playlist.
    *
    * @param  \Laravel\Dusk\Browser  $browser
    * @param  string  $name
    * @return void
    */
    public function createPlaylist(Browser $browser, $name)
    {
        $browser->type('name', $name)
                ->check('share')
                ->press('Create Playlist');
    }
}

Una vez que el método ha sido definido, puedes usarlo dentro de cualquier prueba que utilice la página. La instancia de navegador será pasada automáticamente al método 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 son planeados para partes de UI y funcionalidades que sean reusadas en otras partes de tu aplicación, tal como una barra de navegación o ventana de notificación. Como tal, los componentes no son enlazados a URLs específicas.

# Generando componentes

Para generar un componente, usa el comando Artisan dusk:component. Los nuevos componentes son colocados en el directorio test/Browser/Components:

php artisan dusk:component DatePicker

Como se muestra antes, un "calendario" es un ejemplo de un componente que puede existir en cualquier parte de tu aplicación en una variedad de páginas. Puede volverse complejo escribir manualmente lógica de automatización de navegador para seleccionar una fecha entre docenas de pruebas en cualquier parte de tu software de prueba. En lugar de esto, podemos definir un componente de Dusk para representar el calendario, permitiendo 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.
    *
    * @return string
    */
    public function selector()
    {
        return '.date-picker';
    }

    /**
    * Assert that the browser page contains the component.
    *
    * @param  Browser  $browser
    * @return void
    */
    public function assert(Browser $browser)
    {
        $browser->assertVisible($this->selector());
    }

    /**
    * Get the element shortcuts for the component.
    *
    * @return array
    */
    public function elements()
    {
        return [
            '@date-field' => 'input.datepicker-input',
            '@month-list' => 'div > div.datepicker-months',
            '@day-list' => 'div > div.datepicker-days',
        ];
    }

    /**
    * Select the given date.
    *
    * @param  \Laravel\Dusk\Browser  $browser
    * @param  int  $month
    * @param  int  $day
    * @return void
    */
    public function selectDate($browser, $month, $day)
    {
        $browser->click('@date-field')
                ->within('@month-list', function ($browser) use ($month) {
                    $browser->click($month);
                })
                ->within('@day-list', function ($browser) use ($day) {
                    $browser->click($day);
                });
    }
}

# Usando componentes

Una vez que el componente ha sido definido, fácilmente podemos seleccionar una fecha dentro del calendario desde cualquier prueba. Y, si la lógica necesaria para seleccionar una fecha cambia, solamente necesitaremos 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
{
    /**
    * A basic component test example.
    *
    * @return void
    */
    public function testBasicExample()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/')
                    ->within(new DatePicker, function ($browser) {
                        $browser->selectDate(1, 2018);
                    })
                    ->assertSee('January');
        });
    }
}

# Integración continua

# CircleCI

Si estás usando CircleCI para ejecutar tus pruebas de Dusk, puedes usar este archivo de configuración como punto de partida. Al igual que con TravisCI, usaremos el comando php artisan serve para ejecutar el servidor web integrado de PHP:

version: 2
jobs:
    build:
        steps:
            - run: sudo apt-get install -y libsqlite3-dev
            - run: cp .env.testing .env
            - run: composer install -n --ignore-platform-reqs
            - run: npm install
            - run: npm run production
            - run: vendor/bin/phpunit
    
            - run:
                name: Start Chrome Driver
                command: ./vendor/laravel/dusk/bin/chromedriver-linux
                background: true
    
            - run:
                name: Run Laravel Server
                command: php artisan serve
                background: true
    
            - run:
                name: Run Laravel Dusk Tests
                command: php artisan dusk
                    
            - store_artifacts:
                path: tests/Browser/screenshots

# Codeship

Para ejecutar pruebas de Dusk en Codeship, agrega los siguientes comandos a tu proyecto Codeship. Estos comandos son sólo un punto de partida y eres libre de agregar los comandos adicionales que necesites:

phpenv local 7.2
cp .env.testing .env
mkdir -p ./bootstrap/cache
composer install --no-interaction --prefer-dist
php artisan key:generate
nohup bash -c "php artisan serve 2>&1 &" && sleep 5
php artisan dusk

# Heroku CI

Para ejecutar tus pruebas de 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 > /dev/null 2>&1 &' && php artisan dusk"
            }
        }
    }
}

# Travis CI

Para ejecutar tus pruebas de Dusk en Travis CI, usa la siguiente configuración en el archivo .travis.yml. Ya que Travis CI no es un entorno gráfico, necesitaremos tomar algunos pasos extras con el propósito de ejecutar un navegador Chrome. En adición a esto, usaremos php artisan serve para ejecutar el servidor web integrado de PHP:

language: php

php:
    - 7.3

addons:
    chrome: stable

install:
    - cp .env.testing .env
    - travis_retry composer install --no-interaction --prefer-dist --no-suggest
    - php artisan key:generate

before_script:
    - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
    - php artisan serve &

script:
    - php artisan dusk

En tu archivo .env.testing, ajusta el valor de APP_URL:

APP_URL=http://127.0.0.1:8000

# GitHub Actions

Si estás usando acciónes de GitHub para ejecutar tus pruebas de Dusk, puedes usar este archivo de configuración como punto de partida. Igual que TravisCI, usaremos el comando php artisan serve para ejecutar el servidor integrado de PHP:

name: CI
on: [push]
jobs:

dusk-php:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v1
    - name: Prepare The Environment
      run: cp .env.example .env
    - name: Create Database
      run: mysql --user="root" --password="root" -e "CREATE DATABASE my-database character set UTF8mb4 collate utf8mb4_bin;"
    - name: Install Composer Dependencies
      run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
    - name: Generate Application Key
      run: php artisan key:generate
    - name: Upgrade Chrome Driver
      run: php artisan dusk:chrome-driver
    - name: Start Chrome Driver
      run: ./vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &
    - name: Run Laravel Server
      run: php artisan serve > /dev/null 2>&1 &
    - name: Run Dusk Tests
      run: php artisan dusk

En tu archivo .env.testing, ajusta el valor de APP_URL:

APP_URL=http://127.0.0.1:8000