Browser Testing

Introduzione

Laravel Dusk offre un’API espressiva e facile da usare per l’automazione e il testing del browser. Di default, Dusk non richiede l’installazione di JDK o Selenium sul tuo computer locale. Invece, Dusk utilizza un’installazione autonoma di ChromeDriver. Tuttavia, puoi utilizzare qualsiasi altro driver compatibile con Selenium che preferisci.

Installazione

Per iniziare, dovresti installare Google Chrome e aggiungere la dipendenza Composer laravel/dusk al tuo progetto:

composer require laravel/dusk --dev

Se stai registrando manualmente il service provider di Dusk, non dovresti mai registrarlo nell’ambiente di produzione, poiché ciò potrebbe permettere a utenti non autorizzati di autenticarsi con la tua applicazione.

Dopo aver installato il pacchetto Dusk, esegui il comando Artisan dusk:install. Il comando dusk:install creerà una cartella tests/Browser, un test di esempio Dusk e installerà il binario di Chrome Driver per il tuo sistema operativo:

php artisan dusk:install

Successivamente, imposta la variabile d’ambiente APP_URL nel file .env della tua applicazione. Questo valore dovrebbe corrispondere all’URL che usi per accedere alla tua applicazione nel browser.

Se stai usando Laravel Sail per gestire il tuo ambiente di sviluppo locale, consulta anche la documentazione di Sail su come configurare ed eseguire i test Dusk.

Gestione delle installazioni di ChromeDriver

Se desideri installare una versione diversa di ChromeDriver rispetto a quella installata da Laravel Dusk tramite il comando dusk:install, puoi usare il comando dusk:chrome-driver:

# Installa l'ultima versione di ChromeDriver per il tuo sistema operativo...
php artisan dusk:chrome-driver

# Installa una versione specifica di ChromeDriver per il tuo OS...
php artisan dusk:chrome-driver 86

# Installa una versione specifica di ChromeDriver per tutti i sistemi operativi supportati...
php artisan dusk:chrome-driver --all

# Installa la versione di ChromeDriver che corrisponde alla versione rilevata di Chrome / Chromium per il tuo sistema operativo...
php artisan dusk:chrome-driver --detect

Dusk richiede che i binari chromedriver siano eseguibili. Se hai problemi nell’eseguire Dusk, assicurati che i binari siano eseguibili usando il seguente comando: chmod -R 0755 vendor/laravel/dusk/bin/.

Utilizzo di Altri Browser

Per default, Dusk utilizza Google Chrome e un’installazione standalone di ChromeDriver per eseguire i test del browser. Tuttavia, puoi avviare il tuo server Selenium ed eseguire i test su qualsiasi browser desideri.

Per iniziare, apri il file tests/DuskTestCase.php, che è il caso di test base di Dusk per la tua applicazione. All’interno di questo file, puoi rimuovere la chiamata al metodo startChromeDriver. Questo impedirà a Dusk di avviare automaticamente ChromeDriver:

/**
 * Prepara per l'esecuzione dei test Dusk.
 *
 * @beforeClass
 */
public static function prepare(): void
{
    // static::startChromeDriver();
}

Successivamente, puoi modificare il metodo driver per connetterti all’URL e alla porta di tua scelta. Inoltre, puoi modificare le "desired capabilities" che devono essere passate a WebDriver:

use Facebook\WebDriver\Remote\RemoteWebDriver;

/**
 * Crea l'istanza di RemoteWebDriver.
 */
protected function driver(): RemoteWebDriver
{
    return RemoteWebDriver::create(
        'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
    );
}

Iniziare

Generazione dei Test

Per creare un test Dusk, utilizza il comando Artisan dusk:make. Il test generato sarà posizionato nella directory tests/Browser:

php artisan dusk:make LoginTest

Resettare il Database Dopo Ogni Test

La maggior parte dei test che scrivi interagirà con pagine che recuperano dati dal database della tua applicazione; tuttavia, i tuoi test Dusk non dovrebbero mai usare il trait RefreshDatabase. Il trait RefreshDatabase sfrutta le transazioni del database che non saranno applicabili o disponibili tra richieste HTTP. Invece, hai due opzioni: il trait DatabaseMigrations e il trait DatabaseTruncation.

Utilizzo delle migrazioni del database

Il trait DatabaseMigrations esegue le migrazioni del database prima di ogni test. Tuttavia, eliminare e ricreare le tabelle del database per ogni test è generalmente più lento rispetto a troncarle:

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

    //
}

I database SQLite in-memory non possono essere utilizzati durante l’esecuzione dei test Dusk. Poiché il browser opera in un processo separato, non sarà in grado di accedere ai database in-memory di altri processi.

Utilizzo della Troncatura del Database

Il trait DatabaseTruncation migrate il tuo database al primo test per assicurarsi che le tabelle del database siano state create correttamente. Tuttavia, nei test successivi, le tabelle del database verranno semplicemente troncate, offrendo un aumento di velocità rispetto al riavvio di tutte le migrazioni del database:

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

    //
}

Per impostazione predefinita, questo trait tronca tutte le tabelle tranne la tabella migrations. Se desideri personalizzare le tabelle da troncare, puoi definire una proprietà $tablesToTruncate nella tua classe di test:

Se stai usando Pest, dovresti definire proprietà o metodi nella classe base DuskTestCase o in qualsiasi classe che il tuo file di test estende.

    /**
     * Indica quali tabelle devono essere troncate.
     *
     * @var array
     */
    protected $tablesToTruncate = ['users'];

In alternativa, puoi definire una proprietà $exceptTables nella tua classe di test per specificare quali tabelle devono essere escluse dalla troncatura:

    /**
     * Indica quali tabelle devono essere escluse dalla troncatura.
     *
     * @var array
     */
    protected $exceptTables = ['users'];

Per specificare le connessioni al database le cui tabelle devono essere troncate, puoi definire una proprietà $connectionsToTruncate nella tua classe di test:

    /**
     * Indica quali connessioni devono avere le loro tabelle troncate.
     *
     * @var array
     */
    protected $connectionsToTruncate = ['mysql'];

Se desideri eseguire del codice prima o dopo che la troncatura del database venga eseguita, puoi definire i metodi beforeTruncatingDatabase o afterTruncatingDatabase nella tua classe di test:

    /**
     * Esegue qualsiasi operazione che dovrebbe avvenire prima che il database inizi a troncare.
     */
    protected function beforeTruncatingDatabase(): void
    {
        //
    }

    /**
     * Esegue qualsiasi operazione che dovrebbe avvenire dopo che il database ha finito di troncare.
     */
    protected function afterTruncatingDatabase(): void
    {
        //
    }

