Minitest/Capybara Dropdown Menu Locator Problem - ruby

I tried to choose four dropdown menu respectively like this:
find(:xpath, '//select[#id = 'homework_filter_program_id']/option[1]').click_on
find(:xpath, '//select[#id = 'homework_filter_lesson_id']/option[1]').select_option
find(:xpath, '//select[#id = 'homework_filter_unit_id']/option[1]').select_option
find(:xpath, '//select[#id = 'homework_filter_difficulty_level_id']/option[1]').select_option
I tried both click_on and select_option to choose.
I derived the locators from this segment.
enter image description here
But I get these errors:
in block in require
in load_dependency
in <top (required)>
What is the wrong thing in my XPath, it doesn't select. I am newbie in Minitest and Ruby.
I am also open suggestions regarding CSS selectors for dropdown menu.

As Paul points out your quotes are invalid, but beyond that there's no need for XPath here. CSS is easier to read and is generally more efficient for locating elements when it can be used. Something along the lines of
find('select#homework_filter_lesson_id option:first-child').select_option
should work (assuming the select is actually visible on the page). Even easier yet would be to just do
select('text of the first option', from: 'homework_filter_lesson_id')

It seems there's an issue with building the xpath locator string (which you're passing as a second argument to find).
'//select[#id = 'homework_filter_program_id']/option[1]'
This isn't valid ruby code.
What you're looking for is string interpolation. There are two common ways of doing that:
"//select[#id = #{homework_filter_program_id}]/option[1]"
Notice the #{} expression inside the string. The result of the expression is interpolated. Keep in mind that this only works with doubly quoted strings ("like this one")
Or use the string formatting:
'//select[#id = %s]/option[1]' % homework_filter_program_id
Notice the %s placeholder. This works very much like sprintf.
Upd: for some reason as I've read your example I figured you had the local variables called homework_filter_program_id etc. Now that I've also looked at the screenshot I realized that homework_filter_program_id is actually the id of an element you want to find.
Your code will most likely work once you drop the unnecessary quotation marks:
'//select[#id=homework_filter_program_id]/option[1]'
But you probably still want to parameterize this locator, and that's when the interpolation advice will come handy.

Related

Capybara / Ruby - Trying to get only the Text from all ambiguous css selector and convert it to string

