Laravel dusk scroll and waitFor dynamic element - laravel

I have lists which dynamically shows when scroll down the page, for example, when I open the page, there is only <div id="list-0" date="2018-01-01"></div>, and after I scroll down, <div id="list-1" date="2018-01-02"></div> will dynamically shows up, same as <div id="list-2" date="2018-01-03"></div> and <div id="list-3" date="2018-01-04"></div> and so on.
Those lists are not able to see on chrome view-source(ctrl+U)
I want to use laravel dusk to scroll down page if there is no list-n, and retrieve the date attribute.
Here is my code
/** #test */
public function scroll_down_each_list_and_match_the_date()
{
$this->browse(function (Browser $browser) {
$browser->visit('/lists');
$listsLength = $this->listsLength();
for($i = 0; $i <= $listsLength; $i++){
$selector = "div[id=\"list-$i\"]";
while(!$browser->waitFor($selector)){
$browser->driver->executeScript('window.scrollTo(0, 500);');
}
$date = $browser->attribute($selector, 'date');
$browser->assertEquals($date, $this->date($i));
}
});
}
The Assertion Error is
Facebook\WebDriver\Exception\TimeOutException: Waited 5 seconds for selector [div[id="list-1"]].
if I replace while(!$browser->waitFor($selector)) to while(!$browser->element($selector)), it seems like laravel dusk will execute forever and won't stop.

Instead of:
$selector = "div[id=\"list-$i\"]";
Maybe try:
$selector = $browser->attribute('list-$i', 'id');

Related

how to calculate percentage while getting data from api response

I'm getting response from an api in Laravel using HTTP client. And It takes some time.I just want to calculate that how much percentage of data is fetched from api. And when progressbar of 100% complete it means data is completely fetched and ready to show. So any solution for this.
if you are that flexible then you can do it this way ;)
You can use queue job with livewire and tailwind css to achieve this goal.
install tailwind.
install livewire with this command composer require livewire/livewire.
run php artisan make:livewire YourClassWire to create livwire class, and you can find it under /App/Http/Livewire.
make YourClassWire somthing like this :
class YourClassWire extends Component
{
public $visibility;
// you need this to hide progress bar if it is 100% or 0
public $progress ;
// this will be your progress.
public $total ;
// this is your total job
protected $listeners = ['reresh']; // this will called from the view
public function mount()
{
$this->total = \DB::table('jobs')->count(); // you must change this if you have multiple queue with different names
$this->visibility = $this->total > 0 ? 'visible' : 'hidden' ; // we need it to show hide porgress ;
$this->progress = \DB::table('jobs')->count() * 100 / $this->total; // simple equation to get progress percentage
$this->render(); // you now call render each time the view refresh
}
public function refresh()
{
// this function will be responsible from updating the progress
$this->visibility = $this->total > 0 ? 'visible' : 'hidden' ; // we need it to show hide porgress ;
$this->progress = \DB::table('jobs')->count() * 100 / $this->total; // simple equation to get progress percentage.
}
public function render()
{
return view('livewire.your-class-wire',) // note this will created with command you executed earlier `php artisan make:livewire YourClassWire` and you can find it in view/livewire
->with('progress', $this->progress)
->with('visibility',$this->visibility);
}
}
now in your livewire view you can create your progress bar
<div wire:poll.2s="refresh">
<div class="{{$visibility}}">
<div class=" progress_main flex w-3/6 bg-gray-200 rounded-full h-1 mb-4 dark:bg-gray-700">
<!-- controll the width with the progress -->
<div class= "progressbar border-0 relative align-middle bg-gradient-to-r from-[#d72323] via-[#dddd03] to-[#248401] dark:bg-[#dcf5ff] h-1 rounded-full" style="width: {{ $progress }}%">
<div class="overflow-hidden absolute bottom-[-9px] flex items-center right-0 mr-[-60px] z-30 h-6 text-2xl text-[#f36d1f]">✈️
<!-- now this text icon will move to the end based on your progress -->
</div>
</div>
</div>
</div>
</div>
I hope this guide you to what you want..success
You could do some logic like:
get data from API
foreach dataset taken, do the calculation
after the calculation store/update model
get out of the foreach loop or take the next entry
or if you need to calculate average or something like that you can go
$emptyArray = [];
foreach($responses as $response) {
$emptyArray [$response->id][] = $response->data; // data === type of integer
}
$emptyArray_avg = [];
foreach($emptyArray as $arrEl) {
$emptyArray_avg[] = array_sum($arrEl)/count($arrEl);
}
Note: If there are a lot of data in the JSON response, queues would be a good solution for it :)
Queues

