I need to run one of my Laravel Dusk tests from an artisan command so that it processes daily. I've tried $this->call('dusk');
in my command but that runs all of dusk's tests and doesn't allow me to add a group or a filter. I need to run just 1 test. How can I add a filter?
$this->call('dusk', [ '--group' => 'communication_tests' ]);
or
$this->call('dusk', [ '--filter' => 'tests\Browser\myTestFile::myTestMethod' ]);
doesn't work and it ignores the options passed in. Any ideas on how to accomplish this?
1st create your working Laravel Dusk Test. Test it out with php artisan dusk and make sure it's working.
2nd create your own command in the app\Commands folder called DuskCommand to overwrite laravels native DuskCommand and have it's signature be 'dusk'. Have it extend Laravel\Dusk\Console\DuskCommand and write into its handle method the code below (see https://github.com/laravel/dusk/issues/371 for other version of this code). I edited mine to remove the $this->option('without-tty') ? 3 : 2
ternary statement so it just reads 2
for mine as that option didn't exist or wasn't needed for my version of laravel.
3rd add your new class to the Kernel so that when you call php artisan dusk the class is recognized.
4th create your new command to programmatically run your dusk test using the final line added by @taytus.
5th add that new class to the Kernel also.
Here's my file run down below...
My laravel dusk test (tests\Browser\CommunicationsTest.php) (STEP 1)
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use App\Models\User;
class CommunicationsTest extends DuskTestCase
{
/**
* A Dusk test example.
* @group all_communication_tests
* @return void
*/
public function test_that_all_coms_work()
{
// Test Text
$this->browse(function (Browser $browser) {
$browser->resize(1920, 1080);
$browser->loginAs(7)
->visit('/events/types/appointments')
->assertSee('Automated Messages')
->click('.text_message0')
->pause(1000)
->click('.send-eng-text-btn')
->pause(1000)
->type('phone', env('TESTING_DEVELOPER_PHONE'))
->click('.send-text')
->pause(5000)
->assertSee('Your test text has been sent.');
// Test Email
$browser->visit('/events/types/appointments')
->assertSee('Automated Messages')
->click('.email0')
->assertSee('Automated Messages')
->driver->executeScript('window.scrollTo(595, 1063);');
$browser->click('.send-eng-email-btn')
->pause(2000)
->click('.send-email')
->pause(10000)
->assertSee('Your test email has been sent.');
// Test Call
$browser->visit('/audio/testcall')
->assertSee('Test Call')
->type('phone', env('TESTING_DEVELOPER_PHONE'))
->press('Call')
->pause(3000)
->assertSee('Test Call Queued');
});
}
}
My Overwritting dusk command (app\Console\Commands\DuskCommand.php) (STEP 2)
<?php
namespace App\Console\Commands;
use Laravel\Dusk\Console\DuskCommand as VendorDuskCommand;
use Symfony\Component\Process\ProcessBuilder;
class DuskCommand extends VendorDuskCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'dusk';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run Tests on our system... by extending the Laravel Vendor DuskCommand.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->purgeScreenshots();
$this->purgeConsoleLogs();
$options=array();
// This line checks if it is a direct call or if has been called from Artisan (we assume is Artisan, more checks can be added)
if($_SERVER['argv'][1]!='dusk'){
$filter=$this->input->getParameterOption('--filter');
// $filter returns 0 if has not been set up
if($filter){
$options[]='--filter';
$options[]=$filter;
// note: --path is a custom key, check how I use it in Commands\CommunicationsTest.php
$options[]=$this->input->getParameterOption('--path');
}
}else{
$options = array_slice($_SERVER['argv'], 2);
}
return $this->withDuskEnvironment(function () use ($options) {
$process = (new ProcessBuilder())
->setTimeout(null)
->setPrefix($this->binary())
->setArguments($this->phpunitArguments($options))
->getProcess();
try {
$process->setTty(true);
} catch (RuntimeException $e) {
$this->output->writeln('Warning: '.$e->getMessage());
}
return $process->run(function ($type, $line) {
$this->output->write($line);
});
});
}
}
Now add that new command to the Kernel so it will register when you call it and overwrite the functionality of the native/vendor DuskCommand. (STEP 3)
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
// .... preceding commands....
Commands\DuskCommand::class,
];
Now create your command that will call dusk programmatically - here's mine (STEP 4)
<?php
namespace App\Console\Commands;
use DB;
use Illuminate\Console\Command;
class TestCommunications extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test:communications';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Test Email, Text, and Phone Calls to make sure they\'re operational.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$response = $this->call('dusk',['--filter'=>'test_that_all_coms_work','--path'=>'tests/Browser/CommunicationsTest.php']);
}
}
and register it in the Kernel (STEP 5)...
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
.... preceding commands....
Commands\DuskCommand::class,
Commands\TestCommunications::class,
];
Now run php artisan dusk
and it should hit the extending DuskCommand and work fine. Then call your new php artisan command that replaces my TestCommunications.php file and it should run dusk perfectly... assuming your test works of course. Let me know if you have any question or if I left anything out.
Remember Dusk only works in a local environment ... this is not something you want/should/natively can implement on a production environment