How can I wait for an image to load using Capybara? Here's my app code:
-- <img src="data:image/jpeg.*" class="some class" alt="Image">
I have tried using the wait_for method using those class and img attributes with no luck. It never waited for that image to load.
img elements have a complete property that indicates whether the image is loaded or not. Therefore something along the lines of
start = Time.now
while true
break if find('img.some_class')['complete']
if Time.now > start + 5.seconds
fail "Image still not loaded"
end
sleep 0.1
end
would wait up to 5 seconds for the image to finish loading, assuming the img element is already on the page.
This could also be implemented as a custom selector with something along the lines of
Capybara.add_selector(:loaded_image) do
css { |locator_class| "img.#{locator_class}" }
filter(:loaded, default: true, boolean: true) { |node, value| not(value ^ node['complete']) }
end
which could then be called as
find(:loaded_image, 'some_class')
*** Note: I haven't actually tried the custom selector code, but it should be close enough to provide guidance
Related
I have the HTML below:
<tbody>
<tr>
<td>
<span>
<span> I Will Change </span>
</span>
I am trying to figure out when the text inside of the span changes and store the new text in a variable. This is what I tried:
waitForClick = wait.until {
newText = ExpectedConditions.invisibilityOfElementWithText(xpath, //path for the span with the text
}
This produces a syntax error and I am not sure how to use the invisibilityOfelementswithText. How could I use this in Ruby and what is the best way to notice a change of text? I do not know what the text will change to so I cannot have a wait.until either
I'm not ruby developer but i think that the idea needs to be something like:
while(driver.PageSource.Equals("I Will Change"))
{
sleep(1000)
}
I don't think there's a way to do this using ExpectedConditions. The text related methods are all related to finding text not for waiting for text to change. In Java there is FluentWait but I can't find any reference to it for Ruby. FluentWait allows you to write a custom wait so that you could wait for the current element with the current text to return false which would signal a text change. Other than that, you would have to implement it yourself. I don't know Ruby but the pseudocode would be like
timeout = 20 // seconds
while (element.text == "I Will Change" and timeElapsed < timeout)
{
sleep(100)
}
Make sure you add the timeout to the code above if you implement it yourself. You don't want the expected text to be slightly off and the script get stuck and never finish.
As to the InvisibilityOfElementWithText() method, it's looking for an element to go invisible or to be removed. I don't think that's what you want here because you are expecting the element to persist but the text contained in it to change.
I want to retrieve hidden text when the visibiility attribute is hidden:
<div id = "tt52433002" class="yui-module yui-overlay yui-tt yui-overlay-hidden" style="z-index: 2; visibility: hidden;">
<div class="bd">Associated with the domain : testci20160503105556.com</div>
</div>
I tried:
browser.hidden(:class, 'bd').text
and
browser.hidden(:class, 'bd').value
But I get this error:
"unable to locate element, using {:class=>"bd", :tag_name=>"input", :type=>"hidden"}"
Watir is designed to act like a user. So if a user can not see the text in an element, then Watir will not return the text of the element.
Also, the element you are looking for is a div not a hidden.
If you need the text you can do:
browser.div(class: 'bd').inner_html
which makes a JavaScript call to provide the result.
This works:
browser.div.attribute_value('id') => tt52433002
as does this:
browser.div(class: 'bd').inner_html[/testci\d{14}/] => testci20160503105556
First things first. The error says that Watir cannot find an element using the criteria you specified. That means either that no such thing exists anywhere in the DOM, or that it might be inside a frame.
Since the element you want is a div, then you should be using the .div method to access it
browser.div(:class => 'bd') #finds first matching div
A potential second problem could occur if that classname is not very unique. Unless you specify an additional parameter, such as index, or perhaps a portion of the text contained by the div, you may not find the div you are looking for. A fast debugging trick (I like to do it from IRB) is to get a collection of matching divs and check the size
div_count = browser.divs(:class => 'bd').size
puts "there are #{divcount} divs of class bd in the dom"
If the count is anything more than 1, then you likely need to change how you are selecting it to ensure you get the right one. for example
browser.div(:class => 'bd', :text => /Associated with the domain/)
If the count is zero, then check for frames
frame_count = browser.frames.size
iframe_count = browser.iframes.size
If there are frames you will have to tell watir to look inside the frame for the div, if more than one frame then be sure you specify the right one
browser.frame.div(:class => 'bd') #looks for div inside first frame
Once you are sure you have the right div, then you ought to be able to use a method like .text or as in another answer .inner_html to get the contents of the div.
It's maybe an easy question but after some research I didn't find any valid solution.
I use carrierwave to generate multiple versions of an uploaded image. Each image has a specific size for different screen sizes.
For example (in my uploader) I have
version :xl
for screens larger than 1200px.
How can I use rails to display specific image according to client screen size ?
I know there is gem responsive-images but I can't use size.
The subject of responsive images is for the moment quite vast and nobody seems to have the same answer, especially with javascript and html5. How about rails (even if it's server side) ?
Here are a couple of ways:
Load the page with all the paths to various image sizes stored as data attributes, grab the screen width with js, then stick an image tag with the correct path where you want it. Code might look like (not tested, and I've been using coffeescript, so probably missing details):
# ruby command line
$ #image.mobile_asset_url
=> "path/to/mobile/image"
$ #image.desktop_asset_url
=> "path/to/desktop/image"
# html.erb
<img src="" class="placeholder" data-mobile-url=<%=#image.mobile_asset_url%> data-desktop-url=<%=#image.desktop_asset_url%>></div>
# javascript
el = $('.placeholder')
data = el.data();
width = $(window).width();
mobile_breakpoint = 480;
if (width <= mobile_breakpoint) {
url = data.mobileUrl;
} else {
url = data.desktopUrl;
}
el.attr('src', url)
Do a server side test not for screen width, but browser type with a gem like browser, and load smaller images for mobile specific browsers like iOS8. Logic like this might go best in a decorator. I'm more fluent with Ruby, so an example might look like (this code not tested):
# user_decorator.rb, from the Draper gem
class UserDecorator
def profile_url
if mobile_browser?
"path/to/small/image"
else
"path/to/large/image"
end
end
private
def mobile_browser?
browser.ios || browser.android || ...
end
end
Well, I really wanted to do it in a more rails way. I ended up using ajax :
Fire ajax at load page :
# assets/javascript/cards/index.js
// fire loading image
$.ajax({
url: document.URL,
data: {
w: window.innerWidth
}
});
In controller, respond to ajax :
# controllers/cards_controller.rb
render 'cards/images/loadimage' if request.xhr?
Then load your images
# cards/images/loadimage.js.erb
<% w = params[:w].to_i %>
document.getElementById('banner-container').innerHTML = "<%= j image_tag #card.banner_type_url(w), class: 'banner' %>";
document.getElementById('logo-container').innerHTML = "<%= j image_tag #card.logo_type(w), class: 'logo' %>";
I made in my model a method to return right image given screen width (and based on carrierwave version) :
# models/card.rb
# returns banner type based on width (w)
def banner_type_url(w)
return self.banner.s.url || banner_default('s') if(w <= 768)
return self.banner.m.url || banner_default('m') if(w <= 992)
return self.banner.l.url || banner_default('l') if(w <= 1200)
return self.banner.xl.url || banner_default('xl') if(w <= 1920)
return self.banner.full.url || banner_default('full')
end
def banner_default(s)
"card/banner/#{s}_default-banner.jpg"
end
How would I use Applescript to click on a web link in a google search. Can I identify them by name or number or anything?
Safari:
tell application "Safari"
open location "http://google.com/search?q=example"
do JavaScript "window.addEventListener('load', function() {
document.querySelectorAll('.r a')[0].click()
})" in document 1
end tell
Chrome:
tell application "Google Chrome"
open location "http://google.com/search?q=example"
tell active tab of window 1
execute javascript "window.addEventListener('load', function() {
document.querySelectorAll('.r a')[0].click()
})"
end tell
end tell
Edit: try something like this to match a YouTube result by title:
document.querySelector('.yt-uix-tile-link').click()
Edit 2: changed window.onload=function(){} to window.addEventListener('load',function(){}).
Building on #Lauri Ranta's great answer, here is convenience function clickOn(), which:
accepts a target document, a CSS selector string plus a zero-based index to select among what the selector string matches, and simulates a click on the element thus identified.
works irrespective of whether the target document is still being loaded or has already fully loaded - this turned out to be non-trivial. (Lauri's code relies on the window object's load event not to have fired yet).
works with both Safari and Google Chrome (if you don't have Chrome installed, you'll have to comment out a few lines)
Examples that use a delay to demonstrate that the click works even after the document has fully loaded:
# SAFARI
# Click on the *2nd* result returned from googling 'example' with Safari:
tell application "Safari"
open location "http://google.com/search?q=example"
delay 5 # sample delay - NOT needed
my clickOn(document 1, ".r a", 1)
end tell
# CHROME
tell application "Google Chrome"
open location "http://google.com/search?q=example"
delay 5 # sample delay - NOT needed
my clickOn(active tab of window 1, ".r a", 1)
end tell
clickOn source code:
on clickOn(doc, cssSelector, ndx)
# If no explicit index (into the matches returned by the CSS selector)
# is specified, default to 0.
if ndx is missing value or ndx = "" then set ndx to 0
# Synthesize the JavaScript command.
set jsCode to "
(function () { // Use a closure to avoid polluting the global namespace.
function doIt(t) { // Helper function
if (doIt.done) return; // Already successfully called? Ignore.
try {
// Perform the desired action - may fail if invoked too early.
document.querySelectorAll('" & cssSelector & "')[" & ndx & "].click();
} catch(e) {
return; // Return without setting the success 'flag'.
}
doIt.done=true; // Set success 'flag' as a property on this function object.
};
// Attach a listener to the window's load event for invoking the helper function then.
window.addEventListener('load', doIt);
// If the document signals readiness -- which may still be too early, we can't know --
// also try to invoke the helper function *directly*.
if (document.readyState === 'complete') { doIt(); }
})();
"
# Execute the JavaScript command in the target page.
if class of doc as text is "document" then # Safari: a «document» instance was passed
using terms from application "Safari"
tell doc to do JavaScript jsCode
end using terms from
else # Google Chrome: a «tab» instance was passed
# !! IF CHROME IS NOT INSTALLED, SIMPLY DEACTIVATE THIS `using terms from` BLOCK.
using terms from application "Google Chrome"
tell doc to execute javascript jsCode
end using terms from
end if
end clickOn
I have a standard rails application with a delete link. This delete link comes up with a browser popup modal (using rails confirm option).
I am currently attempting to test the delete function with Cucumber, Selenium-Webdriver (or Watir-Webdriver, still haven't decided), and the page-object gem.
Once the modal has been triggered, anything I do on the page gives me the following error:
Modal dialog present (Selenium::WebDriver::Error::UnhandledAlertError)
I have been looking all over, but cannot find a way to handle this condition. If possible, I would like to continue using the PageFactory module in the page-object gem.
How can I dismiss/accept the modal?
I figured out a way to do this, but haven't decided upon the exact implementation.
In Javascript you can overwrite any function, which means you can overwrite the confirm
This means that you can run the following code to disable any popups.
def disable_popups
# don't return anything for alert
browser.execute_script("window.alert = function() {}")
# return some string for prompt to simulate user entering it
browser.execute_script("window.prompt = function() {return 'my name'}")
# return null for prompt to simulate clicking Cancel
browser.execute_script("window.prompt = function() {return null}")
# return true for confirm to simulate clicking OK
browser.execute_script("window.confirm = function() {return true}")
# return false for confirm to simulate clicking Cancel
browser.execute_script("window.confirm = function() {return false}")
end
If you put this inside the initalize_page function of a page-object then the dialogs are automatically removed.
def initialize_page
disable_popups
end
Or you could do it right before the pop is triggered
def delete
disable_popups
delete_link # => clicks the link
end
References:
Testing Webpages with Javascript Popups Correctly
Dismissing Pesky Javascript Dialogs with Watir
The page object gem has methods to handle javascript popups - see the original page-object gem post. In your case, I believe you want the confirm method::
(String) confirm(response, frame = nil, &block)
Override the normal confirm popup so it does not occurr.
Examples:
message = #popup.confirm(true) do
#page.button_that_causes_confirm
end
Parameters:
what (bool) — response you want to return back from the confirm popup
frame (defaults to: nil) — optional parameter used when the confirm is nested within a frame
block — a block that has the call that will cause the confirm to display
Returns:
(String) — the message that was prompted in the confirm
There is similar for the alert and prompt popups.
In your page-object, I would define:
class MyPage
link(:delete_link, :id=>'delete')
def delete()
confirm(true){ delete_link }
end
end
Then when you call page.delete, it will click the link and confirm the popup.