I am trying to select td.col_4 within the following HTML structure using Capybara, but to no avail so far:
<div id="potentialResults">
<div class="result">
<table class="report">
<tbody>
<tr>
<td class="col_1">
<img original-title="Sources : Telephone Directory">
<u>Name 1</u>
</td>
<td class="col_2"></td>
<td class="col_3"></td>
<td class="col_4">Address 1</td>
</tr>
</tbody>
</table>
</div>
<div class="result">
<table class="report">
<tbody>
<tr>
<td class="col_1">
<img original-title="Sources : Telephone Directory">
<u>Name 2</u>
</td>
<td class="col_2"></td>
<td class="col_3"></td>
<td class="col_4">Address 2</td>
</tr>
</tbody>
</table>
</div>
</div>
So at the moment to get the text address 1 for Name 1 I do this
page.find("#potentialResults > .result > .report > tbody > tr > td.col_1 > a", text: "Name 1", match: :first).find('td.col_4').text
But where I seem to be struggling is getting the same address but using the img data-attribute as my identifier
page.find("#potentialResults > .result > .report > tbody > tr > td.col_1 > img[original-title='Sources : Telephone Directory'] + td.col_4", match: :first).text
But td.col_4 isn't exactly adjacent in this example is it?
How else would I be able to get the text when stipulating that it has to be the first match?
The css you're passing to your finders is way too lengthy. It can be shortened without running into ambiguous results.
If you want to just simply get text from td.col_4, just do:
find('#potentialResults .col_4').text
If you want the address for 'Name 1', you need to first find that element, traverse up to the parent by a few levels, then find your way back down. This is because the elements you are looking for are siblings, and Capybara doesn't really provide an easy way to find these:
find('#potentialResults .col_1 a', :text => 'Name 1').find(:xpath, '../../..').find('.col_4').text
Related
So I have a weird situation were I have a table footer that I only need to repeat once so I decided to just add the "footer" trs to the end of the tbody so it would only appear once at the end of the table and look like a footer without the repeating on each printed page.
<table>
<tbody>
#foreach(var item in Model.Items)
{
<tr><td>#item.Name</td></tr>
<tr><td>#item.Total</td></tr>
}
<tr>
<td>Total</td>
<td>#Model.Items.Sum(x => x.Total)</td>
</tr>
<tr>
<td>Discount</td>
<td>#(Model.Items.Sum(x => x.Total) / 50)</td>
</tr>
</tbody>
</table>
This was fine until Model.Items had enough rows that it pushed the second row of the footer onto the next page when I need to keep the two footer rows together.
I've also tried using the actual tfoot tag like so with a page-break-inside:avoid style.
<table>
<tbody>
#foreach (var item in Model.Items)
{
<tr><td>#item.Name</td></tr>
<tr><td>#item.Total</td></tr>
}
</tbody>
<tfoot style="page-break-inside:avoid;">
<tr>
<td>Total</td>
<td>#Model.Items.Sum(x => x.Total)</td>
</tr>
<tr>
<td>Discount</td>
<td>#(Model.Items.Sum(x => x.Total) / 50)</td>
</tr>
</tfoot>
</table>
This kept the footer together but its now repeating on each page instead of once. Someone on another Stack Overflow question suggested using the following style on the tfoot to stop it repeating which worked, but now it ignores the page-break-inside:avoid style.
<tfoot style="display: table-row-group"></tfoot>
Does anyone know how to get a footer to appear once and avoid breaking over multiple printed pages?
I got it working but I had to apply a few styles to make it looks like one table. I put the original footer HTML into a tr > td > table like so:
<table>
<tbody>
#foreach (var item in Model.Items)
{
<tr><td>#item.Name</td></tr>
<tr><td>#item.Total</td></tr>
}
<tr style="page-break-inside:avoid;">
<td colspan="2">
<table>
<tr>
<td>Total</td>
<td>#Model.Items.Sum(x => x.Total)</td>
</tr>
<tr>
<td>Discount</td>
<td>#(Model.Items.Sum(x => x.Total) / 50)</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
This allowed the footer to stay together while only appearing once at the end of the page.
I want display cricket team details. First row of a table is the captains, so for every first row color, I want to make the font size to be green and bold
This is My HTML:
<div >
<table id="sports" border=1 align=center >
<thead >
<tr>
<th>Team1</th>
<th>Team2</th>
</tr>
</thead>
<tbody *ngFor="let T of Teams" >
<td> {{T.Indian_players}}</td>
<td>{{T.Australia_players}}</td>
</tr>
</tbody>
</table>
</div>
This is my model, where I have taken static data:
export const Players=[
{Indian_players:'Kohili(c)',Australia_players:'David Warner(c)',Pakistan_players:'shaheen Afridi(c)',Southafrica_players:'dale steyn(c)',England_players:'Harry Kane(c)'},
{Indian_players:' Dhoni',Australia_players:'Steve Smith',Pakistan_players:'sarfraz Ahmed',Southafrica_players:'du plessis',England_players:'Joe Root'},
{Indian_players:'Rohit Sharma',Australia_players:'Glen Maxwell',Pakistan_players:'Babar Azam',Southafrica_players:'Imran Tahir',England_players:'Alex Hales'},
{Indian_players:'Jadeja',Australia_players:'Aron Finch',Pakistan_players:'Mohamad Hafeez',Southafrica_players:'David Miller',England_players:'James Anderson'},
{Indian_players:'K.L.Rahul',Australia_players:'Mitchel Starc',Pakistan_players:'Imad Wasim',Southafrica_players:'Jp duminy',England_players:'Moeen Ali'},
{Indian_players:'Bhuvaneswar Kumar',Australia_players:'Travis Head',Pakistan_players:'Shadab khan',Southafrica_players:'de kock',England_players:'Jos Buttler'},
{Indian_players:'Shikar Dhawan',Australia_players:'Pat cummins',Pakistan_players:'yasir shah',Southafrica_players:'Hashim Amla',England_players:'Ben Strokes'},
{Indian_players:'RishabPanth',Australia_players:'Mitchel Marsh',Pakistan_players:'Imam-ul-haq',Southafrica_players:'chris morris',England_players:'Sam Billings'},
{Indian_players:'Ashwin',Australia_players:'Peter siddle',Pakistan_players:'Faheem Ashraf',Southafrica_players:'Aiden markram',England_players:'Eoin Morgan'},
{Indian_players:'Dinesh Karthik',Australia_players:'Tim Paine',Pakistan_players:'Shoib Malik',Southafrica_players:'Dean Elgar',England_players:'chris Woakes'},
You can do the following.
<tbody>
<tr *ngFor="let T of Teams; let i = index" [class.your-captain-css-class]="index===0">
<td> {{T.Indian_players}}</td>
<td>{{T.Australia_players}}</td>
</tr>
</tbody>
I've done the following.
getting the index along with object.
Adding class if index is 0.
This is cover your requirement. There is another way of doing it only by CSS.
You can do it with CSS by doing this
custom-class:first-child {
color: yellow;
font-size: 15px;
}
Also, you have an error in your sample an ending tr tag but no beginning tr tag inside the body
<tbody *ngFor="let T of Teams" >
<td> {{T.Indian_players}}</td>
<td>{{T.Australia_players}}</td>
</tr>
</tbody>
I believe after reading the comments you could/should do this instead and use the CSS at the top and it should all work the reason I suggest using the class instead of tr is because if you are using tr you have used tr more then once and it will affect all tr tags instead of just the one you want in the body.
<tbody>
<tr *ngFor="let T of Teams" class="custom-class">
<td> {{T.Indian_players}}</td>
<td>{{T.Australia_players}}</td>
</tr>
</tbody>
I have a complex html structure with lot of tables and divs.. and also the structure might change. How to find xpath by skipping the elements in between.
for example :
<table>
<tr>
<td>
<span>First Name</span>
</td>
<td>
<div>
<table>
<tbody>
<tr>
<td>
<div>
<table>
<tbody>
<tr>
<td>
<img src="1401-2ATd8" alt="" align="middle">
</td>
<td><span><input atabindex="2" id=
"MainLimitLimit" type="text"></span></td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
I have to get the input element with respect to the "First Name" span
eg :
By.xpath("//span[contains(text(), 'First Name')]/../../td[2]/div/table/tbody/tr/td/table/tbody/tr/td[2]/input")
but.. can I skip the between htmls and directly access the input element.. something like?
By.xpath("//span[contains(text(), 'First Name')]/../../td[2]//input[contains#id,'MainLimitLimit')]")
You can try this Xpath :
//td[contains(span,'First Name')]/following-sibling::td[1]//input[contains(#id, 'MainLimitLimit')]
Explanation :
select <td><span>First Name</span></td> element :
//td[contains(span,'First Name')]
then get <td> element next to above <td> element :
/following-sibling::td[1]
then get <input> element within <td> element selected in the 2nd step above :
//input[contains(#id, 'MainLimitLimit')]
You can use // which means at any level
By.xpath("//span[contains(text(), 'First Name')]//td[2]/input[contains#id,'MainLimitLimit')]")
you can use the "First Name" span as a predicate. Try the code below
//td[preceding-sibling::td[span[contains(text(), 'First Name')]]]//input[contains(#id,'MainLimitLimit')]
I have a complex html structure with lot of tables and divs.. and also the structure might change. How to find xpath by skipping the elements in between.
for example :
<table>
<tr>
<td>
<span>First Name</span>
</td>
<td>
<div>
<table>
<tbody>
<tr>
<td>
<div>
<table>
<tbody>
<tr>
<td>
<img src="1401-2ATd8" alt="" align="middle">
</td>
<td><span><input atabindex="2" id=
"MainLimitLimit" type="text"></span></td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
I have to get the input element with respect to the "First Name" span
eg :
By.xpath("//span[contains(text(), 'First Name')]/../../td[2]/div/table/tbody/tr/td/table/tbody/tr/td[2]/input")
but.. can I skip the between htmls and directly access the input element.. something like?
By.xpath("//span[contains(text(), 'First Name')]/../../td[2]//input[contains#id,'MainLimitLimit')]")
You can try this Xpath :
//td[contains(span,'First Name')]/following-sibling::td[1]//input[contains(#id, 'MainLimitLimit')]
Explanation :
select <td><span>First Name</span></td> element :
//td[contains(span,'First Name')]
then get <td> element next to above <td> element :
/following-sibling::td[1]
then get <input> element within <td> element selected in the 2nd step above :
//input[contains(#id, 'MainLimitLimit')]
You can use // which means at any level
By.xpath("//span[contains(text(), 'First Name')]//td[2]/input[contains#id,'MainLimitLimit')]")
you can use the "First Name" span as a predicate. Try the code below
//td[preceding-sibling::td[span[contains(text(), 'First Name')]]]//input[contains(#id,'MainLimitLimit')]
I need to repeat an html snippet several times on a page but the problem is that the contained html elements have id defined that should be unique on page. So what is the proper way I could repeat this snippet without removing the id attribute from the elements ?
Could I use iframe as container for this snippet & let the duplicate ids exist on page?
You can use JavaScript to clone the snippet and at the same time change the IDs.
Example if your snippet is:
<div id="snippet">
<table id="list">
<tr id="0">
<td id="firstName0">John</td>
<td id="lastName0">Smith</td>
</tr>
<tr id="1">
<td id="firstName2">John</td>
<td id="lastName2">Doe</td>
</tr>
</table>
</div>
Using
$("#snippet").clone(false).find("*[id]").andSelf().each(function() { $(this).attr("id", $(this).attr("id") + "_1"); });
Will Produce
<div id="snippet_1">
<table id="list_1">
<tr id="0_1">
<td id="firstName0_1">John</td>
<td id="lastName0_1">Smith</td>
</tr>
<tr id="1_1">
<td id="firstName2_1">John</td>
<td id="lastName2_1">Doe</td>
</tr>
</table>
</div>
Which will solve the duplicate ID problem.