Esecuzione dei Test

Per eseguire i test del browser, esegui il comando Artisan dusk:

php artisan dusk

Se l’ultima volta che hai eseguito il comando dusk hai avuto dei test falliti, puoi risparmiare tempo rieseguendo prima i test che non sono riusciti usando il comando dusk:fails:

php artisan dusk:fails

Il comando dusk accetta qualsiasi argomento normalmente accettato dal runner di test Pest / PHPUnit, permettendoti ad esempio di eseguire solo i test di un determinato gruppo:

php artisan dusk --group=foo

Se stai usando Laravel Sail per gestire il tuo ambiente di sviluppo locale, consulta la documentazione di Sail su configurare ed eseguire i test Dusk.

Avvio manuale di ChromeDriver

Per impostazione predefinita, Dusk tenterà automaticamente di avviare ChromeDriver. Se ciò non funziona sul tuo sistema, puoi avviare manualmente ChromeDriver prima di eseguire il comando dusk. Se scegli di avviare manualmente ChromeDriver, dovresti commentare la seguente riga nel file tests/DuskTestCase.php:

/**
 * Prepara per l'esecuzione dei test Dusk.
 *
 * @beforeClass
 */
public static function prepare(): void
{
    // static::startChromeDriver();
}

Inoltre, se avvii ChromeDriver su una porta diversa da 9515, dovresti modificare il metodo driver della stessa classe per riflettere la porta corretta:

use Facebook\WebDriver\Remote\RemoteWebDriver;

/**
 * Crea l'istanza di RemoteWebDriver.
 */
protected function driver(): RemoteWebDriver
{
    return RemoteWebDriver::create(
        'http://localhost:9515', DesiredCapabilities::chrome()
    );
}

Gestione dell’Ambiente

Per fare in modo che Dusk utilizzi il proprio file di ambiente durante l’esecuzione dei test, crea un file .env.dusk.{environment} nella root del tuo progetto. Ad esempio, se avvierai il comando dusk dal tuo ambiente local, dovresti creare un file .env.dusk.local.

Durante l’esecuzione dei test, Dusk effettuerà una copia di backup del tuo file .env e rinominerà l’ambiente Dusk in .env. Una volta completati i test, il tuo file .env sarà ripristinato.

Nozioni di base del browser

Creazione dei Browser

Per iniziare, scriviamo un test che verifica se possiamo effettuare il login nella nostra applicazione. Dopo aver generato un test, possiamo modificarlo per navigare alla pagina di login, inserire alcune credenziali e cliccare il pulsante "Login". Per creare un’istanza del browser, puoi chiamare il metodo browse all’interno del tuo test 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;

    /**
     * Un esempio base di test browser.
     */
    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');
        });
    }
}

Come puoi vedere nell’esempio sopra, il metodo browse accetta una closure. Un’istanza del browser verrà passata automaticamente a questa closure da Dusk ed è l’oggetto principale usato per interagire e fare asserzioni sulla tua applicazione.

Creare Più Browser

A volte potresti aver bisogno di più browser per eseguire correttamente un test. Ad esempio, potrebbero servire più browser per testare una schermata di chat che interagisce con i websockets. Per creare più browser, aggiungi semplicemente più argomenti browser alla firma della closure passata al metodo 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');
    });

Navigazione

Il metodo visit può essere usato per navigare verso un determinato URI all’interno della tua applicazione:

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

Puoi usare il metodo visitRoute per navigare verso una route nominata:

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

Puoi navigare "indietro" e "avanti" usando i metodi back e forward:

$browser->back();

$browser->forward();

Puoi usare il metodo refresh per ricaricare la pagina:

$browser->refresh();

Ridimensionamento delle Finestre del Browser

Puoi usare il metodo resize per regolare la dimensione della finestra del browser:

$browser->resize(1920, 1080);

Il metodo maximize può essere utilizzato per massimizzare la finestra del browser:

$browser->maximize();

Il metodo fitContent ridimensiona la finestra del browser in base alle dimensioni del suo contenuto:

$browser->fitContent();

Quando un test fallisce, Dusk ridimensiona automaticamente il browser per adattarsi al contenuto prima di scattare uno screenshot. Puoi disabilitare questa funzione chiamando il metodo disableFitOnFailure all’interno del tuo test:

$browser->disableFitOnFailure();

Puoi usare il metodo move per spostare la finestra del browser in una posizione diversa sullo schermo:

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

Macro del Browser

Se desideri definire un metodo personalizzato del browser che puoi riutilizzare in diversi test, puoi usare il metodo macro sulla classe Browser. Tipicamente, dovresti chiamare questo metodo dal metodo boot di un provider di servizi:

<?php

namespace App\Providers;

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

class DuskServiceProvider extends ServiceProvider
{
    /**
     * Registra le macro del browser di Dusk.
     */
    public function boot(): void
    {
        Browser::macro('scrollToElement', function (string $element = null) {
            $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");

            return $this;
        });
    }
}

La funzione macro accetta un nome come primo argomento e una closure come secondo. La closure della macro verrà eseguita quando chiami la macro come metodo su un’istanza di Browser:

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

Autenticazione

Spesso testerai pagine che richiedono autenticazione. Puoi usare il metodo loginAs di Dusk per evitare di interagire con la schermata di login della tua applicazione in ogni test. Il metodo loginAs accetta una chiave primaria associata al tuo modello autenticabile o un’istanza del modello autenticabile:

use App\Models\User;
use Laravel\Dusk\Browser;

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

Dopo aver usato il metodo loginAs, la sessione dell’utente sarà mantenuta per tutti i test nel file.

Cookies

Puoi usare il metodo cookie per ottenere o impostare il valore di un cookie crittografato. Per impostazione predefinita, tutti i cookie creati da Laravel sono crittografati:

    $browser->cookie('name');

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

Puoi usare il metodo plainCookie per ottenere o impostare il valore di un cookie non crittografato:

    $browser->plainCookie('name');

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

Puoi usare il metodo `deleteCookie` per eliminare il cookie specificato:

    $browser->deleteCookie('name');

Eseguire JavaScript

Puoi utilizzare il metodo script per eseguire istruzioni JavaScript arbitrarie nel browser:

$browser->script('document.documentElement.scrollTop = 0');

