How to work with Page Objects in laravel-dusk - php

I'm a new in a php and dusk, but I try to work with page object in dusk, and I'm stuck because when I try to add page object to test, phpstorm said me that "Method logInUserName not found in $this". Can someone explain to me where i'm wrong?
I have page class:
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class LogInPage extends Page
{
/**
* Get the URL for the page.
*
* #return string
*/
public function url()
{
return '/login';
}
/**
*
* #return void
*/
public function logInUserName(Browser $browser)
{
$browser->type("#username", "lol");
}
}
I have test class
use Tests\Browser\Pages\LogInPage;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use PHPUnit\Framework\Assert;
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* #return void
*/
public function testLogInFail()
{
$this->browse(function (Browser $browser) {
$browser
->visit(new LogInPage)
->logInUserName()
->keys("#password","lol")
->click("button.btn-primary"));}

Agree this is annoying, there are 2 ways you could get around this
Restart the chaining on the browser object, you may still get a warning about logInUserName but you get your code assist back, which I agree can be useful when still learning.
$browser
->visit(new LogInPage)
->logInUserName();
$browser
->keys("#password","lol")
->click("button.btn-primary"));
Create a helper file defining your custom functions
Or use this gist and create a file in the root of your project that your IDE will read - https://gist.github.com/slava-vishnyakov/5eb90352fc97702f53a41888e5bae27a
Only issue is you may get a PHPSTORM warning about multiple definitions exist for class Browser...not sure how to get around that
Results in something like this
<?php
namespace Laravel\Dusk {
class Browser
{
/**
* #return Browser
*/
public function logInUserName()
{
}
}
}

Related

Override protected method in Laravel Illuminate class

I'd like to override the following method in Laravel's Illuminate\Foundation\Vite class:
/**
* Generate a script tag for the given URL.
*
* #param string $url
* #return string
*/
protected function makeScriptTag($url)
{
return sprintf('<script type="module" src="%s"></script>', $url);
}
...by adding a "defer" attribute to the script tag. How would I go about doing this, as this is a protected function?
May be like that :
<?php
namespace myApp;
use Illuminate\Foundation\Vite as IllVite;
class myClass extends IllVite{
//...
protected function makeScriptTag($url){
return sprintf('<script type="module" src="%s" defer></script>', $url);
}
//...
}
In the controller(s) which call "Vite", change :
use Illuminate\Foundation\Vite;
by
use myApp\myClass;
The svgta is right, if you use that method in another place in your app.
But, if you want to change only that particular behavior, and not to use it in other places, then you can rewrite the original class and replace it by binding changed class to the app in app/Providers/AppServiceProvider:
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
'Illuminate\Foundation\Vite', // original class that will be replaced
'App\VendorRewrites\ViteChanged' // custom class that will be injected
);
}
............
Another post that can help: Laravel 6-7 How Can I Override/Change a Vendor Class?

Laravel - Event causing 404 error

I am in the process of making realtime notifications and stumbled in this weird error. I have in my model a boot method which triggers an event called SendNotificationData (no listener). It handles when there is a new notification made.
Trial Controller
<?php
namespace App\Http\Controllers\Notification;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Models\Notification;
class NotificationController extends Controller
{
/**
* Trigger event to display notifications. This displays 404 error page
*
* #return none
*/
public function displayNotification()
{
$notification = new Notification();
$notification->EmployeeID = "EMP-00001";
$notification->NotificationText = "There is a new notification";
$notification->NotificationStatus = "unread";
$notification->NotificationType = "trial";
$notification->save();
}
}
Notification model boot method:
/**
* Handle booting of model.
*
* #var string
*/
public static function boot()
{
static::created(function ($data) {
event(new SendNotificationData($data));
});
parent::boot();
}
This is my SendNotificationData event:
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class SendNotificationData extends Event implements ShouldBroadcast
{
use SerializesModels;
public $new_notification_data;
/**
* Create a new event instance.
*
* #param $notification_data
* #return void
*/
public function __construct($new_notification_data)
{
$this->new_notification_data = $new_notification_data;
}
/**
* Get the channels the event should be broadcast on.
*
* #return array
*/
public function broadcastOn()
{
return ['new-notification'];
}
/**
* Customize event name.
*
* #return array
*/
public function broadcastAs()
{
return 'private-send-new-notification';
}
}
On Javascript
var newNotificationChannel = pusher.subscribe('new-notification');
newNotificationChannel.bind("private-send-new-notification", function(data) {
addNotification(data);
}); //This gives me no error in the console and the 404 error still shows up even if i remove this..
function addNotification(data)
{
console.log(data);
$('.notification-link').closest('li').append('This is a sample notification!!!');
}
Now, If I try to test adding some random notification in my controller, the event fires. However, it shows me the 404 error page. When I removed the ShouldBroadcast interface or remove the contents of the constructor, the error no longer shows up. I am confused what would be causing such an error when my other events are working fine. I might have missed something so please guide me.
I can't believe it, it was caused by the $incrementing variable in the model being set to false instead of true. If only laravel would show me the proper error stack trace.

Phalcon DI phpStorm IDE not understand my code

