Geb: Web element locator for multi element navigator - xpath

I have a html page with an element called Student and it has multiple rows like below:
Student: Foo Bar
Class X
Section A
I was able to locate the Student element using XPath like this: //b[contains(text(),'Student')] but I want to extract the same thing using jquery selector and extract it's child elements like Foo Bar, Class X and Section A using jquery Selector
I tried something like below but it didn't work:
student(wait: true) { $('b', text: contains('Student:')).parent().parent()
Here is how the html looks:
<tr>
<td colspan="2" align="left" height="25" class="SubSectionTitle" style=""><b style=""> Student Information</b></td>
</tr>
<tr>
<td valign="top" class="Label" rowspan="3"><b style="">Student:</b></td>
<td style="">Foo Bar</td>
</tr>
<tr>
<td>Class X</td>
</tr>
<tr>
<td>Section A</td>
</tr>

Because your question is unclear, I am showing you several options:
HTML page src/test/resources/page-q66129162.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<table>
<tr>
<td colspan="2" align="left" height="25" class="SubSectionTitle" style=""><b style=""> Student Information</b>
</td>
</tr>
<tr>
<td valign="top" class="Label" rowspan="3"><b style="">Student:</b></td>
<td style="">Foo Bar</td>
</tr>
<tr>
<td>Class X</td>
</tr>
<tr>
<td>Section A</td>
</tr>
</table>
</body>
</html>
Geb test:
package de.scrum_master.stackoverflow.q66129162
import geb.spock.GebSpec
import org.openqa.selenium.By
class MultiElementNavigatorIT extends GebSpec {
static url = this.getResource("/page-q66129162.html").toString()
def test() {
given:
go url
when:
def xp = $(By.xpath("//b[contains(text(),'Student:')][1]"))
then:
xp.text() == "Student:"
xp.parent().text() == "Student:"
xp.parent().parent().text() == "Student: Foo Bar"
xp.parent().parent().parent().text() == " Student Information\nStudent: Foo Bar\nClass X\nSection A"
when:
def nav = $('b', text: contains('Student:'))
then:
nav.text() == "Student:"
nav.parent().text() == "Student:"
nav.parent().parent().text() == "Student: Foo Bar"
nav.parent().parent().parent().text() == " Student Information\nStudent: Foo Bar\nClass X\nSection A"
}
}
Or if you like it a bit more compact:
package de.scrum_master.stackoverflow.q66129162
import geb.spock.GebSpec
import org.openqa.selenium.By
import spock.lang.Unroll
class MultiElementNavigatorIT extends GebSpec {
static url = this.getResource("/page-q66129162.html").toString()
#Unroll
def "select by #selectorType"() {
given:
go url
def selector = selectorClosure()
expect:
selector.text() == "Student:"
selector.parent().text() == "Student:"
selector.parent().parent().text() == "Student: Foo Bar"
selector.parent().parent().parent().text() == " Student Information\nStudent: Foo Bar\nClass X\nSection A"
where:
selectorType | selectorClosure
"XPath" | { $(By.xpath("//b[contains(text(),'Student:')][1]")) }
"CSS" | { $('b', text: contains('Student:')) }
}
}
If your question was rather how to select from a multi element navigator, you can use something like myNavigator.first() or myNavigator[0].

Related

How to get only two element from html in same attribute?

i want parsing website using htmlagilitypack on aspx
below is my code
var html = #"http://test.com";
HtmlWeb web = new HtmlWeb();
var htmlDoc = web.Load(html);
var htmlNodes = htmlDoc.DocumentNode.SelectNodes("//table[#class='tableclass']//tr")
.Where(x => !x.Attributes["id"].Value.Contains("tableid"));
when this code is executed, all 'tr' from HTMLtable are returned.
below is One of returned HTML
<tr bgcolor="gray">
<td align="center" height="40">123</td>
<td align="center" width="56">
<div>
<img src="http://img.test.com/img.jpg" height="10" border="0" />
</div>
</td>
<td style="padding-left:3px;">THIS_1</td>
<td style="padding-left:3px;">THIS_2</td>
<td style="padding-left:3px;"><font color='red'>blah</font></td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
<td align="center">0</td>
</tr>
I Only want two td (THIS_1, THIS_2) InnerText
below is my wrong code
foreach (var node in htmlNodes)
{
var str1 = node.ChildNodes["td"].InnerHtml;
var str2 = node.SelectNodes(".//td[#style='padding-left:3px;']");
}
I want to Put [THIS_1 in str1] and [THIS_2 in str2].
Try get elements by index. For example:
foreach (var node in htmlNodes)
{
var str1 = node.SelectSingleNode("td[3]").InnerText; // THIS_1
var str2 = node.SelectSingleNode("td[4]").InnerText; // THIS_2
}

Nokogiri iterating over tr tags too many times

I'm scraping this page https://www.library.uq.edu.au/uqlsm/availablepcsembed.php?branch=Duhig and for each tr I am collecting and returning the level name and the number of computers available.
The problem is that it is being iterated over too many times. There are only 4 tr tags but the loop goes through 5 iterations. This causes an extra nil to be appended to the return array. Why is this?
Scraped Section:
<table class="chart">
<tr valign="middle">
<td class="left">Level 1</td>
<td class="middle"><div style="width:68%;"><strong>68%</strong></div></td>
<td class="right">23 Free of 34 PC's</td>
</tr>
<tr valign="middle">
<td class="left">Level 2</td>
<td class="middle"><div style="width:78%;"><strong>78%</strong></div></td>
<td class="right">83 Free of 107 PC's</td>
</tr>
<tr valign="middle">
<td class="left">Level 4</td>
<td class="middle"><div style="width:64%;"><strong>64%</strong></div></td>
<td class="right">9 Free of 14 PC's</td>
</tr>
<tr valign="middle">
<td class="left">Level 5</td>
<td class="middle"><div style="width:97%;"><strong>97%</strong></div></td>
<td class="right">28 Free of 29 PC's</td>
</tr>
</table>
Shortened Method:
def self.scrape_details_page(library_url)
details_page = Nokogiri::HTML(open(library_url))
library_name = details_page.css("h3")
details_page.css("table tr").collect do |level|
case level.css("a[href]").text.downcase
when "level 1"
name = level.css("a[href]").text
total_available = level.css(".right").text.split(" ")[0]
out_of_available = level.css(".right").text.split(" ")[3]
level = {name: name, total_available: total_available, out_of_available: out_of_available}
when "level 2"
name = level.css("a[href]").text
total_available = level.css(".right").text.split(" ")[0]
out_of_available = level.css(".right").text.split(" ")[3]
level = {name: name, total_available: total_available, out_of_available: out_of_available}
end
end
end
You can specify the class attribute of the table and then access the tr tags inside, this way you avoid the "additional" tr, like:
details_page.css("table.chart tr").map do |level|
...
And simplify a little bit the scrape_details_page method:
def scrape_details_page(library_url)
details_page = Nokogiri::HTML(open(library_url))
details_page.css('table.chart tr').map do |level|
right = level.css('.right').text.split
{ name: level.css('a[href]').text, total_available: right[0], out_of_available: right[3] }
end
end
p scrape_details_page('https://www.library.uq.edu.au/uqlsm/availablepcsembed.php?branch=Duhig')
# [{:name=>"Level 1", :total_available=>"22", :out_of_available=>"34"},
# {:name=>"Level 2", :total_available=>"98", :out_of_available=>"107"},
# {:name=>"Level 4", :total_available=>"12", :out_of_available=>"14"},
# {:name=>"Level 5", :total_available=>"26", :out_of_available=>"29"}]

Unable to check checkbox in table cell

I have this table:
<iframe title="ManageLegs">
<table title="Leg Details">
<tr>
<th class="headerRow">
<div>PAX</div>
</th>
<th class="headerRow">
<div>Leg</div>
</th>
</tr>
<tr>
<td>
<input type="text"></input>
</td>
<td>
<input type="checkbox"></input>
</td>
</tr>
</table>
</iframe>
Accessing the textbox is working fine, but I canĀ“t figure out how to check the checkbox.
I use the following code in my PageObject class:
in_iframe(:title => 'ManageLegs') do |frame|
table(:leg_details, title: 'Leg Details', :frame => frame)
text_field(:pax) { leg_details_element['1']['PAX'].text_field_element }
text_field(:aircraft) { leg_details_element['1']['Aircraft'].text_field_element }
text_field(:show_leg_on_quote_pdf) { leg_details_element['1']['Leg'].checkbox_element }
end
Which I am calling like this:
on(CharterInquirySavedPage).pax = "2"
on(CharterInquirySavedPage).check_show_leg_on_quote_pdf
It is working fine for setting the pax-textbox, but fails when trying to check the checkbox:
NoMethodError: undefined method `check_show_leg_on_quote_pdf' for #<CharterInquirySavedPage:0x00000004cfd420>
The text_field accessor is for text fields, which are input elements with type "text" and other text-like input fields such as "email".
Inputs of type "checkbox" are considered checkboxes. It is the checkbox accessor that creates the check_ method. In other words, you want the following definition for the checkbox:
checkbox(:show_leg_on_quote_pdf) { leg_details_element['1']['Leg'].checkbox_element }

Grails - sorting g:sortableColumn with hasMany relation in scaffolding

I have 3 domain classes:
class Material {
String sku
String description
String uom
String category
String type
double standardPrice
static hasMany = [prices: Price]
static constraints = {
sku(blank:false, nullable:false)
description(blank:false, nullable:false)
}
}
class Price {
double price
static belongsTo = [material: Material, priceList: PriceList]
static constraints = {
}
}
class PriceList {
String priceListName
Date validFrom
Date validTo
Boolean isValid
//static belongsTo = [supplier: Supplier]
static hasMany =[prices: Price]
static constraints = {
}
}
my PriceList update gsp is following:
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<meta name="layout" content="main"/>
<title>Update price list</title>
</head>
<body>
<g:form name="priceListlUpdateForm" id="${priceListInstance.id}">
<div class="buttons" role="navigation">
<g:actionSubmit action="save" class="edit" value="Update" />
<g:actionSubmit action="index" class = "cancel" value = "Cancel" />
<g:actionSubmit action="delete" class ="delete" value="Delete" />
</div>
<div class="body">
<h1>Update price list</h1>
</div>
<table>
<tbody>
<td>Price list name</td>
<td>
<g:textField name ="priceListName" value="${priceListInstance.priceListName}" />
</td>
</tr>
<tr>
<td>Valid From</td>
<td>
<g:datePicker name="validFrom" precision = 'day' value = "${priceListInstance.validFrom}" />
</td>
</tr>
<tr>
<td>Valid To</td>
<td>
<g:datePicker name="validTo" precision ='day' value = "${priceListInstance.validTo}" />
</td>
</tr>
</g:form>
</tbody>
</table>
<div class="constent scaffold-list" role=main>
<h1>Materials for price list</h1>
<table>
<thead>
<tr>
<g:sortableColumn property="sku" title="SKU" />
<g:sortableColumn property="description" title="Description" />
</tr>
</thead>
<tbody>
<g:each in="${pricesInPriceList}" status="i" var="pricesRow">
<tr>
<td>${pricesRow.material.sku}</td>
<td>${pricesRow.material.description}</td>
</tr>
</g:each>
</tbody>
</table>
</div>
</body>
</html>
update in PriceList Controller is following:
def update(long id) {
PriceList row=PriceList.get(id)
def pricesInPriceList = row.prices
[priceListInstance: row, pricesInPriceList: pricesInPriceList]
//render "this is update controller"
}
Everything is working fine except sorting.
When I click sortablecolumn on sku or description sorting is not working (rows are sorted randomly).
I stuck with this sorting. Thank you for help.
Regards,
To get the sorting work, you will need to implement a list method in your controller.. The list code will look like this:
def list(Integer max) {
params.max = Math.min(max ?: 10, 100)
[priceListInstanceList: PriceList.list(params), priceListInstanceTotal:
PriceList.count()]
}
Maybe it will be useful for somebody.
I have changed my update method in PriceList controller as follows:
def update(long id) {
PriceList row=PriceList.get(id)
//default sorting
def pricesInPriceList = row.prices.sort{it.material.sku}
if (params.sort && params.order == "asc") {
pricesInPriceList.asList().sort{it.material."${params.sort}"}
}
if (params.sort && params.order == "desc"){
pricesInPriceList.asList().sort{it.material."${params.sort}"}.reverse()
}
[priceListInstance: row, pricesInPriceList: pricesInPriceList]
}
now sorting with child works perfect.

Dynamic table which contains multiple submit forms

I am developing a shopping cart website in MVC3 with razor. Below code is view code, in which I am showing multiple products, each product has quantity text box and one submit button. So, in controller how would I know which submit button is called by user and how to read text from Label?
<table>
<tr>
#foreach (System.Data.DataRow i in Model.dt.Rows )
{
using (#Html.BeginForm("addtocart", "Chocolatier",FormMethod.Post,null))
{
<tr><td> <img src=" #Url.Content("~/Content/abc.gif") " alt="Imge" height ="40" width ="40"/></td></tr>
foreach (System.Data.DataColumn j in Model.dt.Columns)
{
lbl = "lbl";
lbl = lbl+#cnt.ToString();
if(j.ToString().ToLower ().Equals ("name"))
{
<tr> <td style="width : 200px"><h5>#j.ToString()</h5><label id="#lbl" >#i[j].ToString()</label></td></tr>
}
if(j.ToString().ToLower ().Equals ("description"))
{
<tr> <td style="width : 200px"><h5>#j.ToString()</h5>#i[j].ToString())</td></tr>
}
if(j.ToString().ToLower ().Equals ("price"))
{
<tr> <td style="width : 200px"><h5>#j.ToString()</h5>#Html.LabelFor(m=>m.prod_price,i[j].ToString())</td></tr>
}
}
cnt += 1;
<td>Enter quantity : #Html.TextBoxFor(m=>m.prod_quantity)</td>
<tr><td><input type ="submit" value="Add to cart" id="#cnt"/></td></tr>
<tr> <td>__________________________________________________________________________</td></tr>
}
}
</tr>
</table>

Resources