I'm trying to get all Texts from a specific CSS Selector that are ambiguous in the HTML. I would like to access these ambiguous css and get the Text and then return all that info.
I've figured out how to find all ambiguous selectors but I dont know how to get just the text from each selector.
The ambiguous selector is (it finds 3 matchers)
.list-card-title .js-card-name
I've already tried commands like:
arr = Array(3)
arr = find_all('.list-card-title.js-card-name').to_a
puts arr.to_s
When I use puts arr
I got the following output
[#<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[1]/DIV[3]/SPAN[1]">, #<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[2]/DIV[3]/SPAN[1]">, #<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[3]/DIV[3]/SPAN[1]">]
To get the text of elements you need to call text on each of the elements. In your case the easiest way to do that would be
find_all('.list-card-title.js-card-name').map(&:text)
which will return an array of the text contained in each of the elements. If you then want all of that concatenated into one string you could do
find_all('.list-card-title.js-card-name').map(&:text).join
Note: you have tagged your questions with automated-tests, are you actually testing an app/site, or are you instead doing web scraping? If you are testing you'd be much better off writing your tests using Capybaras expectation/assertion methods (and the :text options they accept) rather than finding elements, extracting/manipulating contained text and then doing something (I assume asserting on) with that.

Xpath to find an element using partial class name and exact text with capybara in ruby

I have html code with div having same matching text in class name as menu1 and text like
Berlin
and
Berlin Germany
for which when i use below code returns ambiguous elements
find(:xpath, "//div[contains(text(), \"Berlin\") and contains(#class, \"menu1\")]")
Note: I want both class and text to be in my xpath
Suggestions will be appreciated, thanks in advance.
If by partial class name you mean something like <div class="blah menu1 other">Berlin</div> then you could just do it in a readable way with something like
find('div.menu1', exact_text: 'Berlin')
or
find('div.menu1', text: 'Berlin', exact: true)
If it's more like <div class="blah menu1_part other">Berlin</div> you can still do it with a more readable CSS selector like
find('div[class*=menu1]', exact_text: 'Berlin')
If you actually need to do it all in one XPath for performance reasons (a LOT of div.menu1 elements on the page, where you can't scope to a limited section of the page for some crazy reason) then you could do something like
find(:xpath, './/div[text()="Berlin"][contains(#class, "menu1")]')
Note the leading . in the XPath expression. 99.9% of the time when using Capybara, and manually writing your own XPath expressions, you want to start your XPath expressions with .//, otherwise you are defeating any scoping you have done - see https://github.com/teamcapybara/capybara#beware-the-xpath--trap
Another option is to use the xpath gem Capybara uses internally for generating XPaths, which would be something like
find(:xpath, XPath.css('div.menu1')[XPath.string.n.is('Berlin')], exact: true)
or
find(:xpath, XPath.css('div[class *= "menu1"]')[XPath.string.n.is('Berlin')], exact: true)
depending on exactly what you mean by partial class name. The benefit of doing something like that is the meaning of the is method can be changed from contains to equals depending on the value of the exact option, and it also handles all the normalizing and escaping of strings as necessary if your strings weren't as simple as 'Berlin'

How does Ruby webdriver get element with hyphen in <name, value> pair

I have HTML like below and I want to get the element by "sku-code" (there is a hyphen in it)
<div class="leavemessagebox" style="position: relative;" sku-code="m_showcase">
When I used
browser.div(:sku-code=>'m_showcase')
Ruby reported an error
ERROR:undefined local variable or method `code' for #<AUT::WebClient:0x2c59650>
It sames Ruby can't recognize "sku-code" as a name, anyone can give me any suggestion about how to get the element by "sku-code"?
Sorry for not explain myself clearly. There are many elements that have sku-code selector and I want to collect them all in a list, so the class name and tag name isn't stable. How can I do that
Looks like Watir WebDriver. Try use css (preferred) or xpath.
browser.element(:css => "[sku-code='m_showcase']") # single one
browser.elements(:css => "[sku-code='m_showcase']") # a list of all matches
Documention on CSS's attribute selector is here.
So basically the above selector finds all elements with attribute sku-code equals to m_showcase.
:sku-code is not a valid symbol literal. It is however valid Ruby code, it is parsed as:
:sku - code
So, you are trying to call a method named code and subtracting its return value from the symbol :sku, which obviously doesn't make sense.
But you can quote the symbol literal, of course:
:'sku-code'
browser.element(:css => ".leavemessagebox[sku-code='m_showcase']") I think this will still the specific CSS.
Please Let me know is the above CSS is working or Not.

How to find out if a string includes a word stored in a variable

I am testing browser data via cucumber. I am looking through a list of links on a page, to determine which link I should click.
So let's say I have a list of dessert links and I wanted to find the one for apple sauce. I call a method that goes through the links and finds the one that has apple sauce after I pass the name apple sauce to it.
The string "apple sauce" is stored in #dessert and since #dessert will change often, I need to know if there is a way to find out if say "apple sauce" is stored in the variable #dessert.
when I do #dessert.text.include? "##dessert" i keep getting false. I need this to be true in order to make the decision to click it. When I evaluate #dessert by itself I have "apple sauce"...
How can I get rid of the quotes ( " ) so they are not evaluated? I think this is messing me up!
The standard way to test if a string has another string in it is to either use a regular expression, where you can describe patterns for "close enough" matches, or to search for a literal substring:
#dessert = "apple sauce;pears;walnuts"
#dessert.include?("apple sauce")
# => true
I'm not suer why your question has "##dessert" since that evaluates to a string exactly like that. "#{#dessert}" is probably what you were intending, where #{...} inlines a string value, but that's redundant since you're only testing against a singular variable with no other data. x and "#{x}" evaluate to equivalent strings if x is a string to begin with.
You seem to be comparing #dessert, an object with #dessert.text, an attribute of that object. Also, you are using a form of string interpolation that I, along with others here, would recommend avoiding.
Try
#dessert.text.include? "#{#dessert.text}"
and see if it returns true.
Here's an example where I went into a container and pulled all the links out to see click, be navigated to the page, and then assert that the page titles, url, and breadcrumbs all included the text within the link within the container. You could just add an if statement that will evaluate whether a link has certain text, and if it does, click it.
From lib file
def pull_text_and_href_from_continent_link
#continents.each do |continent|
find(".continents li.#{continent}").hover
page.should have_selector(".#{continent} .continent-wrapper", :visible => true)
page.all(:css, '.continents .continent-wrapper a').each do |nav_link|
#array.push(:text => nav_link.native.attribute("text"), :href => nav_link[:href])
end
end
end
From spec file (this is a suite not using cuke, but the code is still similar)
it "should have the right page title, url, and breadcrumbs" do
#array.each do |info|
if info[:text].include?('See all')
pp "Skipping the see all link for this container"
else
visit_page_logged_out(info[:href])
assert_correct_page_url info[:href]
assert_country_breadcrumbs remove_specials(info[:text])
assert_page_title info[:text]
end
end

using xpath in selenium.get.Text and selenium.click

I have Адреса магазинов on page and want to store text, then click on this link and verify that the page where am I going to contains this text in headers. So I tried to find element by xpath, and selenium.getText get the right result, but selenium.click goes to another link. Where have I made a mistake? Thanks in advance!
String m_1 = selenium.getText("xpath=html/body/div[3]/div[2]/div[1]/h4[1]");
selenium.click("xpath=html/body/div[3]/div[2]/div[1]/h4[1]");
selenium.waitForPageToLoad("30000");
assertTrue(selenium.getText("css=h3").contains(m_1));
page:http://www.svyaznoy.ru/map/
Resume:
using xpath=//descendant::a[#href='/address_shops/'][2] or css=div.deff_one_column a[href='/address_shops/'] get right results
using xpath=//a[#href='/address_shops/'] - Element is not currently visible
xpath=//a[#href='/address_shops/'][2] - Element not found
There is a missing slash at the beginning of the expression. I am kind of surprised this got through at all - the first slash means "begin at root node".
Also, it is better to select the <a> element instead of the <h>. Sometimes it works, sometimes is misclicks, sometimes the click doesn't do anything at all. Try to be as concrete as you can be.
Try this one.
String m1 = selenium.getText("xpath=/html/body/div[3]/div[2]/div/h4/a");
selenium.click("xpath=/html/body/div[3]/div[2]/div/h4/a");
selenium.waitForPageToLoad("30000");
// your variable is named m1, but m_1 was used here
assertTrue(selenium.getText("css=h3").contains(m1));
By the way, there are even better XPath expressions you could use. See the documentation, it really is helpful. Just an example, this would work, too, and is much easier to write and read:
String m1 = selenium.getText("xpath=//a[#href='/address_shops/']");
selenium.click("xpath=//a[#href='/address_shops/']");
Sorry, didn't notice page link. Css for second link can be something like that css=div.deff_one_column a[href='/address_shops/']

Resources