Testing vuetify v-select with laravel dusk

Does anyone know how to test vuetify v-select with laravel dusk?
I've tried $browser->select('size', 'Large'); without success
this is one of the v-selects that i want to test
<v-flex class="form__item">
<v-select
id="estatus"
dusk="estatus"
v-model="form.id_estatus"
label="Estatus"
:items="estatus"
item-text="nombre"
item-value="id"
v-validate="{ required:true }"
data-vv-name="estatus"
data-vv-as="estatus"
:error-messages="(errors.collect('estatus'))"
required
></v-select>
</v-flex>
And this the generated HTML
When v-select is clicked, shows the option list in other part of the HTML
Click on the .v-select element and wait for the select to open up:
$browser->click('.v-select');
$browser->waitFor('.v-menu__content');
Then you can select an option by index:
$browser->elements('.v-menu__content a')[2]->click();
Or by text (using XPath):
$selector = "//div[#class='v-menu__content menuable__content__active']//div[text()='State 3']";
$browser->driver->findElement(WebDriverBy::xpath($selector))->click();
(1) In the Vue template:
Wrap the <v-select> with a <div id="selectStatus"></div>
(2) In the Dusk test, use:
$browser->click('#selectStatus .v-select');
$browser->waitFor('.menuable__content__active');
$browser->elements('.menuable__content__active a')[1]->click();
$browser->waitUntilMissing('.menuable__content__active');
$browser->assertSeeIn('#selectStatus .v-select','theStatusIExpectToSee');
— OR —
The <v-select> can be tested without wrapping it in a <div id="foo"></div> if we use a slightly more complicated strategy.
Instead of putting the id on a div wrapper, we can put the id directly on the v-select or even rely on text content contained within the v-select with the following strategy (involving xpath):
use Facebook\WebDriver\WebDriverBy;
public function keyOfParentContainingItem($elements, $itemName, $itemValue = null){
$elWithItemKey = null;
$itemNamePrefix = ($itemName !== 'text()') ? '#' : '';
$itemValueMatchString = ($itemValue !== null) ? '="'.$itemValue.'"' : '';
foreach($elements as $k=>$v){
$xpath = './/*['.$itemNamePrefix.$itemName.$itemValueMatchString.']';
if(!empty($v->findElements(WebDriverBy::xpath($xpath)))){
$elWithItemKey = $k;
}
}
return $elWithItemKey;
}
public function testMyVSelect(){
$this->browse(function (Browser $browser) {
$browser->visit('/myAddress');
$els = $browser->elements('.v-select');
$els[$this->keyOfParentContainingItem($els,'id','idOnVSelect')]->click();
$browser->waitFor('.menuable__content__active');
$menuEls = $browser->elements('.menuable__content__active a');
$menuEls[$this->keyOfParentContainingItem($menuEls,'text()',"some text")]->click();
$browser->waitUntilMissing('.menuable__content__active');
$els = $browser->elements('.v-select');
$key = $this->keyOfParentContainingItem($els,'text()',"some text");
$this->assertTrue($key !== null);
});
}
Using Vuetify 1.5.14.
Click on the v-select element for it to list the options, then wait for the dropdown-menu class to be present before selecting an element from the list (2 in the example below) by clicking on the third tag in the menu list.
Finally, wait until the dropdown-menu class disappears before moving onto the next part of the test.
$browser->click('#region')
->with('#region', function ($vSelect) {
$vSelect->waitFor('.dropdown-menu')
->elements('.dropdown-menu a')[2]->click();
})
->waitUntilMissing('.dropdown-menu');