$browser->script([
    'document.body.scrollTop = 0',
    'document.documentElement.scrollTop = 0',
]);

$output = $browser->script('return window.location.pathname');

Fare uno Screenshot

Puoi utilizzare il metodo screenshot per scattare uno screenshot e salvarlo con il nome file specificato. Tutti gli screenshot saranno salvati nella directory tests/Browser/screenshots:

$browser->screenshot('filename');

Il metodo responsiveScreenshots può essere usato per scattare una serie di screenshot a vari breakpoint:

$browser->responsiveScreenshots('filename');

Il metodo screenshotElement può essere utilizzato per scattare uno screenshot di un elemento specifico sulla pagina:

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

Salvataggio dell’output della Console su Disco

Puoi usare il metodo storeConsoleLog per scrivere l’output della console del browser corrente su disco con il nome di file specificato. L’output della console sarà salvato nella directory tests/Browser/console:

    $browser->storeConsoleLog('filename');

Salvare il Sorgente della Pagina su Disco

Puoi usare il metodo storeSource per scrivere il sorgente della pagina corrente su disco con il nome del file specificato. Il sorgente sarà salvato nella directory tests/Browser/source:

$browser->storeSource('filename');

Interagire con gli elementi

Dusk Selectors

Scegliere selettori CSS efficaci per interagire con gli elementi è una delle parti più difficili della scrittura dei test Dusk. Nel tempo, i cambiamenti nel frontend possono far sì che selettori CSS come il seguente rompano i tuoi test:

// HTML...

<button>Login</button>

// Test...

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

I selettori Dusk ti permettono di concentrarti sulla scrittura di test efficaci invece di ricordare i selettori CSS. Per definire un selettore, aggiungi un attributo dusk al tuo elemento HTML. Poi, quando interagisci con un browser Dusk, prefissa il selettore con @ per manipolare l’elemento associato nel tuo test:

// HTML...

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

// Test...

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

Se desideri, puoi personalizzare l’attributo HTML che il selettore Dusk utilizza tramite il metodo selectorHtmlAttribute. Tipicamente, questo metodo dovrebbe essere chiamato dal metodo boot del AppServiceProvider della tua applicazione:

use Laravel\Dusk\Dusk;

Dusk::selectorHtmlAttribute('data-dusk');

Testo, Valori e Attributi

Recuperare e Impostare Valori

Dusk offre vari metodi per interagire con il valore attuale, il testo visualizzato e gli attributi degli elementi sulla pagina. Ad esempio, per ottenere il "value" di un elemento che corrisponde a un selettore CSS o Dusk, usa il metodo value:

// Recupera il valore...
$value = $browser->value('selector');

// Imposta il valore...
$browser->value('selector', 'value');

Puoi usare il metodo inputValue per ottenere il "value" di un elemento di input con un nome di campo specifico:

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

Recuperare Testo

Il metodo text può essere utilizzato per ottenere il testo visualizzato di un elemento che corrisponde al selettore specificato:

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

Recupero degli Attributi

Infine, il metodo attribute può essere utilizzato per ottenere il valore di un attributo di un elemento che corrisponde al selettore fornito:

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

Interagire con i Forms

Inserimento di Valori

Dusk offre diversi metodi per interagire con i moduli e gli elementi di input. Prima di tutto, vediamo un esempio di inserimento di testo in un campo di input:

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

Nota che, anche se il metodo accetta un selettore CSS se necessario, non è obbligatorio passarlo al metodo type. Se non viene fornito un selettore CSS, Dusk cercherà un campo input o textarea con l’attributo name specificato.

Per aggiungere testo a un campo senza cancellarne il contenuto, puoi usare il metodo append:

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

Puoi cancellare il valore di un input usando il metodo clear:

$browser->clear('email');

Puoi far digitare lentamente a Dusk utilizzando il metodo typeSlowly. Di default, Dusk farà una pausa di 100 millisecondi tra ogni pressione di tasto. Per personalizzare il tempo tra le pressioni di tasto, puoi passare il numero di millisecondi desiderato come terzo argomento del metodo:

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

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

Puoi utilizzare il metodo appendSlowly per aggiungere testo lentamente:

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

Menu a discesa

Per selezionare un valore disponibile in un elemento select, puoi utilizzare il metodo select. Come il metodo type, il metodo select non richiede un selettore CSS completo. Quando passi un valore al metodo select, dovresti passare il valore dell’opzione sottostante invece del testo visualizzato:

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

Puoi selezionare un’opzione casuale omettendo il secondo argomento:

$browser->select('size');

Fornendo un array come secondo argomento al metodo select, puoi istruire il metodo a selezionare più opzioni:

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

Checkbox

Per selezionare una checkbox, puoi usare il metodo check. Come molti altri metodi per gli input, non è necessario un selettore CSS completo. Se non viene trovato un selettore CSS corrispondente, Dusk cercherà una checkbox con l’attributo name uguale:

$browser->check('terms');

Per deselezionare una checkbox, usa il metodo uncheck:

$browser->uncheck('terms');

Radio Buttons

Per "selezionare" un’opzione di input radio, puoi usare il metodo radio. Come molti altri metodi legati agli input, non è necessario un selettore CSS completo. Se non viene trovata una corrispondenza con un selettore CSS, Dusk cercherà un input radio con attributi name e value corrispondenti:

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

Allegare File

Il metodo attach serve ad allegare un file a un elemento di input file. Come molti altri metodi per gli input, non è necessario un selettore CSS completo. Se non si trova una corrispondenza con il selettore CSS, Dusk cercherà un input file con l’attributo name corrispondente:

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

La funzione attach richiede che l’estensione PHP Zip sia installata e abilitata sul server.

Premere Pulsanti

Il metodo press può essere utilizzato per cliccare un elemento pulsante sulla pagina. L’argomento passato al metodo press può essere sia il testo visibile del pulsante sia un selettore CSS / Dusk:

    $browser->press('Login');

Quando si inviano i form, molte applicazioni disabilitano il pulsante di invio dopo che è stato premuto e poi lo riabilitano una volta completata la richiesta HTTP della sottomissione del form. Per premere un pulsante e aspettare che venga riabilitato, puoi utilizzare il metodo pressAndWaitFor:

    // Premi il pulsante e aspetta al massimo 5 secondi che sia abilitato...
    $browser->pressAndWaitFor('Save');

    // Premi il pulsante e aspetta al massimo 1 secondo che sia abilitato...
    $browser->pressAndWaitFor('Save', 1);

Clic sui link

