Access browser print popup for testing purposes - ruby

I'm using capybara to write end to end test.
I have a print feature inside my app and would like to access its DOM in order to assert different things on my printed document.
How could I access the popup DOM using Capybara?
+How could I programmatically close the print popup when I'm done asserting? It seems that Capybara is stuck after executing the print method.
ps: I'm using an headless Chrome.

Note: This answer only applies to using the Selenium driver with non-deadless chrome, and once you're inside the shadowDOM you can only use CSS selectors which means you can't use any of the Capybara methods that involve Capybaras built-in selectors ('fill_in', 'select', etc). This may also break with any release of Chrome or chromedriver.
Because of the way chromedriver/chrome works, it isn't returning from whatever action you did to open the print window until the print window is closed. It is technically possible to work around this by triggering the print window asynchronously. But then you run into the issue that most of the print window is inside shadow DOM elements which the WebDriver spec doesn't currently provide an API for. You can however work around that using evaluate_script.
Therefore to open and then close a print window would be something like this
print_window = page.window_opened_by do
page.execute_script('window.setTimeout(function(){ window.print();}, 50)')
end
page.within_window(print_window) do
print_app = page.find('print-preview-app')
print_app_shadow = print_app.evaluate_script('this.shadowRoot')
print_header = print_app_shadow.find('print-preview-header')
print_header_shadow = print_header.evaluate_script('this.shadowRoot')
print_header_shadow.find('.cancel-button').click
end
Obviously all those calls could be chained together, the intermediate elements are only spelled out to make it clearer what is being done.

Related

How to write Xamarin.UITest case for Software Back Button on Android

I am writing UITest cases for my Xamarin forms project. Now, i am stuck at Navigation Part. I know using "app.Back()" we can navigate back but on our Project Hardware Back Button is disabled. Is there any way we can use Navigation Bar "Back button" ?
I tried to get elements in Page by using following code "AppResult[] results = app.Query();" but still i am not able to find any element which says barbackbutton or backbutton etc in the list.
Bharat, after reading this a couple times, I think what you are asking is "how do I find the automation ids / elements to target".
There's a couple different ways to do this. My preferred one is App.Repl(). Here's the Microsoft docs on it, but in short:
at the point in your test where you are on the application view that you want to find an element on, put in App.Repl()
[Test]
public void CanTapButton()
{
App.Repl();
}
Run the test. When the test gets to this point, a repl window will open and the test will pause. End the test if you want, but keep the command window. It will look like this:
Type into the command prompt tree, to see the full layout of the page visible on the device.
You can use the app query calls in the Repl window to draft queries. For example,
app.Query(x => x.Marked("cpgTitle"));
will return the cpgTitle element that you can see listed in the tree. You can then use that app query to interact with the element, using something like App.Tap(appQueryVariable).
AppQueries docs are here and overall, it's very similar to selenium-style selectors.

Is there a way to determine if an element is clickable in Chrome Browser using Selenium and Watir?