I'm using phalcon in a new project but i realized that phpStorm is not understanding my own injectors, for example:
/**
* Registering the helper component
*/
$di->set('helper', function () {
$helper = new \Frontend\Helpers\Common();
return $helper;
});
from my controller i can do that:
$this->helper->getHelp();
and it is working fine, but the IDE do not autocomplete my code. Of course i could do that:
/** #var \Frontend\Helpers\Common $helper */
$helper->...
In this case i can see all methods, but then each time when i need to use the helper i have to put this comments.
I would like to know if there is a way to use owns injectors and make the IDE abble to understand the code?
You can use this workaround.
services.php
/**
* Registering the helper component
*/
$di->set('helper', function () {
$helper = new \Frontend\Helpers\Common();
return $helper;
});
AbstractController
/**
* My Abstract controller
*
* #property \Frontend\Helpers\Common helper
*/.
class AbstractController extends \Phalcon\Mvc\Controller
{
}
TestController
class TestController extends AbstractController
{
public function indexAction()
{
$this->helper->...
}
}

Forward with another template

I'm facing a problem with the forward method of Symfony (v2.3).
Basicly, I have two controllers in two distinct bundles. Let's say DesktopBundle for a desktop version of the app, and MobileBundle for the mobile version.
I want to reuse code of an action of the DesktopBundle into an action of MobileBundle. What I do now is a forward :
DesktopController
namespace Acme\DesktopBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* #Route("/")
*/
class IndexController extends Controller
{
/**
* #Route("", name="desktopIndex")
* #Template()
*/
public function indexAction()
{
/* some code I don't want to duplicate */
return array(
'some' => 'var'
);
}
}
MobileController
namespace Acme\MobileBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* #Route("/")
*/
class IndexController extends Controller
{
/**
* #Route("", name="mobileIndex")
* #Template()
*/
public function indexAction()
{
return $this->forward('AcmeDesktopBundle:Index:index');
}
}
Now it works, but obviously the Response object is returned with the rendered template of the desktop version of the indexAction.
What I would like, is to get the variables and then render the template of the mobile version.
What I tried is to pass a variable into the forward method and then render the action conditionally into the desktop version:
return $this->forward(
'acmeDesktopBundle:Index:index',
array('mobile' => true)
);
This would work, but I don't really want to change to code inside the DesktopBundle but only the MobileBundle. Is there a way to achieve this ? I am missing something, or should I go to an entirely different solution ?
Forwarding means to redirect to the given page, but without changing the url on the client. I.e. redirect on the server side. If you want only access the return value of the action, simply call it. With the #Template annotation this is made very easy.
namespace Acme\MobileBundle\Controller;
use Acme\DesktopBundle\Controller\IndexController as DesktopController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* #Route("/")
*/
class IndexController extends Controller
{
/**
* #Route("", name="mobileIndex")
* #Template()
*/
public function indexAction()
{
$desktop = new DesktopController();
$desktop->setContainer($this->container);
$values = $desktop->indexAction();
// do something with it
return $values;
}
}

calling a php class function syntax

I am currently looking a this piece of code from a module called ZfcUser for Zend 2:
namespace ZfcUser\Controller;
use Zend\Form\Form;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Stdlib\ResponseInterface as Response;
use Zend\Stdlib\Parameters;
use Zend\View\Model\ViewModel;
use ZfcUser\Service\User as UserService;
use ZfcUser\Options\UserControllerOptionsInterface;
class UserController extends AbstractActionController
{
/**
* #var UserService
*/
protected $userService;
.
.
public function indexAction()
{
if (!$this->zfcUserAuthentication()->hasIdentity()) {
return $this->redirect()->toRoute('zfcuser/login');
}
return new ViewModel();
}
.
.
}
In the namespace ZfcUser\Controller\Plugin:
namespace ZfcUser\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
use ZfcUser\Authentication\Adapter\AdapterChain as AuthAdapter;
class ZfcUserAuthentication extends AbstractPlugin implements ServiceManagerAwareInterface
{
/**
* #var AuthAdapter
*/
protected $authAdapter;
.
.
/**
* Proxy convenience method
*
* #return mixed
*/
public function hasIdentity()
{
return $this->getAuthService()->hasIdentity();
}
/**
* Get authService.
*
* #return AuthenticationService
*/
public function getAuthService()
{
if (null === $this->authService) {
$this->authService = $this->getServiceManager()->get('zfcuser_auth_service');
}
return $this->authService;
}
My Questions:
From indexAction(), the controller plugin is called without being instantiated ($this->zfcUserAuthentication()->hasIdentity()), do controller plugins always work like this?.
What really happens in the hasIdentity()? I see getAuthService() returning something but not hasIdentity().I am not familiar with this type of advanced class implementation of function calling so I would truly appreciate any explanation here or topic I should look into.
I can't answer your first question, but regarding your second question:
The getAuthService() method in your code returns an AuthenticationService object, which has a hasIdentity() method.
So there are two different hasIdentity() methods:
In the AuthenticationService class (source code here).
In the ZfcUserAuthentication class which you're looking at.
This line of code in the ZfcUserAuthentication class:
return $this->getAuthService()->hasIdentity();
does three things:
$this->getAuthService() returns an AuthenticationService object.
The hasIdentity() method of that AuthenticationService object is then called, and it returns a boolean.
That boolean is then returned.
Imagine splitting the code into two parts:
// Get AuthenticationService object Call a method of that object
$this->getAuthService() ->hasIdentity();
Hope that helps!
All sorts of plugins in Zend Framework are managed by plugin managers, which are subclasses of AbstractPluginManager which is subclasss of ServiceManager.
$this->zfcUserAuthentication() proxies by AbstractController to pluginmanager internally.
AuthenticationService::hasIdentity() checks if something was added to storage during successful authentication attempt in this or previous request:
See here

Categories