Per cliccare un link, puoi usare il metodo clickLink sull’istanza del browser. Il metodo clickLink cliccherà il link che ha il testo visualizzato specificato:

$browser->clickLink($linkText);

Puoi usare il metodo seeLink per verificare se un link con il testo visualizzato specificato è visibile sulla pagina:

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

Questi metodi interagiscono con jQuery. Se jQuery non è disponibile sulla pagina, Dusk lo inietterà automaticamente nella pagina in modo che sia disponibile per la durata del test.

Utilizzo della Tastiera

Il metodo keys permette di fornire sequenze di input più complesse a un elemento rispetto a quanto normalmente consentito dal metodo type. Ad esempio, puoi istruirlo a tenere premuti i tasti modificatori mentre inserisci dei valori. In questo esempio, il tasto shift sarà premuto mentre viene inserito taylor nell’elemento corrispondente al selettore specificato. Dopo aver digitato taylor, verrà digitato swift senza nessun tasto modificatore:

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

Un altro utilizzo utile del metodo keys è inviare una combinazione di "scorciatoia da tastiera" al selettore CSS principale della tua applicazione:

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

Tutti i tasti modificatori come {command} sono racchiusi tra parentesi graffe {}, e corrispondono alle costanti definite nella classe Facebook\WebDriver\WebDriverKeys, che possono essere trovate su GitHub.

Interazioni fluide con la tastiera

Dusk offre anche un metodo withKeyboard, che permette di eseguire interazioni complesse con la tastiera in modo fluido tramite la classe Laravel\Dusk\Keyboard. La classe Keyboard fornisce i metodi press, release, type e pause:

use Laravel\Dusk\Keyboard;

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

Macro da Tastiera

Se desideri definire interazioni da tastiera personalizzate che puoi facilmente riutilizzare in tutta la tua suite di test, puoi utilizzare il metodo macro fornito dalla classe Keyboard. Di solito, dovresti chiamare questo metodo dal metodo boot di un provider di servizi boot:

<?php

namespace App\Providers;

use Facebook\WebDriver\WebDriverKeys;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Keyboard;
use Laravel\Dusk\OperatingSystem;

class DuskServiceProvider extends ServiceProvider
{
    /**
     * Registra le macro del browser di Dusk.
     */
    public function boot(): void
    {
        Keyboard::macro('copy', function (string $element = null) {
            $this->type([
                OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c',
            ]);

            return $this;
        });

        Keyboard::macro('paste', function (string $element = null) {
            $this->type([
                OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v',
            ]);

            return $this;
        });
    }
}

La funzione macro accetta un nome come primo argomento e una closure come secondo argomento. La closure della macro verrà eseguita quando si chiama la macro come metodo su un’istanza di Keyboard:

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

Usare il Mouse

Cliccare sugli Elementi

Il metodo click può essere usato per cliccare su un elemento che corrisponde al selettore CSS o Dusk fornito:

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

Il metodo clickAtXPath può essere usato per cliccare su un elemento che corrisponde all’espressione XPath fornita:

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

Il metodo clickAtPoint può essere usato per cliccare sull’elemento più in alto in un dato paio di coordinate relative all’area visibile del browser:

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

Il metodo doubleClick può essere usato per simulare un doppio clic del mouse:

$browser->doubleClick();

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

Il metodo rightClick può essere usato per simulare un clic destro del mouse:

$browser->rightClick();

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

Il metodo clickAndHold può essere usato per simulare la pressione e il mantenimento di un pulsante del mouse. Una chiamata successiva al metodo releaseMouse annullerà questo comportamento e rilascerà il pulsante del mouse:

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

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

Il metodo controlClick può essere usato per simulare l’evento ctrl+click all’interno del browser:

$browser->controlClick();

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

Mouseover

Il metodo mouseover può essere usato quando devi spostare il mouse su un elemento che corrisponde al selettore CSS o Dusk fornito:

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

Trascinamento e Rilascio

Il metodo drag può essere usato per trascinare un elemento che corrisponde al selettore dato verso un altro elemento:

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

Oppure, puoi trascinare un elemento in una sola direzione:

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

Infine, puoi trascinare un elemento di uno spostamento specifico:

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

Dialoghi JavaScript

Dusk offre diversi metodi per interagire con i dialoghi JavaScript. Ad esempio, puoi usare il metodo waitForDialog per aspettare che appaia un dialogo JavaScript. Questo metodo accetta un argomento opzionale che specifica quanti secondi aspettare prima che il dialogo appaia:

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

Il metodo assertDialogOpened può essere utilizzato per verificare che un dialogo sia stato mostrato e contenga il messaggio specificato:

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

Se il dialogo JavaScript include un prompt, puoi usare il metodo typeInDialog per inserire un valore nel prompt:

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

Per chiudere un dialogo JavaScript aperto cliccando il pulsante "OK", puoi utilizzare il metodo acceptDialog:

$browser->acceptDialog();

Per chiudere un dialogo JavaScript aperto cliccando il pulsante "Cancel", puoi utilizzare il metodo dismissDialog:

$browser->dismissDialog();

Interagire con gli iframe

Se hai bisogno di interagire con elementi all’interno di un iframe, puoi usare il metodo withinFrame. Tutte le interazioni con gli elementi che avvengono all’interno della closure fornita al metodo withinFrame saranno limitate al contesto dell’iframe specificato:

$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');
});

Ambito dei Selettori

A volte potresti voler eseguire diverse operazioni limitandole tutte a un determinato selettore. Per esempio, potresti voler verificare che un certo testo esista solo all’interno di una tabella e poi cliccare un pulsante all’interno di quella tabella. Puoi usare il metodo with per fare questo. Tutte le operazioni eseguite all’interno della closure passata al metodo with saranno limitate al selettore originale:

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

Occasionalmente potresti aver bisogno di eseguire verifiche al di fuori dell’ambito attuale. Puoi usare i metodi elsewhere ed elsewhereWhenAvailable per fare questo:

$browser->with('.table', function (Browser $table) {
    // L'ambito attuale è `body .table`...

    $browser->elsewhere('.page-title', function (Browser $title) {
        // L'ambito attuale è `body .page-title`...
        $title->assertSee('Hello World');
    });

    $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) {
        // L'ambito attuale è `body .page-title`...
        $title->assertSee('Hello World');
    });
});

Attendere gli Elementi