cakephp ajax function issue

I have 2 functions in "events" (index, event_ajax)controller in my cakephp(2.5) web site. I'm trying to load HTML block to 'index.ctp' page by calling to 'event_ajax' function using ajax. When I call to this function it shows nothing. Look at 'net' tab in firebug it shows internal server error and 'net'->'Response' tab I can see whole layout is loaded.
I'm little confuse about in this scenario, can any one give a little explanation for following questions??? thanks in advance :)
Is it possible to call actions in same controller using ajax function ??
How 'Response' tab shows layout when '$this->layout' is set to NULL ??
when type url 'example.com/events/event_ajax', output data still '$this->autoRender=false'. how can this happen ??
this is my 'event_ajax' action.
public function event_ajax($x=1) {
$this->layout = NULL;
$this->autoRender = false ;
$contName = $this->Page->conName($x);
$latestContEvents = $this->Page->latestContEvent($x);
$internal = '';
if (!empty($latestContEvents)){
foreach ($latestContEvents AS $latestContEvent){
$internal .= '<li class="pull-left"> <div class="content-wrapper">'..... //do something
}
else {
$internal = '<p> No events found for this continent</p>';
}
$ContEvents = '<div class="carousel events-location-carousel">'.$internal.'</div> ';
return $ContEvents;
// return json_encode($ContEvents);
}
Try with
$this->layout = 'ajax';

Laravel back using {{ URL::previous() }} doesn't work if there are validation errors

I want to make a cancel button on a simple CRUD edit form. However it seems that when there are validation errors the cancel button does not work.
I guess because it routes from controller#edit to controller#update the button just goes to controller#edit again instead of the actual previous page. Is this normal or did I do something wrong? How do I make it work?
attendee/edit.blade.php
<div class=pull-right>
{{Form::submit('Update',array('class'=>'btn btn-success'))}}
<a href = "{{URL::previous()}}" class = 'btn btn-warning'>Cancel</a>
</div>
{{ Form::close() }}
routes.php
Route::get('user/attendees', 'UserController#getAttendees');
Route::resource('attendee', 'AttendeeController');
attendee controller
public function edit($id)
{
$user = Auth::user();
if(empty($user->id))
return Redirect::to('/');
//return if attendee doesn't belong to user
if( !($user->attendee->contains($id)) )
return Redirect::to('user/index')->with( 'error', 'attendee id error.' );
$attendee = Attendee::find($id);
return View::make('attendees.edit', compact('attendee'));
}
/**
* Update the specified attendee in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
$attendee = Attendee::findOrFail($id);
$validator = Validator::make($data = Input::all(), Attendee::$rules);
if ($validator->fails())
{
return Redirect::back()->withErrors($validator)->withInput()->with('id', $id);
}
$attendee->update($data);
return Redirect::intended();
}
user controller
public function getAttendees()
{
list($user,$redirect) = $this->user->checkAuthAndRedirect('user');
if($redirect){return $redirect;}
// Show the page
return View::make('site/user/attendees/index', compact('user'));
}
It's actually working correctly, it's just the usage that is incorrect.
Take the following:
You submit a form at GET /attendee/edit/{id}
The form takes you to POST /attendee/edit/{id}
You fail validation and are redirected to GET /attendee/edit/{id}
You cannot link to a page using a method other than GET. Your previous route was actually POST /attendee/edit/{id} but the link will always be GET.
Instead, nominate a page to be redirected to, I usually use the index for the section.
I think you should add a hidden input, like this
{!! Form::hidden('redirect_to', old('redirect_to', URL::previous())) !!}
and use this value in your link:
cancel
Redirect::back() doesn't really accomplish the right thing when using the validator provided by Laravel. What I've done before is pass the page to redirect back to as a hidden input in the form, and simply ignore it using Input::except("return"). Here's what I mean:
// On your HTML Form
<input type="hidden" name="return" value="{{ Request::path() }}" />
// In your PHP Controller
$validator = Validator::make($data = Input::except("return"), Attendee::$rules);
if ($validator->fails())
{
return Redirect::to(Input::get("return"))->withErrors($validator)->withInput()->with('id', $id);
}
Hope that can help shed some light. Other options are returning to a higher up, such as instead of attendee/edit/{id} redirect to attendee/edit/, but it all depends on the structure of you website.
I am now using the browser back via JS in onclick. Seems to work:
<a onclick="window.history.back();">Back</a>
I used this jquery for the cancel button.
$(document).ready(function(){
$('a.back').click(function(){
parent.history.back();
return false;
});
});
May you use the Front-End validation to test the input validation without a need to reload the page. So the {{URL::previous()}} won't affected.
The simple way to do that is by using Ajax or validator.js in your Laravel cade.
Here is a full example how to use jquery ajax in Laravel 5 to test the validation, may help:
http://itsolutionstuff.com/post/laravel-5-ajax-request-validation-exampleexample.html

newbie problems with codeigniter

i'm trying to learn codeigniter (following a book) but don't understand why the web page comes out empty.
my controller is
class Welcome extends Controller {
function Welcome()
{
parent::Controller();
}
function index()
{
$data['title'] = "Welcome to Claudia's Kids";
$data['navlist'] = $this->MCats->getCategoriesNav();
$data['mainf'] = $this->MProducts->getMainFeature();
$skip = $data['mainf']['id'];
$data['sidef'] = $this->MProducts->getRandomProducts(3, $skip);
$data['main'] = "home";
$this->load->vars($data);
$this->load->view('template');
}
the view is:
<--doctype declaration etc etc.. -->
</head>
<body>
<div id="wrapper">
<div id="header">
<?php $this->load->view('header');?>
</div>
<div id='nav'>
<?php $this->load->view('navigation');?>
</div>
<div id="main">
<?php $this->load->view($main);?>
</div>
<div id="footer">
<?php $this->load->view('footer');?>
</div>
</div>
</body>
</html>
Now I know the model is passing back the right variables, but the page appears completely blank. I would expect at least to see an error, or the basic html structure, but the page is just empty. Moreover, the controller doesn't work even if I modify it as follows:
function index()
{
echo "hello.";
}
What am I doing wrong?
Everything was working until I made some changes to the model - but even if I delete all those new changes, the page is still blank.. i'm really confused!
thanks,
P.
I've isolated the function that gives me problems.
here it is:
function getMainFeature()
{
$data = array();
$this->db->select("id, name, shortdesc, image");
$this->db->where("featured", "true");
$this->db->where("status", "active");
$this->db->orderby("rand()");
$this->db->limit(1);
$Q = $this->db->get("products");
if ($Q->num_rows() > 0)
{
foreach($Q->result_arry() as $row)
{
$data = array(
"id" => $row['id'],
"name" => $row['name'],
"shortdesc" => $row['shortdesc'],
"image" => $row['image']
);
}
}
$Q->free_result();
return $data;
}
I'm quite convinced there must be a syntax error somewhere - but still don't understand why it doesn't show any error, even if I've set up error_reporting E_ALL in the index function..
First port of call is to run php -l on the command line against your controller and all the models you changed and then reverted.
% php -l somefile.php
It's likely that there is a parse error in one of the files, and you have Display Errors set to Off in your php.ini. You should set Display Errors on for development and off for production, in case you haven't already.
(Edit: in the example above you have missed off the closing } of the class. It might be that.)
Make sure error_reporting in index.php is set to E_ALL and post your code for the model in question.
After looking through your function I suspect it's caused by $this->db->orderby("rand()");
For active record this should be $this->db->order_by('id', 'random');
Note that orderby is deprecated, you can still use it for now but the new function name is order_by
Not sure, but it can be also caused by php's "display_errors" is set to false.
You can change it in your php.ini file.

Resources