XPath to click 2nd link on page with this class - xpath

In an acceptance test I'm trying to click the .fa-trash-o icon for a given address. To simplify things, I've been trying to select the 2nd element on the page with the given class name.
I've tried several variations of the following with no luck.
$I->click(\Codeception\Util\Locator::find('a', ['class' => 'fa fa-trash-o']).'[2]');
$I->click('//a[contains(#class,"fa-trash-o")][2]');
Here's my template:
<div class="grid simple address-card">
<div class="grid-title no-border">
<h4>{{ $address->name }}</h4>
<div class="tools">
{!! Form::open(['url' => '/account/address/'.$address->id, 'method' => 'delete']) !!}
<a onclick="$(this).closest('form').submit();" class="fa fa-trash-o"></a>
{!! Form::close() !!}
</div>
</div>
<div class="grid-body no-border">
#include('partials.address')
</div>
</div>
How can I click that delete button?

You're tripping up on a common mistake related to the way the // operator is defined. The XPath expression
//a[contains(#class,"fa-trash-o")][2]
actually means
/descendant-or-self::node()/child::a[contains(#class,"fa-trash-o")][2]
The [2] applies to the child:: step rather than the descendant search, so it will select every a element that is the second fa-trash-o link under its respective parent. This finds nothing since there is only ever one such a within any given div. If you add parentheses:
(//a[contains(#class,"fa-trash-o")])[2]
then it will select the second fa-trash-o in the whole document - the [2] now applies to the whole result of the (...) expression rather than just the final step.

Related

Cypress: How to click on a array-list element if matching text found

I want to add an item in cart with a matching text like 'cashews'. I tried below code but .click() function is giving error as "bind and event handler to the click javascript event"
cy.get('.products').find('.product').each(($e1, index, $list) => {
const textveg = $e1.find('h4.product-name').text() {
if (textveg.includes('Cashews')) {
$e1.find('.button').click();
}
}
})
can someone suggest what can be the possible reason that .click() method is not identified by cypress. I am using cypress version 7
How you do this depends on the structure of the HTML.
It looks like you may have this sort of hierarchy
<div class="products">
<div class="product">
<h4 class="product-name">Almonds</h4>
<button>Add to cart</button>
</div>
<div class="product">
<h4 class="product-name">Cashews</h4>
<button>Add to cart</button>
</div>
</div>
Take the product section containing the text you want, and within that find the products <button>.
Your test might be
cy.contains('.product', 'Cashews') // pick the <div class="product"> with required text
.find('button') // inside the product, find it's button
.click()
You can use .filter() to find your element and click it:
cy.get('h4.product-name').filter(':contains("Cashews")').click()

Click on a element with specific text

Alright, I have a item which has this class class="country" and there are 12 elements with the same class. Now I want to get a element on its value. For example Italy. And now I want to click on a link in this item. The class of the link is class="link". So basically I want to click the link of the item with the name Italy
My code at the moment:
cy.get('.country').should('have.text', 'Italy').click();
HTML
<div class="countries">
<div class="text">
<h3></h3>
<div class="country">Italy</div>
<h4>Yala</h4>
<p>test</p>
<a class="link" href="/mysite">Show details</a>
</div>
</div>
Should() is an assertion and won't select the element you want.
you probably want the contains() function.
cy.get('.country').contains('Italy').click()
Best

Ordering laravel form::select by already selected values

Is it possible to order the form list by previously already selected values? these values are already selected when the page is loaded.
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Role:</strong>
{!! Form::select('roles[]', $roles, $userRole, array('class' => 'form-control form','multiple')) !!}
</div>
</div>
Outputs my list with the values in a non ordered fashion.
It is not possible to order the list because it is essentially an select list with options, which makes it impossible to order automatically based in value.
Your select is populated with data from $roles variable, I think that you should have ordered your list in that variable before render the template.

Laravel custom validation message for multiple fields with the same name

I have the following validation in the controller's action:
foreach ($request['qtys'] as $key => $val){
if (!$this->_validateMinQty($key, $job, $val)){
$customerTitle = $job->customers()->where('customer_id',$key)->first()->title;
return redirect()->back()->withErrors(['qtys' => __('The qty of the customer :customerTitle is less than allowed qty',['customerTitle' => $customerTitle])]);
}
}
This check multiple form's input named qtys in the view:
#foreach($job->customers as $customer)
<div class="form-group {{$errors->first('qtys has-error')}}">
{!! Form::label('qtys-'.$customer->id, __('Qty').' '.$customer->title) !!}
<div class="row">
<div class="col-md-9">
{!! Form::text('qtys['.$customer->id.']',$customer->pivot->e_production,['class' =>'form-control qtys', "data-sumequal"=>"qty",'required' => 'required','title' => $customer->pivot->aid,'id' => 'qtys-'.$customer->id]) !!}
<div class="help-block with-errors"></div>
#php ($eleE = $errors->first('qtys'))
#include('layouts.form-ele-error')
</div>
<div class="col-md-3">
<i class="fox-add"></i>{{__('Add Storage')}}
</div>
</div>
</div>
#endforeach
The above code works, but with the following limitation:
The error message is rendered under every input named qtys[x] where x is an integer and the first input only Testana has the invalid qty, like the following screen shot:
In the controller's action return message, I have tried to use indexed name for the input like the following:
return redirect()->back()->withErrors(['qtys.10' => ....
However, it prevents rendering the error message under any qtys field. Is there any solution?
The solution that I have found starts from the definition of first method found in the view :
#php ($eleE = $errors->first('qtys'))
This, in my code, should be changed to:
#php ($eleE = $errors->first('qtys.'.$customer->id))
Because the multiple fields have gotten keys equals to the customer id. This is a technique I usually use, when I want to send double piece of data in single post or in single form element.
Then in the controller, I keep the first try,
return redirect()->back()->withErrors(['qtys.'.$key => __('The qty of the customer :customerTitle is less than allowed qty',['customerTitle' => $customerTitle])]);
Where $key is an integer.

xPath strange behaviour - selecting ALL elements even if [1] set

today I stumbled upon a very interesting case (at least for me). I am messing around with Selenium and xPath and tried to get some elements, but got a strange behaviour:
<div class="resultcontainer">
<div class="info">
<div class="title">
<a>
some text
</a>
</div>
</div>
</div>
<div class="resultcontainer">
<div class="info">
<div class="title">
<a>
some other text
</a>
</div>
</div>
</div>
<div class="resultcontainer">
<div class="info">
<div class="title">
<a>
some even unrelated text
</a>
</div>
</div>
</div>
This is my data.
When i run the following xPath query:
//div[#class="title"][1]/a
I get as a result ALL instead of only the first one. But if I query:
//div[#class="resultcontainer"][1]/div[#class="info"]/div[#class="title"]/a
I get only the first , not all.
Is there some divine reason behind that?
Best regards,
bisko
I think you want
(//div[#class="title"])[1]/a
This:
//div[#class="title"][1]/a
selects all (<a> elements that are children of) <div> elements that have a #class of 'title', that are the first children of their parents (in this context). Which means: it selects all of them.
The working XPath selects all <div> elements that have a #class of 'title' - and of those it takes the first one.
The predicates (the expressions in square brackets []) are applied to each element that matched the preceding location step (i.e. "//div") individually. To apply a predicate to a filtered set of nodes, you need to make the grouping clear with parentheses.
Consequently, this:
//div[1][#class="title"]/a
would select all <div> elements, take the first one, and then filter it down futher by checking the #class value. Also not what you want. ;-)

Resources