Quando si testano applicazioni che utilizzano ampiamente JavaScript, spesso è necessario "attendere" che certi elementi o dati siano disponibili prima di procedere con un test. Dusk rende questo semplice. Utilizzando vari metodi, puoi aspettare che gli elementi diventino visibili sulla pagina o anche aspettare che un’espressione JavaScript restituisca true.

Attesa

Se devi semplicemente mettere in pausa il test per un determinato numero di millisecondi, usa il metodo pause:

$browser->pause(1000);

Se hai bisogno di mettere in pausa il test solo se una certa condizione è true, usa il metodo pauseIf:

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

Allo stesso modo, se devi mettere in pausa il test a meno che una condizione non sia true, puoi usare il metodo pauseUnless:

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

Attendere i selettori

Il metodo waitFor può essere utilizzato per mettere in pausa l’esecuzione del test fino a quando l’elemento che corrisponde al selettore CSS o Dusk fornito non viene visualizzato nella pagina. Per impostazione predefinita, questa pausa durerà al massimo cinque secondi prima di generare un’eccezione. Se necessario, puoi passare un limite di timeout personalizzato come secondo argomento del metodo:

// Attendere al massimo cinque secondi per il selettore...
$browser->waitFor('.selector');

// Attendere al massimo un secondo per il selettore...
$browser->waitFor('.selector', 1);

Puoi anche attendere fino a quando l’elemento che corrisponde al selettore specificato contiene il testo dato:

// Attendere al massimo cinque secondi affinché il selettore contenga il testo specificato...
$browser->waitForTextIn('.selector', 'Hello World');

// Attendere al massimo un secondo affinché il selettore contenga il testo specificato...
$browser->waitForTextIn('.selector', 'Hello World', 1);

Puoi inoltre attendere fino a quando l’elemento che corrisponde al selettore specificato non è più presente nella pagina:

// Attendere al massimo cinque secondi fino a quando il selettore non è presente...
$browser->waitUntilMissing('.selector');

// Attendere al massimo un secondo fino a quando il selettore non è presente...
$browser->waitUntilMissing('.selector', 1);

Oppure, puoi attendere fino a quando l’elemento che corrisponde al selettore specificato è abilitato o disabilitato:

// Attendere al massimo cinque secondi fino a quando il selettore è abilitato...
$browser->waitUntilEnabled('.selector');

// Attendere al massimo un secondo fino a quando il selettore è abilitato...
$browser->waitUntilEnabled('.selector', 1);

// Attendere al massimo cinque secondi fino a quando il selettore è disabilitato...
$browser->waitUntilDisabled('.selector');

// Attendere al massimo un secondo fino a quando il selettore è disabilitato...
$browser->waitUntilDisabled('.selector', 1);

Limitare i Selettori Quando Disponibili

A volte, potresti voler aspettare che un elemento appaia che corrisponde a un determinato selettore e poi interagire con esso. Ad esempio, potresti voler attendere che una finestra modale sia disponibile e poi premere il pulsante "OK" all’interno della modale. Il metodo whenAvailable può essere utilizzato per questo scopo. Tutte le operazioni sugli elementi eseguite all’interno della closure saranno limitate al selettore originale:

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

Attendere il Testo

Il metodo waitForText può essere utilizzato per attendere che il testo specificato venga visualizzato sulla pagina:

// Attendi al massimo cinque secondi per il testo...
$browser->waitForText('Hello World');

// Attendi al massimo un secondo per il testo...
$browser->waitForText('Hello World', 1);

Puoi utilizzare il metodo waitUntilMissingText per attendere che il testo visualizzato venga rimosso dalla pagina:

// Attendi al massimo cinque secondi per la rimozione del testo...
$browser->waitUntilMissingText('Hello World');

// Attendi al massimo un secondo per la rimozione del testo...
$browser->waitUntilMissingText('Hello World', 1);

Attendere i Link

Il metodo waitForLink può essere usato per attendere che il testo del link specificato sia visualizzato sulla pagina:

// Attendere al massimo cinque secondi per il link...
$browser->waitForLink('Create');

// Attendere al massimo un secondo per il link...
$browser->waitForLink('Create', 1);

Attesa degli input

Il metodo waitForInput può essere utilizzato per attendere che il campo di input specificato sia visibile nella pagina:

// Attendi al massimo cinque secondi per l'input...
$browser->waitForInput($field);

// Attendi al massimo un secondo per l'input...
$browser->waitForInput($field, 1);

Attendere la Posizione della Pagina

Quando si effettua un’asserzione sul percorso come $browser->assertPathIs('/home'), l’asserzione può fallire se window.location.pathname viene aggiornato in modo asincrono. Puoi usare il metodo waitForLocation per attendere che la posizione sia un valore specifico:

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

Il metodo waitForLocation può essere usato anche per attendere che la posizione attuale della finestra sia un URL completo:

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

Puoi anche attendere la posizione di una route nominata:

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

Attendere il Ricaricamento della Pagina

Se devi attendere che una pagina si ricarichi dopo aver eseguito un’azione, usa il metodo waitForReload:

use Laravel\Dusk\Browser;

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

Poiché spesso è necessario attendere il ricaricamento della pagina dopo aver cliccato un pulsante, puoi usare il metodo clickAndWaitForReload per comodità:

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

Attendere espressioni JavaScript

A volte potresti voler mettere in pausa l’esecuzione di un test finché una determinata espressione JavaScript non restituisce true. Puoi facilmente farlo usando il metodo waitUntil. Quando passi un’espressione a questo metodo, non è necessario includere la parola chiave return o il punto e virgola finale:

// Attendi al massimo cinque secondi affinché l'espressione sia vera...
$browser->waitUntil('App.data.servers.length > 0');

// Attendi al massimo un secondo affinché l'espressione sia vera...
$browser->waitUntil('App.data.servers.length > 0', 1);

In attesa delle espressioni Vue

I metodi waitUntilVue e waitUntilVueIsNot possono essere usati per attendere che un attributo di un componente Vue abbia un determinato valore:

// Attendi finché l'attributo del componente contiene il valore specificato...
$browser->waitUntilVue('user.name', 'Taylor', '@user');

// Attendi finché l'attributo del componente non contiene il valore specificato...
$browser->waitUntilVueIsNot('user.name', null, '@user');

Attendere gli Eventi JavaScript

Il metodo waitForEvent può essere utilizzato per mettere in pausa l’esecuzione di un test fino a quando si verifica un evento JavaScript:

$browser->waitForEvent('load');

Il listener dell’evento viene collegato all’ambito corrente, che per impostazione predefinita è l’elemento body. Quando si utilizza un selettore con ambito, il listener dell’evento sarà collegato all’elemento corrispondente:

