How do I interact with a file_field thats hidden by its parent?
<span class="btn button-large fileinput-button">
Select files...
<input accept="image/png, image/gif, image/jpeg" id="gallery_files" multiple="multiple" name="gallery_files" type="file">
</span>
The button overlays the input, therefore it's not visible.
Edit
For the record, here's my code:
data[:photos].each do |photo|
$browser.file_field.set photo
end
and the error: Watir::Wait::TimeoutError: timed out after 20 seconds, waiting for {:tag_name=>"input", :type=>"file"} to become present
Workable example in a Gist
I was a bit suprised, but I was able to set the file field in the sample HTML without any issue using:
browser.file_field.set('path/to/file.txt')
From the code, it looks like setting the file field only requires the input to exist. It does not require it to be visible (or present).
Given that you are getting a Watir::Wait::TimeoutError exception, I would guess that your code is actually failing before the file_field.set. As it looks like the page has the input in a dialog, I am guessing your code actually looks more like:
$browser.file_field.wait_until_present
data[:photos].each do |photo|
$browser.file_field.set photo
end
It would be the wait_until_present method that is actually throwing the exception.
Solution 1
Assuming that an explicit wait method is being called for the file field, you should be able to just remove the wait.
If you have the wait because the dialog is being loaded by Ajax, you could try waiting for a different element instead - eg the parent span.
Solution 2
If for some reason you need the file field to be present, you will need to change its CSS. In this case, the opacity:
p $browser.file_field.present?
#=> false
$browser.execute_script('arguments[0].style.opacity = "1.0";', browser.file_field)
p $browser.file_field.present?
#=> true
For my situation, this worked:
$browser.execute_script("jQuery(function($) {
$('.fileinput-button').css('visibility', 'hidden')
$('#gallery_files').css('visibility', 'visible').css('opacity', '1').css('width', '100').css('height', '50')
})")
I had to hide the parent span, then show, resize, and change the opacity of the input
Related
I need to click this input only if it has "aria-checked ="true"
<input class="mat-checkbox-input cdk-visually-hidden" type="checkbox" id="mat-checkbox-131-input" tabindex="0" aria-checked="true" style="" xpath="1">
Ruby:
aria_checked = true
if aria_checked = true
impressora_etiqueta = "//mat-checkbox[#id='mat-checkbox-23']/label/div"
page.find(:xpath, impressora_etiqueta).click
end
There are many ways to do what you want - simplest is probably
page.first('#mat-checkbox-23[aria-checked="true"]', minimum: 0)&.click
which will look for the first element with the given id and aria-checked = "true" and click it if one exists.
Note: the id in your test and sample HTML didn't match so I went the id from your test, adjust as needed. Also you have a class of cdk-visually-hidden shown -- If that's actually making the element not visible on the page, then this won't work and you'll need to add more surrounding HTML to your question with a better description of exactly what you're trying to do (you can't click on non-visible elements)
I have a button
<button type="button" id="saveListing" class="button small save-button" data-bind="enable: saveEnabled, click: save"><i class="fa fa-save"></i> Save</button>
located in the tr of a table.
I wrote a function for testing the button status, simply using:
And(/^...the button "(.*?)" on "(.*?)" page is disabled$/) do |button_name, page|
button_id = ConfigHelper.get_button_info(button_name, page)['id']
button_class = ConfigHelper.get_button_info(button_name, page)['class']
if !button_id.nil?
find_button(button_id)[:disabled].should eq 'true'
elsif !button_class.nil?
find(button_class)[:disabled].should eq 'true'
else
button_text = ConfigHelper.get_button_info(button_name, page)['text']
find('button', text: button_text)[:disabled].should eq "true"
end
end
However, this block does not work for a button in a table row. I also tried add checking by button id, and it also did not work. How can I implement it without taking table id as a parameter? (As I don't want to write table id inside the feature)
When using id, the error is:
Capybara::ElementNotFound: Unable to find css ".saveListing"
or using text:
Ambiguous match, found 4 elements matching css "button" (Capybara::Ambiguous)
Thanks.
Capybaras find_button doesn't search css classes at all so unless you have overwritten find_button I'm not sure why you would be getting that error from using it with an id. find_button will search by the id, value, text content, or title attribute of the button, and also supports a disabled filter for searching. More stable (if the status of the button is changing due to JS) versions of those checks would be
find_button('saveListing', disabled: true).should be #Note: no # in front of the id here since its not a css selector
find_button('button text', disabled: true).should be
These would be more stable because they will utilize Capybaras waiting behavior to find the disabled button, whereas the way they were written previously would immediately find the button and error if they weren't yet disabled.
saveListing is the id of your button, not a class. In css selectors, dot is used for classes and hash sign is used for ids.
Therefore, you should either be using #saveListing or .save-button, but not .saveListing. This is why your first matching fails.
As to why the second one does - I guess there are 4 rows, each with one button and Capybara doesn't know which one you are referring to. If you want to check this condition for all of them, you could use all instead of find like this:
all('button', text: button_text).each do |button|
button[:disabled].should eq "true"
end
I have a watir-webserver Ruby script I am developing, and I am having it first prompt the user for a username and password (with Ruby-Tk).
In any case, I can open the page with this code:
b = Watir::Browser.start 'https://connect.mypage.com/Home'
After that, I call the following:
t1=b.text_field(:id => 'ctl00_cphBody_txtUserName').when_present.set #entry1.textvariable
Nothing is filled in.
I have tried hard coding a name in for ctl00_cphBody_txtUsername, but that did not work either.
Using the Inspector in Firefox, the field is constructed as following:
<input name="ctl00$cphBody$txtUsername" id="ctl00_cphBody_txtUsername" class="firstFocus" maxlength="50" type="text">
The class of firstFocus is:
<script type="text/javascript">
$(document).ready(function () {
jQuery('.firstFocus').focus();
});
</script>
The problem is that the locator is case-sensitive.
Notice that the id attribute value is "ctl00_cphBody_txtUsername" but the locator being used is "ctl00_cphBody_txtUserName". Due to the "N" not matching the "n", the element is never found, which is why the when_present times out.
Correcting the id in the locator will fix the issue:
t1=b.text_field(:id => 'ctl00_cphBody_txtUsername').when_present.set #entry1.textvariable
If I start the browser and open the page like below, I have no problems locating those two text_field's:
browser = Watir::Browser.new
browser.goto 'https://connect.mypage.com/Home'
#Then, the first item actually has a class, so I use that
t1=browser.text_field :class => 'firstFocus'
t1.set #entry1.textvariable
The context is I'm using watir-webdriver and I need to locate if an image appears prior to a particular item in a list.
More specifically, there is a section of the site that has articles uploaded to them. Those articles appear in a list. The structure looks like this:
<div id="article-resources"
<ul class="components">
...
<li>
<div class="component">
<img src="some/path/article.png">
<div class="replies">
<label>Replies</label>
</div>
<div class="subject">
Saving the Day
</div>
</div>
</li>
...
</ul>
</div>
Each article appears as a separate li item. (The ellipses above are just meant to indicate I can have lots of liste items.)
What I want our automation to do is find out if the article has been appropriately given the image article.png. The trick is I need to make sure the actual article -- in the above case, "Saving the Day" -- has the image next to it. I can't just check for the image because there will be multiples.
So I figured I had to use xpath to solve this. Using Firefox to help look at the xpath gave me this:
id("article-resources")/x:ul/x:li[2]/x:div/x:img
That does me no good, though, because the key discriminator seems to be the li[2], but I can't count on this article always being the second in the list.
So I tried this:
article_image = '//div[#class="component"]/a[contains(.,"Saving the Day")]/../img'
#browser.image(:xpath => article_image).exist?.should be_true
The output I get is:
expected: true value
got: false (RSpec::Expectations::ExpectationNotMetError)
So it's not finding the image which likely means I'm doing something wrong since I'm certain the test is on the correct page.
My thinking was I could use the above to get any link (a) tags in the div area referenced as class "component". Check if the link has the text and then "back up" one level to see if an image is there.
I'm not even checking the exact image, which I probably should be. I'm just checking if there's an image at all.
So I guess my questions are:
What am I doing wrong with my XPath?
Is this even the best way to solve this problem?
Using Watir
There are a couple of approaches possible.
One way would be find the link, go up to the component div and then check for the image:
browser.link(:text => 'Saving the Day').parent.parent.image.present?
or
browser.div(:class => 'subject', :text => 'Saving the Day').parent.image.present?
Another approach, which is a little more robust to changes, is to find the component div that contains the link:
browser.divs(:class => 'component').find { |component|
component.div(:class => 'subject', :text => 'Saving the Day').exists?
}.image.present?
Using XPath
The above could of course be done through xpath as well.
Here is your corrected xpath:
article_image = '//div[#class="component"]//a[contains(.,"Saving the Day")]/../../img'
puts browser.image(:xpath => article_image).present?
Or alternatively:
article_image = '//a[contains(.,"Saving the Day")]/../../img'
browser.image(:xpath => article_image).present?
Again, there is also the top down approach:
article_image = '//div[#class="component"][//a[contains(.,"Saving the Day")]]/img'
browser.image(:xpath => article_image).present?
You can read more about these approaches and other options in the book Watirways.
The issue I'm dealing with, and unable to solve due to my ignorance, is that I have a page on a browser with different education requirements which each have four radio buttons. I want to be able to select a certain radio button for each education requirement.
The code I have is like this:
radios = browser.radios
radios.each do |radio|
radio.input(:value => "very").set
end
However this keep giving me an error message saying: "undefined method 'set' for #Watir::Input:0x103a5d508"
I did something similar for select_lists where I changed the option of all select_lists on a page to the 2nd option which worked:
lists = browser.select_lists
lists.each do |list|
list.option(:index, 1).select
end
For my code for the radio buttons I tried using radio.option but it gave me a similar error: "NoMethodError: undefined method `set' for #Watir::Option:0x103a466a0"
Problem
The code
radios = browser.radios
radios.each do |radio|
radio.input(:value => "very").set
end
Says that for each radio button on the page, set the first input element with value "very". This suggests that you are looking for html like:
<radio>
<input value="very" />
</radio>
This is probably not what the html looks like.
Solution
I assume the html you really want to set is like:
<input type="radio" value="very" />
To set each radio button with value "very", the code should be:
# Get a collection of all radios with value "very"
radios = browser.radios(:value => "very")
# Iterate through each radio button in the collection and set it
radios.each do |radio|
radio.set
end
This can be shortened to simply:
browser.radios(:value => "very").each(&:set)