Is createApplication() method required for Laravel Dusk test cases? - laravel

I am attempting to set up Laravel Dusk in a Laravel 5.4 application. Executing php artisan dusk does not launch a browser window as shown in this guide and I am trying to figure out why. PHPStorm complains that the ExampleTest class created during execution of the php artisan dusk:install command must implement the createApplication() method, but I cannot find any mention of this method in:
The official Laravel guide
This Scotch.io guide
A search of all files within the Laravel application directory using PHPStorm's search function.
ExampleTest class is as follows:
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* #return void
*/
public function testBasicExample()
{
$this->browse(function ($browser) {
$browser->visit('/')
->assertSee('Laravel');
});
}
}

Yes, this is required.
One method to solve this is to simply add the code contained in the trait (from a later version of Laravel) to your DuskTestCase.
/**
* Creates the application.
*
* #return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
Alternatively, you can create the CreatesApplication trait, paste the above code within in, and use CreatesApplication within your DuskTestCase.

Related

FIXED AFTER I REPORTED THE BUG - laravel new project --auth doesn't create HomeController.php

I have created a new laravel project with --auth, but HomeController hasn't been created. I have tried with composer require laravel/ui and php artisan ui vue --auth in an existing project and I encounter the same problem. Any solutions? I know I coould create it manually, but it is annoying. Thanks
The HomeController is not being created at the time of writing 29-Apr-2020
a quick workaround is to create it manually:
php artisan make:controller HomeController
and then fill it with the following code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}
It was a bug that was fixed by the Laravel staff in Laravel UI version 2.0.3.

Unable to run Laravel Dusk out of the box

I followed the instructions from here. But I am having this error and can't figure out why.
There was 1 error:
1) Tests\Browser\Auth\LoginTest::testLogin
TypeError: Argument 1 passed to Facebook\WebDriver\Remote\DesiredCapabilities::__construct() must be of the type array, null given, called in /var/www/xxxxxx/vendor/facebook/webdriver/lib/Remote/RemoteWebDriver.php on line 127
/var/www/xxxxxx/vendor/facebook/webdriver/lib/Remote/DesiredCapabilities.php:33
/var/www/xxxxxx/vendor/facebook/webdriver/lib/Remote/RemoteWebDriver.php:127
/var/www/xxxxxx/tests/DuskTestCase.php:40
/var/www/xxxxxx/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:192
/var/www/xxxxxx/vendor/laravel/framework/src/Illuminate/Support/helpers.php:816
/var/www/xxxxxx/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:193
/var/www/xxxxxx/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:93
/var/www/xxxxxx/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:64
/var/www/xxxxxx/tests/Browser/Auth/LoginTest.php:27
Here is my DuskTestCase
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* #beforeClass
* #return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* #return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--no-sandbox', // as suggested in one of the forums but didn't work
'--window-size=1920,1080',
]);
return RemoteWebDriver::create(
'http://localhost:3402', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
}
I have installed facebook/webdriver but same error. I am using Ubuntu and Docker. Chrome also isn't my default browser, changing that didn't help. What am I msising here?
Browser drivers are downloaded to \vendor\laravel\dusk\bin within your laravel app.
Navigate to the location from your file explorer and run the executable file (depending on your Operating system, mine is Windows and I'm running chromedriver-win.exe). You should see this
This is the port number to use in your DuskTestCase.php file
Also, you need to ensure you set your APP_URL variable in the .env file to the URL accessible from your browser (mine is APP_URL=http://localhost:8888).
Note: I'm not using port 9515 in my .env file.
I hope that helps.

Laravel Testing Request - Service Provider - Middleware Issue

I have a Laravel 5.5 App where I have a Service Provider which I use to put some stuff in the request->attributes to access it everywhere (simplified):
namespace App\Providers;
use App\Models\Domain;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
class GlobalVarsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap the application services.
*
* #param Request $request
*
* #return void
*/
public function boot(Request $request)
{
$domain = .. get domain with language and some logic and cache because of multiple domains ..
$request->attributes->add(['domain' => $domain]);
}
}
I do this in a Service Provider, because then I can already use it in other Service Providers like my ViewComposerServiceProvider, where I compose some stuff for the Views. I'm able to access $domain everywhere like this:
$this->domain = $request->attributes->get('domain');
It works great. BUT not in testing. When I want to access $domain in a Unit Test in a middleware the $request->attributes are empty (In UnitTests as in DuskTests either).
It looks like the testing environment uses a different Request Lifecycle? If yes, what else is different in the testing environment?
What am I doing wrong?
-- Edit --
Test Example:
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
TestCase uses trait MakesHttpRequests which has method call. When you use get method in your tests, it's simply a shortcut to this.
In your test you can use it like this:
$this->call('GET', '/url/here', $yourRequestParametersHere);