$browser->with('iframe', function (Browser $iframe) {
    // Attendere l'evento load dell'iframe...
    $iframe->waitForEvent('load');
});

Puoi anche fornire un selettore come secondo argomento del metodo waitForEvent per collegare il listener dell’evento a un elemento specifico:

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

Puoi anche attendere eventi sugli oggetti document e window:

// Attendere che il documento venga scrollato...
$browser->waitForEvent('scroll', 'document');

// Attendere al massimo cinque secondi fino a quando la finestra viene ridimensionata...
$browser->waitForEvent('resize', 'window', 5);

Attendere con una Callback

Molti dei metodi "wait" in Dusk si basano sul metodo waitUsing. Puoi usare direttamente questo metodo per attendere che una determinata closure ritorni true. Il metodo waitUsing accetta il numero massimo di secondi da attendere, l’intervallo con cui valutare la closure, la closure stessa e un messaggio di errore opzionale:

$browser->waitUsing(10, 1, function () use ($something) {
    return $something->isReady();
}, "Qualcosa non era pronto in tempo.");

Scorrere un elemento nella visualizzazione

A volte potresti non riuscire a cliccare su un elemento perché si trova al di fuori dell’area visibile del browser. Il metodo scrollIntoView scorrerà la finestra del browser finché l’elemento con il selettore specificato non sarà visibile:

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

Asserzioni Disponibili

Dusk offre una varietà di asserzioni che puoi utilizzare nella tua applicazione. Tutte le asserzioni disponibili sono documentate nell’elenco sottostante:

assertTitle

Verifica che il titolo della pagina corrisponda al testo fornito:

$browser->assertTitle($title);

assertTitleContains

Verifica che il titolo della pagina contenga il testo specificato:

$browser->assertTitleContains($title);

assertUrlIs

Verifica che l’URL corrente (senza la stringa di query) corrisponda alla stringa fornita:

$browser->assertUrlIs($url);

assertSchemeIs

Verifica che lo schema dell’URL attuale corrisponda allo schema specificato:

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

Verifica che lo schema dell’URL attuale non corrisponda a quello fornito:

$browser->assertSchemeIsNot($scheme);

assertHostIs

Verifica che l’host dell’URL corrente corrisponda a quello fornito:

$browser->assertHostIs($host);

assertHostIsNot

Verifica che l’host dell’URL corrente non corrisponda all’host fornito:

$browser->assertHostIsNot($host);

assertPortIs

Verifica che la porta dell’URL corrente corrisponda alla porta specificata:

$browser->assertPortIs($port);

assertPortIsNot

Verifica che la porta dell’URL corrente non corrisponda alla porta specificata:

$browser->assertPortIsNot($port);

assertPathBeginsWith

Verifica che il percorso dell’URL corrente inizi con il percorso fornito:

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

assertPathEndsWith

Verifica che il percorso URL attuale finisca con il percorso specificato:

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

assertPathContains

Verifica che il percorso dell’URL attuale contenga il percorso specificato:

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

assertPathIs

Verifica che il percorso corrente corrisponda al percorso specificato:

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

assertPathIsNot

Verifica che il percorso corrente non corrisponda al percorso fornito:

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

assertRouteIs

Verifica che l’URL attuale corrisponda all’URL della route nominata:

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

assertQueryStringHas

Verifica che il parametro della query string specificato sia presente:

$browser->assertQueryStringHas($name);

Verifica che il parametro della query string specificato sia presente e abbia un valore dato:

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

assertQueryStringMissing

Verifica che il parametro della query string specificato sia assente:

$browser->assertQueryStringMissing($name);

assertFragmentIs

Verifica che l’attuale frammento hash dell’URL corrisponda al frammento fornito:

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

Verifica che l’attuale frammento hash dell’URL inizi con il frammento fornito:

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

Verifica che l’hash corrente dell’URL non corrisponda al frammento specificato:

$browser->assertFragmentIsNot('anchor');

assertHasCookie

Verifica che il cookie criptato specificato sia presente:

$browser->assertHasCookie($name);

assertHasPlainCookie

Verifica che il cookie non criptato fornito sia presente:

$browser->assertHasPlainCookie($name);

assertCookieMissing

Verifica che il cookie crittografato specificato non sia presente:

$browser->assertCookieMissing($name);

assertPlainCookieMissing

Verifica che il cookie non criptato specificato non sia presente:

$browser->assertPlainCookieMissing($name);

assertCookieValue

Verifica che un cookie crittografato abbia un determinato valore:

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

assertPlainCookieValue

Verifica che un cookie non crittografato abbia un determinato valore:

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

assertSee

Verifica che il testo specificato sia presente nella pagina:

$browser->assertSee($text);

assertDontSee

Verifica che il testo fornito non sia presente sulla pagina:

$browser->assertDontSee($text);

assertSeeIn

Verifica che il testo fornito sia presente all’interno del selettore:

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

assertDontSeeIn

Assicura che il testo fornito non sia presente all’interno del selettore:

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

#### assertSeeAnythingIn

Verifica che qualsiasi testo sia presente all'interno del selettore:

    $browser->assertSeeAnythingIn($selector);

assertSeeNothingIn

Verifica che nessun testo sia presente all’interno del selettore:

$browser->assertSeeNothingIn($selector);

assertScript

Verifica che un’espressione JavaScript restituisca il valore atteso:

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

assertSourceHas

Verifica che il codice sorgente fornito sia presente nella pagina:

$browser->assertSourceHas($code);

assertSourceMissing

Verifica che il codice sorgente specificato non sia presente sulla pagina:

$browser->assertSourceMissing($code);

assertSeeLink

Verifica che il link specificato sia presente nella pagina:

$browser->assertSeeLink($linkText);

assertDontSeeLink

Verifica che il link specificato non sia presente nella pagina:

    $browser->assertDontSeeLink($linkText);

assertInputValue

Verifica che il campo di input specificato abbia il valore indicato:

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

assertInputValueIsNot

Verifica che il campo input specificato non abbia il valore indicato:

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

assertChecked

Verifica che la checkbox specificata sia selezionata:

$browser->assertChecked($field);

assertNotChecked

Assicura che il checkbox specificato non sia selezionato:

$browser->assertNotChecked($field);

assertIndeterminate

Verifica che la checkbox specificata sia in uno stato indeterminato:

$browser->assertIndeterminate($field);

assertRadioSelected

