I am writing some browser tests using Laravel Dusk. Problem is that when using Dusk's type or value methods it changes visibly the data in the fields but not the underlying Vue models' data. I am trying to test a form using this test class:
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\User;
use Tests\Browser\Pages\Login;
class ProfileFormTest extends DuskTestCase
{
public User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create(['country' => 'Bulgaria']);
}
public function test_profile_form(): void
{
$user = $this->user;
$this->browse(function (Browser $browser) use ($user) {
$generalError = __('validation.general_error');
$requiredMsg = __('validation.required');
$invalidPhoneMsg = __('validation.phone_number');
$browser->visit(new Login)
->loginUser($user)
->visit('/profile')
->waitForLocation('/profile')
->assertSee('Major')
->assertSee('Name')
->assertSee('Zuname')
->assertSee('Museum')
->assertSee('Adresse')
->assertSee('Stadt')
->assertSee('Staat')
->assertSee('Email')
->assertSee('Telefon')
->assertSee('Erlaubt beacons')
->assertSee('Profil speichern')
->assertSee('Aktuelles Kennwort')
->assertSee('Neues Kennwort')
->assertSee('Neues Kennwort wiedeholen')
->assertSee('Neues Kennwort speichern')
// test empty profile data
->pause(4000)
->value('#name', '')
->value('#surname', '')
->value('#museum', '')
->type('#address', '')
->type('#city', '')
->type('#country', '')
->type('#email', '')
->type('#phone', '')
->click('#save-profile')
->waitForText($generalError)
->pause(60000)
->within('#name', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#surname', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#museum', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#adress', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#city', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#country', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#email', function($browser) use ($requiredMsg) {
$browser->assertSee($requiredMsg);
})
->within('#phone', function($browser) use ($invalidPhoneMsg) {
$browser->assertSee($invalidPhoneMsg);
});
});
}
}
I've disabled headless chrome and added some pause calls in order to see what is going on. The problem is with the type/value calls. I can see that they empty the fields in the browser but when I check the request data send to the server to save the profile form - the orignal data is used.
The form is built using Vue 2.6 and I am using vuelidate to validate the data so the input fields bind to e.g $v.user.city.$model. I tried binding straight to the model used but the effect is the same.
I am lost. Any ideas?
Related
I'm making a request with Axios and the time it takes for making a request is approximately ~1.21s.
This is my sample controller for the delete method.
public function destroy($id)
{
$business = Business::findOrFail($id);
if($business->delete())
{
return new BusinessResource($business);
}
}
And this is my Axios script.
deleteBusiness: function(id)
{
let vm = this;
axios.delete('api/business/'+id)
.then(function (response){
alert("Business Deleted");
vm.fetchBusiness();
})
.catch(function (error) {
console.log(error);
});
},
This is my model, implementing Laravel Scout & TNTSearch:.
namespace App;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
class Business extends Model
{
use Searchable;
protected $table = 'business';
public function toSearchableArray()
{
return [
'id' => $this->id,
'businessName' => $this->businessName,
];
}
public function searchableAs()
{
return 'business_index';
}
}
When I remove the use Searchable in the model, the request time reduces to approximately ~300ms. Is there any way to reduce the time of this request without removing the use Searchable in the model? Do I need to use Redis?
I try to catch an event, when job is completed
Test code:
class MyTest extends TestCase {
public function testJobsEvents ()
{
Queue::after(function (JobProcessed $event) {
// if ( $job is 'MyJob1' ) then do test
dump($event->job->payload());
$event->job->payload()
});
$response = $this->post('/api/user', [ 'test' => 'data' ], $this->headers);
$response->assertSuccessful($response->isOk());
}
}
method in UserController:
public function userAction (Request $request) {
MyJob1::dispatch($request->toArray());
MyJob2::dispatch($request->toArray());
return response(null, 200);
}
My job:
class Job1 implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $data = [];
public function __construct($data)
{
$this->data= $data;
}
public function handle()
{
// Process uploaded
}
}
I need to check some data after job is complete but I get serialized data from
$event->job->payload() in Queue::after And I don't understand how to check job ?
Well, to test the logic inside handle method you just need to instantiate the job class & invoke the handle method.
public function testJobsEvents()
{
$job = new \App\Jobs\YourJob;
$job->handle();
// Assert the side effect of your job...
}
Remember, a job is just a class after all.
Laravel version ^5 || ^7
Synchronous Dispatching
If you would like to dispatch a job immediately (synchronously), you may use the dispatchNow method. When using this method, the job will not be queued and will be run immediately within the current process:
Job::dispatchNow()
Laravel 8 update
<?php
namespace Tests\Feature;
use App\Jobs\ShipOrder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Bus;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped()
{
Bus::fake();
// Perform order shipping...
// Assert that a job was dispatched...
Bus::assertDispatched(ShipOrder::class);
// Assert a job was not dispatched...
Bus::assertNotDispatched(AnotherJob::class);
}
}
This my generic method, using a route
Route::get('job-tester/{job}', function ($job) {
if(env('APP_ENV') == 'local'){
$j = "\\App\Jobs\\".$job;
$j::dispatch();
}
});
On my Symnfony3 project I noticed that during registration some events are generated where I can override the response. eg. Instead of rendering the default twig template and redirect to just return a JsonResponse with a successMessage.
Therefore I did the following Event Subscriber:
namespace AppBundle\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use AppBundle\Constants\AjaxJsonResponseConstants;
use Symfony\Component\HttpFoundation\JsonResponse;
use FOS\UserBundle\Event\FilterUserResponseEvent;
class UserRegistrationResponseChanger implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
$subscribedEvents=[
// FOSUserEvents::REGISTRATION_INITIALIZE=>[],
FOSUserEvents::REGISTRATION_COMPLETED=>[],
FOSUserEvents::REGISTRATION_SUCCESS=>["setJsonResponseOnSuccess",-1],
FOSUserEvents::REGISTRATION_FAILURE=>["setJsonResponseOnFailure",-1],
// FOSUserEvents::REGISTRATION_CONFIRM=>[],
// FOSUserEvents::REGISTRATION_CONFIRMED=>[]
];
}
public function setJsonResponseOnSuccess(FormEvent $formEvent)
{
$response=['status'=>AjaxJsonResponseConstants::AJAX_ACTION_SUCCESS,'message'=>"User Sucessfully Registered please check your mail."];
$response=new JsonResponse($response);
$formEvent->setResponse($response);
return $response;
}
public function setJsonResponseOnFailure(FormEvent $formEvent)
{
$response=['status'=>AjaxJsonResponseConstants::AJAX_ACTION_FAIL,'message'=>"You cannot register please try again later"];
$response=new JsonResponse($response);
$formEvent->setResponse($response);
return $response;
}
}
Also on my services.yml I have put the following:
app.user_register.subscriber:
class: AppBundle\EventSubscriber\UserRegistrationResponseChanger
tags:
- { name: app.user_register.subscriber }
And the command
In order to override on how the response will get returned but somehow it fails to do so and redirects to the default page. What I try to acheive it to perform the registration via ajax call instead of rendering the registration page and redirecting.
You should prioritize the REGISTRATION_SUCCESS event when you have registration confirmation (default behaviour in FOSUserBundle), see http://symfony.com/doc/master/bundles/FOSUserBundle/controller_events.html#registration-success-listener-with-enabled-confirmation-at-the-same-time
The service definition needs to be like this:
#app/config/services.yml
app.security_registration_success:
class: Path\To\Your\EventListener\RegistrationSuccessListener
tags:
- { name: kernel.event_subscriber }
An example of a registration success listener:
<?php
declare(strict_types=1);
namespace Path\To\Your\EventListener;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class RegistrationSuccessListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [FOSUserEvents::REGISTRATION_SUCCESS => [['onRegistrationSuccess', -10]]];
}
public function onRegistrationSuccess(FormEvent $event): void
{
$event->setResponse(new JsonResponse());
}
}
You should do this steps:
First of all you should use kernel.event_subscriber instead of app.user_register.subscriber when you define the event subscriber therfore your subscriber will be defined like that:
app.user_register.subscriber:
class: AppBundle\EventSubscriber\UserRegistrationResponseChanger
tags:
- { name: kernel.event_subscriber }
To the services.yml.
Furthermore the getSubscribedEvents must return the array of the listeners. Also the FOSUserEvents::REGISTRATION_COMPLETED MUST Have a listener even if it isdoes not have an implementation, if you do not want a listener just comment the like.
In the end your listener shuld be implemented like that:
namespace AppBundle\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use AppBundle\Constants\AjaxJsonResponseConstants;
use Symfony\Component\HttpFoundation\JsonResponse;
use FOS\UserBundle\Event\FilterUserResponseEvent;
class UserRegistrationResponseChanger implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
$subscribedEvents=[
// FOSUserEvents::REGISTRATION_INITIALIZE=>[],
// FOSUserEvents::REGISTRATION_COMPLETED=>[],
FOSUserEvents::REGISTRATION_SUCCESS=>["setJsonResponseOnSuccess",-1],
FOSUserEvents::REGISTRATION_FAILURE=>["setJsonResponseOnFailure",-1],
// FOSUserEvents::REGISTRATION_CONFIRM=>[],
// FOSUserEvents::REGISTRATION_CONFIRMED=>[]
];
return $subscribedEvents;
}
public function setJsonResponseOnSuccess(FormEvent $formEvent)
{
$response=['status'=>AjaxJsonResponseConstants::AJAX_ACTION_SUCCESS,'message'=>"User Sucessfully Registered please check your mail."];
$response=new JsonResponse($response);
$formEvent->setResponse($response);
return $response;
}
public function setJsonResponseOnFailure(FormEvent $formEvent)
{
$response=['status'=>AjaxJsonResponseConstants::AJAX_ACTION_FAIL,'message'=>"You cannot register please try again later"];
$response=new JsonResponse($response);
$formEvent->setResponse($response);
return $response;
}
}
starting with Silex.
Say I want a localised site where all routes have to start with /{_locale} and don't fancy repeating myself as :
$app->match('/{_locale}/foo', function() use ($app) {
return $app['twig']->render('foo.twig');
})
->assert('_locale', implode('|', $app['languages.available']))
->value('_locale', $app['locale.default'])
->bind('foo');
$app->match('/{_locale}/bar', function() use ($app) {
return $app['twig']->render('bar.twig');
})
->assert('_locale', implode('|', $app['languages.available']))
->value('_locale', $app['locale.default'])
->bind('bar');
Ideally, I'd like to create a base route that would match the locale and subclass it in some way but couldn't figure out by myself how to trigger that in an elegant way.
I think you can delegate the local detection with mount function:
You mount a route for each local you want to support, but they redirect to the same controller:
$app->mount('/en/', new MyControllerProvider('en'));
$app->mount('/fr/', new MyControllerProvider('fr'));
$app->mount('/de/', new MyControllerProvider('de'));
And now the local can be an attribute of your controller:
class MyControllerProvider implements ControllerProviderInterface {
private $_locale;
public function __construct($_locale) {
$this->_locale = $_locale;
}
public function connect(Application $app) {
$controler = $app['controllers_factory'];
$controler->match('/foo', function() use ($app) {
return $app['twig']->render('foo.twig');
})
->bind('foo');
$controler->match('/bar', function() use ($app) {
return $app['twig']->render('bar.twig');
})
->bind('bar');
return $controler;
}
}
I upgrade Lumen from 5.4 to 5.7, and I want to be able to log DB queries for debugging.
Here's the conf/source code. I have to use 'LumenDB' alias because of naming conflict of a third-party library.
I expect the query could be logged, but I can not see them in lumen.log.
MyApplication.php
<?php
namespace App;
use Illuminate\Support\Facades\Facade;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
class MyApplication extends \Laravel\Lumen\Application {
public function withFacades($aliases = true, $userAliases = [])
{
Facade::setFacadeApplication($this);
if (! static::$aliasesRegistered) {
static::$aliasesRegistered = true;
class_alias('Illuminate\Support\Facades\Auth', 'Auth');
class_alias('Illuminate\Support\Facades\Cache', 'Cache');
class_alias('Illuminate\Support\Facades\DB', 'LumenDB');
class_alias('Illuminate\Support\Facades\Event', 'Event');
class_alias('Illuminate\Support\Facades\Gate', 'Gate');
class_alias('Illuminate\Support\Facades\Log', 'Log');
class_alias('Illuminate\Support\Facades\Queue', 'Queue');
class_alias('Illuminate\Support\Facades\Schema', 'Schema');
class_alias('Illuminate\Support\Facades\Validator', 'Validator');
}
}
protected function registerLogBindings()
{
$this->singleton('Psr\Log\LoggerInterface', function () {
return new Logger('lumen', $this->getMonologHandler());
});
}
protected function getMonologHandler()
{
$maxFiles = 7;
$rotatingLogHandler = new RotatingFileHandler(storage_path('logs/lumen.log'), $maxFiles);
$rotatingLogHandler->setFormatter(new LineFormatter(null, null, true, true));
$handlers = [];
$handlers[] = $rotatingLogHandler;
return $handlers;
}
}
bootstrap/app.php
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\EventServiceProvider::class);
\LumenDB::connection()->enableQueryLog();
app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\DB;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
Log::info("-------");
Log::info($query->sql);
});
}
The query is executed inside a Service method which is called by a Command scheduled by cron.
public function getAllStatsToday()
{
$today = new \DateTime();
$today->setTime(0, 0, 0);
$productUsageStats = ProductUsageStat::make()
->where('updated_at', '>', $today)
->get();
return $productUsageStats;
}
You have not registered the AppServiceProvider in your bootstrap/app.php. Because of this the boot method in your AppServiceProvider is never registered and thus the logging is never executed.
You should change app.php to the following:
$app->register(App\Providers\EventServiceProvider::class);
$app->register(App\Providers\AppServiceProvider::class);
\LumenDB::connection()->enableQueryLog();