Custom laravel migration command "[Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable"

I'm trying to create a custom laravel (5.2) migration command that basically works the same as migrate:status except it just lists the pending migrations instead of all the migrations.
To do this i've very simply copied the migrate:status into another class within my app/console directory and adjusted the code to suit my needs. However whenever I try to run it I get an error:
[Illuminate\Contracts\Container\BindingResolutionException]
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\PendingMigrations, Illuminate\Database\Migrations\Migrator].
The contents of the class itself and the fire() method doesn't seem to matter as it doesn't get that far, it fails within the __construct() method.
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Migrations\Migrator;
class PendingMigrations extends Command
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'migrate:pending';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Shows a list of pending migrations';
/**
* The migrator instance.
*
* #var \Illuminate\Database\Migrations\Migrator
*/
protected $migrator;
/**
* Create a new migration rollback command instance.
*
* #param \Illuminate\Database\Migrations\Migrator $migrator
* #return \Illuminate\Database\Console\Migrations\StatusCommand
*/
public function __construct(Migrator $migrator)
{
parent::__construct();
$this->migrator = $migrator;
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
}
}
The reason for it is likely to be something to do with the IoC container and the order with which things are loaded, but I don't know enough about the inner workings of Laravel to figure out any more than that.
It surely must be possible?
I am currently stuck on 5.2, so i'm not sure if this problem exists in more recent versions.
The only thing i've attempted so far is added the migration service provider to the top of the list in config/app.php however it didn't seem to have an affect and it was just a random guess anyway.
providers' => [
Illuminate\Database\MigrationServiceProvider::class,`
]
I got around this using:
$this->migrator = app('migrator');
but it is not necessarily the best way to do this
The Migrator instance is not bound to the class name in the IoC container, it is bound to the migrator alias.
From Illuminate\Database\MigrationServiceProvider:
/**
* Register the migrator service.
*
* #return void
*/
protected function registerMigrator()
{
// The migrator is responsible for actually running and rollback the migration
// files in the application. We'll pass in our database connection resolver
// so the migrator can resolve any of these connections when it needs to.
$this->app->singleton('migrator', function ($app) {
$repository = $app['migration.repository'];
return new Migrator($repository, $app['db'], $app['files']);
});
}
Since the class name is not bound in the IoC container, when Laravel resolves your command and attempts to resolve the Migrator dependency, it attempts to build a new one from scratch and fails because the Illuminate\Database\Migrations\MigrationRepositoryInterface is also not bound in the IoC container (hence the error you're receiving).
Since Laravel can't figure this out itself, you need to either register the binding for the Migrator class name, or you need to register the binding for your command. Laravel itself registers all the bindings for the commands in the Illuminate\Foundation\Providers\ArtisanServiceProvider. An example of the command.migrate binding:
/**
* Register the command.
*
* #return void
*/
protected function registerMigrateCommand()
{
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator']);
});
}
So, in your AppServiceProvider, or another service provider you setup, you can add one of the following:
Register the command in the IoC:
$this->app->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
Or, register the Migrator class name in the IoC:
$this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ($app) {
return $app['migrator'];
});
As I don't want to register the migrator everywhere in the app, but I still want to extend the MigrateCommand itself, I came up with this approach to maintain my app as it is:
public function __construct()
{
app()->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
parent::__construct(app('migrator'));
}

Clear views cache on Lumen

Few weeks ago, I had the same problem in Laravel 5.1, which I could solve with this solution.
However, now I'm facing the same issue in Lumen, but I can't call php artisan view:clear to clear the cached files. There is any other way?
Thanks!
There's no command for the view cache in lumen, but you can easily create your own or use my mini package found at the end of the answer.
First, put this file inside your app/Console/Commands folder (make sure to change the namespace if your app has a different than App):
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class ClearViewCache extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $name = 'view:clear';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Clear all compiled view files.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$cachedViews = storage_path('/framework/views/');
$files = glob($cachedViews.'*');
foreach($files as $file) {
if(is_file($file)) {
#unlink($file);
}
}
}
}
Then open app/Console/Kernel.php and put the command inside the $commands array (again, mind the namespace):
protected $commands = [
'App\Console\Commands\ClearViewCache'
];
You can verify that everything worked by running
php artisan
inside the project's root.
You will now see the newly created command:
You can now run it like you did in laravel.
EDIT
I've created a small (MIT) package for this, you can require it with composer:
composer require baao/clear-view-cache
then add
$app->register('Baao\ClearViewCache\ClearViewCacheServiceProvider');
to bootsrap/app.php and run it with
php artisan view:clear

Resources