Verifica che il campo radio specificato sia selezionato:

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

assertRadioNotSelected

Verifica che il campo radio specificato non sia selezionato:

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

assertSelected

Verifica che il menu a tendina specificato abbia il valore dato selezionato:

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

assertNotSelected

Verifica che il dropdown specificato non abbia selezionato il valore dato:

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

assertSelectHasOptions

Verifica che l’array di valori fornito sia disponibile per la selezione:

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

assertSelectMissingOptions

Verifica che l’array di valori fornito non sia disponibile per la selezione:

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

assertSelectHasOption

Verifica che il valore specificato sia disponibile per la selezione nel campo indicato:

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

assertSelectMissingOption

Verifica che il valore specificato non sia disponibile per la selezione:

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

assertValue

Verifica che l’elemento che corrisponde al selettore dato abbia il valore specificato:

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

assertValueIsNot

Verifica che l’elemento corrispondente al selettore fornito non abbia il valore specificato:

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

assertAttribute

Verifica che l’elemento che corrisponde al selettore fornito abbia il valore specificato nell’attributo indicato:

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

assertAttributeContains

Verifica che l’elemento che corrisponde al selettore dato contenga il valore specificato nell’attributo fornito:

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

assertAttributeDoesntContain

Verifica che l’elemento corrispondente al selettore indicato non contenga il valore specificato nell’attributo fornito:

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

assertAriaAttribute

Verifica che l’elemento che corrisponde al selettore fornito abbia il valore specificato nell’attributo aria indicato:

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

Ad esempio, dato il markup <button aria-label="Add"></button>, puoi verificare l’attributo aria-label in questo modo:

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

assertDataAttribute

Verifica che l’elemento che corrisponde al selettore dato abbia il valore specificato nell’attributo data fornito:

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

Per esempio, dato il markup <tr id="row-1" data-content="attendees"></tr>, puoi verificare l’attributo data-label così:

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

assertVisible

Verifica che l’elemento che corrisponde al selettore dato sia visibile:

$browser->assertVisible($selector);

assertPresent

Verifica che l’elemento corrispondente al selettore specificato sia presente nel sorgente:

$browser->assertPresent($selector);

assertNotPresent

Verifica che l’elemento che corrisponde al selettore dato non sia presente nel sorgente:

$browser->assertNotPresent($selector);

assertMissing

Verifica che l’elemento che corrisponde al selettore dato non sia visibile:

$browser->assertMissing($selector);

assertInputPresent

Verifica che un input con il nome specificato sia presente:

    $browser->assertInputPresent($name);

assertInputMissing

Verifica che un input con il nome specificato non sia presente nella sorgente:

$browser->assertInputMissing($name);

assertDialogOpened

Controlla che sia stato aperto un dialogo JavaScript con il messaggio fornito:

$browser->assertDialogOpened($message);

assertEnabled

Verifica che il campo specificato sia abilitato:

$browser->assertEnabled($field);

assertDisabled

Verifica che il campo specificato sia disabilitato:

$browser->assertDisabled($field);

assertButtonEnabled

Verifica che il pulsante specificato sia abilitato:

$browser->assertButtonEnabled($button);

assertButtonDisabled

Verifica che il pulsante specificato sia disabilitato:

$browser->assertButtonDisabled($button);

assertFocused

Verifica che il campo specificato sia attivo:

$browser->assertFocused($field);

assertNotFocused

Verifica che il campo specificato non sia focalizzato:

$browser->assertNotFocused($field);

assertAuthenticated

Verifica che l’utente sia autenticato:

$browser->assertAuthenticated();

assertGuest

Verifica che l’utente non sia autenticato:

$browser->assertGuest();

assertAuthenticatedAs

Verifica che l’utente sia autenticato come l’utente specificato:

$browser->assertAuthenticatedAs($user);

assertVue

Dusk permette anche di fare asserzioni sullo stato dei dati di un componente Vue. Ad esempio, immagina che la tua applicazione contenga il seguente componente Vue:

// HTML...

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

// Definizione del Componente...

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

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

Puoi verificare lo stato del componente Vue in questo modo:

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

assertVueIsNot

Verifica che una determinata proprietà dei dati di un componente Vue non corrisponda al valore specificato:

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

assertVueContains

Verifica che una determinata proprietà dati di un componente Vue sia un array e contenga il valore specificato:

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

assertVueDoesntContain

Verifica che una determinata proprietà dati di un componente Vue sia un array e non contenga il valore specificato:

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

Pagine

A volte, i test richiedono di eseguire diverse azioni complesse in sequenza. Questo può rendere i test più difficili da leggere e comprendere. Le Pagine Dusk ti permettono di definire azioni esplicative che possono essere eseguite su una determinata pagina tramite un unico metodo. Le pagine consentono anche di definire scorciatoie per selettori comuni della tua applicazione o di una singola pagina.

Generazione delle Pagine

Per creare un oggetto pagina, esegui il comando Artisan dusk:page. Tutti gli oggetti pagina saranno posizionati nella directory tests/Browser/Pages della tua applicazione:

php artisan dusk:page Login

Configurazione delle Pagine

Per impostazione predefinita, le pagine hanno tre metodi: url, assert ed elements. Ora parleremo dei metodi url e assert. Il metodo elements sarà discusso in maggior dettaglio più avanti.

Il metodo url

Il metodo url dovrebbe restituire il percorso dell’URL che rappresenta la pagina. Dusk utilizzerà questo URL quando naviga alla pagina nel browser:

/**
 * Ottieni l'URL per la pagina.
 */
public function url(): string
{
    return '/login';
}

Il metodo assert

Il metodo assert può effettuare qualsiasi asserzione necessaria per verificare che il browser sia effettivamente sulla pagina data. Non è necessario inserire nulla all’interno di questo metodo; tuttavia, puoi effettuare queste asserzioni se lo desideri. Queste asserzioni verranno eseguite automaticamente durante la navigazione alla pagina:

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

Navigare tra le Pagine

Una volta che una pagina è stata definita, puoi navigarci usando il metodo visit:

use Tests\Browser\Pages\Login;

$browser->visit(new Login);

A volte potresti già trovarti in una pagina specifica e avere bisogno di "caricare" i selettori e i metodi della pagina nel contesto del test corrente. Questo è comune quando premi un pulsante e vieni reindirizzato a una pagina senza navigarci esplicitamente. In questa situazione, puoi usare il metodo on per caricare la pagina:

use Tests\Browser\Pages\CreatePlaylist;

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