As I understand it, this question is really only relevant when performing web test automation using Chrome browser due to the manner in which a click event is implemented with Selenium Webdriver and Chromedriver. To preface, I use and am aware of finding an element through the use of the Watir element function "present?", which as I understand is essentially a combination of "visible?" and "exists?". I could also, at need find an element with Webdriver element functions to identify if an element is present with a rescue for an exception if it is not. What I am trying to determine is the following:
At times, due to a lack of responsiveness to a page, there will be page elements that will be found and pass all validation tests for it's existence, but cannot actively actually be interacted with due to the aforementioned lack of page responsiveness. Using Chrome browser (with Chromedriver) attempts to interact with these elements will result in the error:
irb(main):003:0> #browser.button(:id, "button_login").present?
=> true
irb(main):004:0> #browser.button(:id, "button_login").click
Selenium::WebDriver::Error::UnknownError: unknown error: Element ... is not clickable at point (915,
nt would receive the click: ...
(Session info: chrome=66.0.3359.181)
(Driver info: chromedriver=2.38.552522 (437e6fbedfa8762dec75e2c5b3ddb86763dc9dcb),platform=Windows NT 6.3.9600 x86_64)
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/response.rb:69:in 'assert_ok'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/response.rb:32:in 'initialize'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/http/common.rb:83:in 'new'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/http/common.rb:83:in 'create_response'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/http/default.rb:107:in 'request'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/http/common.rb:61:in 'call'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/bridge.rb:170:in 'execute'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/oss/bridge.rb:579:in 'execute'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/remote/oss/bridge.rb:328:in 'click_element'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.4/lib/selenium/webdriver/common/element.rb:74:in 'click'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.4.1/lib/watir/elements/element.rb:131:in 'block in click'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.4.1/lib/watir/elements/element.rb:656:in 'element_call'
from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.4.1/lib/watir/elements/element.rb:122:in 'click'
from (irb):4
from C:/Ruby23/bin/irb.cmd:19:in ''
I know I can rescue at this point, but that entails that I actually click the element. Essentially I want to write a special function "clickable?" that will return a boolean output without actually clicking the element and possibly navigating away from the page. I would prefer not to attempt this with a type of --ctrl+click, if new window return true, close window, set focus on first window, rescue return false-- workflow.
Watir 6.15.0+
Element#obscured? has been added to check for this scenario. You can now do:
browser.element(id: 'target').wait_while(&:obscured?).click
Watir pre-6.15.0
For older versions, you will need to take a different approach.
I would try waiting for the overlapping element to go away. If the overlapping is something like an overlay that will eventually disappear, it's relatively straightforward - eg:
browser.element(id: 'overlapping_element').wait_while(&:present?)
If the overlapping element gets moved rather than disappears or you don't know the overlapping element, you could try approximating the overlapping element check. When Chrome clicks an element, it gets the element's center location and then clicks at that point. If there the top-level element at that point is not your element, the exception is thrown. The following wait will do this check until there is no overlapping element:
target = browser.button
target_children = target.elements.to_a
browser.wait_until do
location = target.location
size = target.size
center = [location.x + size.width/2, location.y + size.height/2]
element_at_point = browser.execute_script("return document.elementFromPoint(#{center[0]}, #{center[1]});")
[target, target_children].flatten.include?(element_at_point)
end
target.click
Note that I haven't had to do this before, so I don't know if there are edge cases. Seemed to work with Chrome and Firefox.
I can suggest you to wait this button to show on the web page. I experienced the same problem (I was using XPath in my tests). To fix it:
Firstly I defined 2 helper methods because I had to reuse them a lot. One for searching of a exact element on the page(this method is usually takes a while to return a result so you don't need to sleep the browser) and one for clicking a button with given "id".
module Helpers
module Common
def wait_for_element_id(value)
find(:xpath, "(//*[#id='#{value}'])[1]")
end
def click_button_with_id(value)
first(:xpath, "//button[#id='#{value}']").click
end
end
end
After that in your test you can use the helper methods like:
it 'clicks on the login button and magic is executed' do
logout(user)
wait_for_element_id('button_login')
click_button_with_id('button_login')
expect(magic).to be_executed
end
I am also not sure but you can also experience the same problem because of the browser window size (button is not shown because the size is too low) or because of the "headless" mode of your tests.

Can I detect if an element (button) is "clickable" in my rspecs?

Context: In my rspec (using Ruby and Capybara)
I click on a link to test an action in my app: adding a branch to my app.
A modal window opens, where I select the branch, and then I click a "submit" button to add the branch to my app. After clicking "submit" the modal window is closed.
The rspecs continues by clicking "Save" in the main screen, to save the state of the application (and effectively saving adding the branch).
Problem: The rspec is failing because (seemingly) it is trying to click the "Save" button on the main screen while the modal window that is used to select the branch is still present. The test doesn't complain that it can't find the "Save" button component, but that it can't be clicked.
The error in the log is:
[...]Save</button> is not clickable at point (692, 23). Other element would receive the click[...]
A gotcha: this rspec passes correctly on some environments, like when it is run against my local server, but it fails when it is executed by our automation server. Thus, this test has been tagged as "flaky".
Potential solutions: Things we have tried so far:
Play around our "clicks configuration", making sure we are on "ready state" and that the modal window is gone. We failed with this, since we kept hitting the same error.
Implement a "wait". We added a loop to sleep for a bit while the modal window seemed to exist
XYZ.add_new_branch_name(#branch_name)
while Utilities.element_visible?(:xpath, myElement)
sleep(0.5)
end
XYZ.save
The while condition checks if the "submit" button of the modal window exists. The element_visible function uses
find(method,element).visible?
but I'm not sure if find should already take into account that the button may exist and be visible but not be clickable.
Since this still fails, in spite of all our effort to make sure that the modal is gone before we attempt to click on the "save" button, I want tot ask:
Is there a proper way to detect if an element behind a modal window is clickable or not using rspecs?
find only cares about "visibility", not "clickability" (and different drivers may have slightly different interpretations of "visibility"). The reason for the flakiness you're seeing is most likely speed of the machine running the tests which affects the timing of the modal animating away. The best way to solve this issue is to disable animations in the test mode (how you do that is dependent on exactly what library and/or CSS you're using for the animations). The other way is to do as you're doing - checking that the modal has disappeared before clicking the 'Save' button, however you should just be using the Capybara provided methods (which include waiting/retrying behavior) rather than writing your own loop for that.
expect(page).not_to have_css('css selector of the modal') # RSpec version
assert_no_css('css selector of the modal') # minitest version
After looking at the mouse position from your error, one other potential issue you may be having is with screen size and scrolling. If the page requires to be scrolled to get to the 'Save' button and (692, 23) would put the button behind a fixed header (you should be able to verify that by taking a screenshot before the button click attempt) then it may not be possible for whatever driver you're using to click the button. In that case you'd need to use execute_script to scroll the page to a different location so the button is not covered on the page and/or increase the "browser" size so scrolling isn't necessary in the test.
I had a similar problem and solved it by writing my own click_on_with_wait helper function:
def click_on_with_wait(text, wait_time: Capybara.default_max_wait_time)
success = false
(wait_time * 10).round.times do
click_on text
success = true
break
rescue Selenium::WebDriver::Error::WebDriverError
sleep(0.1)
end
# Try clicking one last time, so that the error will get raised if it still doesn't work
click_on text unless success
end
This will try to click on the element. If it's still hidden by the modal, the function will wait 100ms and then try again, until the given wait_time is reached.
Using Rails, I put it in system_spec_helpers.rb so that I can simply replace click_on 'Submit Form' with click_on_with_wait 'Submit Form'.

Testing if a new window opens with Watir-Webdriver

I'm using Watir-webdriver and I was wondering if there was a good way to check if a new window opens. I've googled around a bit and couldn't find anything though it feels like there should be an easy answer.
I have a printer friendly link and I want to test that the link opens in a new window or tab and I would like to test this with ie, firefox, chrome and safari if possible.
Thanks!
You can check the number of windows:
browser.windows.size
or check if a specific window exists:
browser.window(:title => "foo").exists?
More examples in the specs.
You can also use index based browser window checking where you need to worry about index only and it follows zero based index ordering. So, the default window is of index: 0 and if a new window opens it will be of index: 1, the next will be of index: 2 and so on.
To check first child window if you want to test that the link opens in a new window ,
browser.window(index: 1).exists?
Or to work inside this window,
browser.window(index: 1).use do
# do scripting here
end

How to write WATIN scripts for a child window opened with window.open?

I have a main menu window. On clicking any menu item it opens a child window using window.open? I am writing automated test script for this using Watin. How do i write Test script for the child windows.
The Watin.Core.IE class has a static AttatchToIE method you can use. It takes a Watin.Core.Constraint object as an argument (i.e. you have to use Find.ById, Find.ByName, etc.) to help Watin find the window you are looking for, and it returns a reference to an IE object.
Example:
IE myIE = IE.AttatchToIE(Find.ByTitle("Child Window Title"));
From there you can use the myIE object to run your tests, make sure it loads, make sure it has the expected text, etc.

Resources