Appium-Capybara-ruby running automation on Physical device and some of the capybara function not working - ruby

I have setup Appium - Capybara and was able to run automation suites on physical device, but facing issue with few methods
I was able to successfully run Capybara methods like
fill_in field, :with => text
click_button button_text
expect(page).to have_content(text)
But facing issues with below method ( it works on regular chrome on laptop but not on mobile )
page.first(:link, link_text).click
can you please help me to understand if appium capybara supports all the capybara methods or only few of them.
Below is the error message
undefined method `click' for nil:NilClass (NoMethodError)

In the above code, first the link with link_text is being searched on the page. If no link is found then nil is returned.
Thus, to make this code work, we need to wait for the link with the link text to appear on the page and then click it.
So you can use any one of the below mentioned code before clicking the fink
page.should have_content(link_text)
page.find_link(link_text)
If the above code doesn't works then you can also try increasing the default wait time as shown below:
Capybara.default_wait_time = 30
page.should have_content(link_text)
page.first(:link, link_text).click
Capybara.default_wait_time = DEFAULT_WAIT_TIME
DEFAULT_WAIT_TIME for Capybara tests is set in your environment file.
Hope this helps :)

undefined method 'click' for nil:NilClass (NoMethodError) is telling you that #first isn't returning an element (#first returns immediately with a matching element, or nil if there is none). Is there a reason you are using first rather than find?? If there is only one matching link on the page you should really prefer find over first (in this case find_link), or try to scope to a part of the page where the would only be one link, since it will throw an error explaining why it didn't find the link (rather than returning nil) and also has waiting behavior for elements to show up (first can have the same behavior but it doesn't by default)

Related

Capybara will not click button for Stripe SCA authentication

I cannot get Capybara to click on the SCA/3DS ‘Complete authentication’ button when running RSpec tests. Similar tests which do not trigger SCA pass just fine, and if I run VNC to view what Firefox is doing, the button is visible and I can click it myself in the browser.
My problem seems very similar to what’s discussed in the comments here, but the solutions do not work: I have tried changing the browser used, and flattening the iframe traversal.
Test code:
scenario "SCA required" do
create_payment_method(account, payment_method: "stripe", last_four: "1234")
visit "/billing"
click_on "Enter Card Payment"
within "#main-content" do
within_frame(find("iframe")) do # Stripe payment form is in an iframe.
find("input#Field-numberInput").set("4000002760003184") # SCA-required test card.
find("input#Field-expiryInput").set("1234")
find("input#Field-cvcInput").set("123")
find("input#Field-postalCodeInput").set("12345")
end
end
find("button#submit").click
# Stripe nests the popup in several layers of iframes.
stripe_frame = find("body > div > iframe") # Popup is prepended to the body element.
switch_to_frame(stripe_frame)
challenge_frame = find("iframe#challengeFrame")
switch_to_frame(challenge_frame)
fullscreen_frame = find("iframe.FullscreenFrame")
switch_to_frame(fullscreen_frame)
click_on "Complete authentication"
switch_to_frame(:top)
expect(page).to have_content "ends in 3184"
end
Is there some way to debug what Selenium is doing under the hood here? I don’t see any movement on the page when running click_on "Complete authentication", but if I click on the button myself in the Firefox instance being controlled by Selenium it does work.
Running click_on "Complete authentication" returns the element clicked, which appears to be the expected element when I drop into Pry and call native.dom_attribute("id").
I can see an error of some kind in the browser container’s logs:
1654078084345 Marionette WARN TimedPromise timed out after 500 ms: stacktrace:
TimedPromise/<#chrome://remote/content/marionette/sync.js:239:19
TimedPromise#chrome://remote/content/marionette/sync.js:224:10
interaction.flushEventLoop#chrome://remote/content/marionette/interaction.js:431:10
webdriverClickElement#chrome://remote/content/marionette/interaction.js:179:31
It’s a bit odd because it mentions #chrome but this is a headless Firefox instance.
Assuming no error is returned by the click_on call then I'm guessing the button is being clicked before it's ready to be clicked. You can test that by sleeping for a few seconds before calling 'click_on'/navigating through the frames. If that fixes it then you'd need to look at what changes on the button to indicate that the page has finished whatever work it's doing and the button is ready to be clicked.
I have solved this by clicking on the button directly with JavaScript:
execute_script(%Q{ document.querySelector("button#test-source-authorize-3ds").click() })
However, this does not in any way explain why click_on is not working, and if anything makes it more strange that it is not. If anyone has a better solution or a way to dig into why Capybara/Selenium are failing then that would be welcome.

SitePrism: sometimes find elements, sometimes doesn't while Capabara can

I am using Capybara and Selenium for testing my website. I also use with Site Prism for Page Object model. I can make every thing work now, however, I don't understand why sometimes actions with page elements donot work, while using "natively" Capybara work.
For example, I have a Page object:
class MyPage < SitePrism::Page
element :sign_in_link, :css, 'a.signin-link'
element :join_link, :css, "a.join-link"
end
and its implementation:
#mypage = MyPage.new
#mypage.sign_in_link.click
# It works at first, then after some repeated test round, it doesn't work sometimes, with error: NoMethodError <br>
While I use:
find(:css, 'a.signin-link').click #=> always work, but not Page Object model
So, why it happens? Have anyone experienced this problem?
By default site_prism disables Capybaras implicit waiting behavior while finding elements. This means to have the same behavior as your capybara example you would need to do
#mypage = MyPage.new
#mypage.wait_for_sign_in_link
#mypage.sign_in_link.click
You can read more about this in the site_prism README under "Using Capybara Implicit Waits"
Another options is to use site prisms "Load Validations" feature to ensure pages are loaded before starting to click on their elements

Verify for the text to be present in an overlay using watir-webdriver

I have an overlay form where i create an user for our application. After giving the details in the text fields i click on save and try to capture the Saved Successfully Text which appears for about a second on the overlay. But i am unable to do so as i get an error saying "Element is no longer attached to the DOM (Selenium::WebDriver::Error::StaleElementReferenceError)".I have used the below code:
if($browser.div(:class=>"validation-summary-valid").exists?)
message=$browser.div(:class=>"validation-summary-valid").li.text
if(message=="Saved Sucessfully")
puts("Save action complete")
else
fail("fail")
end
end
in capybara i would scope the code using ( within ) to the message element in the Dom then use have_content
within('#Browser div')do
page.should have_content('Saved Successfully')
end
hope this will help to try something similar in watir
What I understood from the situation is, the moment you click on save a transient message appears on the UI and a check needs to be performed.
The below approach should work fine in this case,
# the browser waits for 20 s until the element is present(exists+visible) on the UI
$browser.div(:class=>"validation-summary-valid").wait_until_present(20).li.text

Is "enabled?" method available on Watir and/or Page-Objects for a link?

Yesterday i was working on determining is this link was or not enabled, waiting until enabled in order to click it. I'm using Cucumber + Ruby + Watir + Page-Object gem. The link is very similar to:
<a id="the_link" href="#" disabled="disabled">Proceed with your order</a>
Once you fill some fields, the link is enabled and the source changes to:
<a id="the_link" href="#">Proceed with your order</a>
The idea is to wait until the link is enabled in this way, which works with buttons:
def click_on_link
Watir::Wait.until { self.the_link_element.element.enabled? }
self.the_link
end
...but does not work with the link. I made it work determining if the attribute exists, this way:
def click_on_proceed_form
Watir::Wait.until { !self.the_link_element.element.attribute_value('disabled') }
self.proceed_form_submit
end
Looking for the "enabled?" method at the Watir documentation here or at the Page-Object gem here, it seems that is available for all elements. But looking for the same method in the Watir documentation here, seems it's only available for Buttons, Selects and Inputs.
So, i wonder is there is an "enabled?" method for links (anchors) and, if it exists, how to use it. Can you help clarify this issue, please? Thank you very much!
Watir-webdriver does not support the enabled? method for links. I believe this is because the disabled attribute is not a standard attribute for links.
On the other hand, Watir-classic does support the enabled? method for links. However, it will always return false (again because links cannot be disabled).
Therefore, I think your approach is correct (unless you want to monkey patch the link elements to support the enabled?).
However, you should try to avoid using Watir-webdriver directly where possible. The page-object gem has its own methods for waiting and getting attribute values:
def click_on_proceed_form
wait_until{ !the_link_element.attribute('disabled') }
proceed_form_submit
end

In Ruby, with selenium, difficulty clicking Google Search

I get this far, opening the firefox browser, navigating to google, and finding the google search element
irb(main):001:0> require 'selenium-webdriver'
=> true
irb(main):002:0> driver = Selenium::WebDriver.for:firefox
=> #<Selenium::WebDriver::Firefox::Marionette::Driver:0x..fb3c81796cc82b708 browser=:firefox>
irb(main):003:0> driver.navigate().to("http://www.google.com")
=> nil
irb(main):188:0> driver.find_element(:name, "q").send_keys "fff"
=> nil
irb(main):112:0> driver.find_element(:name, "btnK");
=> #<Selenium::WebDriver::Element:0x5fb450f4379c50ce id="d767311c-27a2-3544-8f11-e4edc9736588">
irb(main):113:0> driver.find_element(:name, "btnK").attribute('value');
=> "Google Search"
But I can't manage to click it!
irb(main):114:0> driver.find_element(:name, "btnK").click
Traceback (most recent call last):
16: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/w3c/bridge.rb:552:in `execute'
15: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/bridge.rb:166:in `execute'
14: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/http/common.rb:62:in `call'
13: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/http/default.rb:104:in `request'
12: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/http/common.rb:84:in `create_response'
11: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/http/common.rb:84:in `new'
10: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/response.rb:32:in `initialize'
9: from /usr/local/lib/ruby/gems/2.5.0/gems/selenium-webdriver-3.141.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
8: from clickElement#chrome://marionette/content/listener.js:1209:5
7: from navigate#chrome://marionette/content/listener.js:409:13
6: from navigate/<#chrome://marionette/content/listener.js:410:13
5: from clickElement/<#chrome://marionette/content/listener.js:1210:14
4: from interaction.clickElement#chrome://marionette/content/interaction.js:130:11
3: from webdriverClickElement#chrome://marionette/content/interaction.js:159:11
2: from ElementNotInteractableError#chrome://marionette/content/error.js:286:5
1: from WebDriverError#chrome://marionette/content/error.js:178:5
Selenium::WebDriver::Error::ElementNotInteractableError (Element <input name="btnK" type="submit"> could not be scrolled into view)
irb(main):115:0>
It says that the element "could not be scrolled into view" !
I can do driver.find_element(:tag_name, "body").send_keys :page_down; which would page down. Which is mentioned Looking at ruby selenium documentation for send_keys https://www.rubydoc.info/gems/selenium-webdriver/Selenium%2FWebDriver%2FElement%3Asend_keys . I see a list of key codes https://www.rubydoc.info/gems/selenium-webdriver/Selenium/WebDriver/Keys#KEYS-constant . And also listed https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/common/keys.rb . Or I can page down manually. But scrolling (at least in the normal sense of the term) doesn't seem to be the issue, I still get that error. Maybe it's in a frame I can switch to and I don't know which.
The button is of course visible. It's just a regular google search page.
I can find the button fine but I can't see how to click it, as .click isn't working for me.
added
in reply to a comment asking me if the google suggestions box is obfuscating the button. I can see in the browser that the button isn't obfuscated 'cos sometimes I have done escape manually, or clicked the background window(to get rid of that. I am using IRB so running each statement manually so I have time to do that). But I also did escape with the code, and escape works to get rid of the suggestion box, but still same error when trying to click the button
While discussing this problem in the comments, I wrote some C# code to demonstrate how we wait for clickable in C#
new WebDriverWait(Driver, TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementToBeClickable(By.Name("btnK"))).Click();
In the process of writing the C# code I posted, I think I see what the issue is. When you start typing in the search box, a dropdown appears that contains the suggested searches. This covers the "Google Search" button that you are trying to click... BUT another "Google Search" button appears at the bottom of the dropdown itself. You should print a count of the (:name, "btnK") element(s)... I'm assuming it will print 2 (if a proper wait is added). From there, you just need to click the one that is on top.
Another option would be to send a \r\n at the end of your search string and avoid this whole issue... or you could just navigate to the final search URL and save even more headaches and time.
Added note from barlop
To clarify, this has nothing to do with the suggestions popup showing(and having to get rid of the popup).. and nothing to do with having a wait (Since I was working with IRB, I was naturally waiting). And I'd already clicked outside or hit escape to get rid of the popup. The issue was, and Jeff's answer alerted me to this - Even after the popup is gone, there are still two buttons with that btnK name. So, using find_elements rather than find_element, and running .click on the second one works! (Also, using find_elements(plural) for the name attribute makes sense, since as noted in an answer herehttps://stackoverflow.com/questions/5518458/does-a-name-attribute-have-to-be-unique-in-a-html-document. the name attribute is not unique / not a unique identifier.
This is what I told you to use WATIR, If you had used WATIR, this problem wouldn't have arrived. The problem in your code, it's not waiting for visibility. Recently Chrome Driver has added implicit wait for click as well, so it would wait for visibility if you set the implicit wait. Otherwise move to WATIR which is a good wrapper which doesn't wait via driver, In WATIR waiting for element status happens from local language binding, so use this selenium code
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
driver.manage.timeouts.implicit_wait = 10
driver.navigate().to("http://www.google.com")
driver.find_element(:name, "q").send_keys "fff"
driver.find_element(:name, "btnK").click
Remember this selenium code wouldn't work for firefox because implicit wait for click is not added yet for firefox.
Since WATIR is handling the timing from local language binding, it doesn't matter whether you use Chrome or Firefox, it would perfectly work
WATIR Code (Default is Chrome)
require 'watir'
b=Watir::Browser.new
b.goto 'www.google.com'
b.text_field(name: 'q').set 'fff'
b.button(name: 'btnK').click
If you want to drive Firefox
b=Watir::Browser.new :firefox
But you can do the click with javascript (sending javascript to the browser).
b=driver.find_element(:name, "btnK")
driver.execute_script("arguments[0].click();",b)
Jeff explains why the javascript method(selenium with javascript), works on either button but the non-javascript method(selenium without javascript) works only on one button, he wrote "Both of the buttons would work, the JS click will click on any element no matter where it is... or if it's visible, etc. Selenium was designed to interact with the page as a user would so it was unable to click on the element due to the error."

Resources