Selettori Abbreviati

Il metodo elements all’interno delle classi delle pagine ti permette di definire scorciatoie veloci e facili da ricordare per qualsiasi selettore CSS sulla tua pagina. Per esempio, definiamo una scorciatoia per il campo di input "email" della pagina di login dell’applicazione:

/**
 * Ottieni le scorciatoie degli elementi per la pagina.
 *
 * @return array<string, string>
 */
public function elements(): array
{
    return [
        '@email' => 'input[name=email]',
    ];
}

Una volta definita la scorciatoia, puoi usare il selettore abbreviato ovunque normalmente utilizzeresti un selettore CSS completo:

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

Selettori abbreviati globali

Dopo aver installato Dusk, una classe base Page sarà posizionata nella directory tests/Browser/Pages. Questa classe contiene un metodo siteElements che può essere utilizzato per definire selettori abbreviati globali che dovrebbero essere disponibili su ogni pagina della tua applicazione:

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

Metodi di Pagina

Oltre ai metodi predefiniti definiti nelle pagine, puoi definire metodi aggiuntivi che possono essere utilizzati in tutti i tuoi test. Ad esempio, immaginiamo di costruire un’applicazione per la gestione della musica. Un’azione comune per una pagina dell’applicazione potrebbe essere quella di creare una playlist. Invece di riscrivere la logica per creare una playlist in ogni test, puoi definire un metodo createPlaylist in una classe di pagina:

    <?php

    namespace Tests\Browser\Pages;

    use Laravel\Dusk\Browser;
    use Laravel\Dusk\Page;

    class Dashboard extends Page
    {
        // Altri metodi della pagina...

        /**
         * Crea una nuova playlist.
         */
        public function createPlaylist(Browser $browser, string $name): void
        {
            $browser->type('name', $name)
                    ->check('share')
                    ->press('Create Playlist');
        }
    }

Una volta che il metodo è stato definito, puoi usarlo in qualsiasi test che utilizzi la pagina. L’istanza del browser verrà automaticamente passata come primo argomento ai metodi personalizzati della pagina:

    use Tests\Browser\Pages\Dashboard;

    $browser->visit(new Dashboard)
            ->createPlaylist('My Playlist')
            ->assertSee('My Playlist');

Componenti

I componenti sono simili ai “page objects” di Dusk, ma sono destinati a parti dell’interfaccia utente e funzionalità che vengono riutilizzate in tutta l’applicazione, come una barra di navigazione o una finestra di notifica. Per questo motivo, i componenti non sono legati a URL specifici.

Generazione dei Componenti

Per generare un componente, esegui il comando Artisan dusk:component. I nuovi componenti vengono posizionati nella directory tests/Browser/Components:

    php artisan dusk:component DatePicker

Come mostrato sopra, un "date picker" è un esempio di componente che potrebbe essere presente in diverse pagine della tua applicazione. Scrivere manualmente la logica di automazione del browser per selezionare una data in decine di test può diventare complicato. Invece, possiamo definire un componente Dusk per rappresentare il date picker, permettendoci di incapsulare quella logica all’interno del componente:

    <?php

    namespace Tests\Browser\Components;

    use Laravel\Dusk\Browser;
    use Laravel\Dusk\Component as BaseComponent;

    class DatePicker extends BaseComponent
    {
        /**
         * Ottieni il selettore principale per il componente.
         */
        public function selector(): string
        {
            return '.date-picker';
        }

        /**
         * Verifica che la pagina del browser contenga il componente.
         */
        public function assert(Browser $browser): void
        {
            $browser->assertVisible($this->selector());
        }

        /**
         * Ottieni le scorciatoie degli elementi per il componente.
         *
         * @return array<string, string>
         */
        public function elements(): array
        {
            return [
                '@date-field' => 'input.datepicker-input',
                '@year-list' => 'div > div.datepicker-years',
                '@month-list' => 'div > div.datepicker-months',
                '@day-list' => 'div > div.datepicker-days',
            ];
        }

        /**
         * Seleziona la data specificata.
         */
        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);
                    });
        }
    }

Utilizzo dei Componenti

Una volta che il componente è stato definito, possiamo facilmente selezionare una data all’interno del date picker da qualsiasi test. E, se la logica necessaria per selezionare una data cambia, dobbiamo solo aggiornare il 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');
        });
    }
}

Integrazione Continua

La maggior parte delle configurazioni di integrazione continua di Dusk si aspetta che la tua applicazione Laravel venga servita utilizzando il server di sviluppo PHP integrato sulla porta 8000. Pertanto, prima di procedere, assicurati che il tuo ambiente di integrazione continua abbia una variabile d’ambiente APP_URL con valore http://127.0.0.1:8000.

Heroku CI

Per eseguire i test Dusk su Heroku CI, aggiungi il seguente buildpack di Google Chrome e gli script al file app.json della tua app 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

Per eseguire i tuoi test Dusk su Travis CI, utilizza la seguente configurazione .travis.yml. Poiché Travis CI non è un ambiente grafico, sarà necessario eseguire alcuni passaggi aggiuntivi per avviare un browser Chrome. Inoltre, useremo php artisan serve per avviare il server web integrato di 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

Se stai usando GitHub Actions per eseguire i tuoi test Dusk, puoi utilizzare il seguente file di configurazione come punto di partenza. Come con TravisCI, useremo il comando php artisan serve per avviare il server web integrato di 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: Prepara l'Ambiente
        run: cp .env.example .env
      - name: Crea il Database
        run: |
          sudo systemctl start mysql
          mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;"
      - name: Installa le Dipendenze di Composer
        run: composer install --no-progress --prefer-dist --optimize-autoloader
      - name: Genera la Chiave dell'Applicazione
        run: php artisan key:generate
      - name: Aggiorna il Chrome Driver
        run: php artisan dusk:chrome-driver --detect
      - name: Avvia il Chrome Driver
        run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 &
      - name: Avvia il Server Laravel
        run: php artisan serve --no-reload &
      - name: Esegui i Test Dusk
        run: php artisan dusk
      - name: Carica gli Screenshot
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: tests/Browser/screenshots
      - name: Carica i Log della Console
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: console
          path: tests/Browser/console

Chipper CI

Se stai usando Chipper CI per eseguire i tuoi test Dusk, puoi utilizzare il seguente file di configurazione come punto di partenza. Useremo il server integrato di PHP per eseguire Laravel in modo da poter ascoltare le richieste:

# 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
Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *