Ensure that a popup has been closed with Capybara/Selenium - ruby

I am attempting to write a capybara spec which opens a popup, and then asserts that the popup has closed itself.
visit "/my_page"
click_button "Open My Popup"
within_window("MyPopup") do
# test things on MyPopup
end
sleep 1 # long enough that window.close() has been called in the popup
# ensure that MyPopup is closed
Things I've tried so far...
Counting the window handles:
page.driver.browser.window_handles.length
This remains at 2 even after the popup is closed
As this post suggests, check for the visibility of an element on the popup. This doesn't seem to work. Even though the popup has been closed, I can run assertions against the window as if it's still there, just as before:
within_window("MyPopup") do
# test things on MyPopup
end
I'm ensured that window.close is firing from the popup (by adding some JS that modifies the popup's DOM after the window.close() and asserting on those changes).
I'm fairly stumped here; anyone out there know the secret sauce for asserting that the window actually closed?

Maybe you should wait for your page has no some specific css?
It may look like this:
def wait_for(timeout = 10)
Timeout.timeout(timeout) do
loop do
break if yield
end
end
end
wait_for { page.first(:css, 'your.specific.css').nil? }

Related

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'.

How to open a Glade-created window and wait for it to close, in Ruby?

I want to make my own window, using Glade (3.14.2).
At a certain point in my program, I want to
1) Put up the window and let the user do stuff
2) Wait for it to close
3) Get values from the window object
4) Continue on in my code
So basically, I want to treat the window like a modal dialog - but one that I write and control.
I've tried for a few hours. The window appears just fine, as designed in Glade. The user can interact with it.
When the window closes, code that's been connected with signal_connect('destroy') executes.
But the code that invoked the window's show() method... does not continue executing after the window closes.
class GrammarNodeEditor
#this makes the class visual:
include GladeGUI
def initialize(raw_node = nil, &close_block)
#raw_node = raw_node || {type: :Sequence, data: []}
#original_data = #raw_node[:data]
#close_block = close_block
end
def show
puts "GNE Window Opening"
load_glade(__FILE__)
#builder["window1"].title = "Edit/Create Grammar Node"
#builder["window1"].signal_connect('destroy') {|*args|
#close_block.call(self)
puts "GNE WINDOW DESTROY"
}
show_window()
puts "Done showing window"
end
Here is how I invoke it:
rhs_editor = GrammarNodeEditor.new {|obj|
puts "In closeblck, obj is #{obj.inspect}"
#rhs = obj.raw_node
}
puts "About to call show in GR:Init"
rhs_editor.show
puts "Back from calling show in GR:Init"
Here is the output:
About to call show in GR:Init
GNE Window Opening
In closeblck, obj is #<GrammarNodeEditor:0x7b82a88 #raw_node={:type=>:Sequence, :data=>[]}, [more junk here]>
GNE WINDOW DESTROY
The first two lines of output appear after I open the window. The 3rd and 4th appear when I close the window.
Note that "Done showing window" and "Back from calling show in GR:Init" are not printed at all.
Just to make this a little more interesting, I want to be able to do this from within code that puts up another window. My top-level window has a button to create a new Rule. The Rule must be initialized with a Node, and then the Rule must be edited. So first I need to put up a Node-definition window (as shown above) and then, when I have a Node defined, I want to put up a Rule window that uses that Node.
So I think I need to call this code within either the initialize() or the show() method of the GrammarRuleWindow class (another Glade-defined window).
Can someone explain why my puts's aren't being printed, and how to make the control flow go on through them?
Thanks!
...So it turned out the problem was that I had created the window's .glade file directly in Glade, rather than using the VisualRuby IDE.
Creating the .glade in VR adds some stuff to the file that VR needs. Specifically, the file needs to contain the line
<signal name="destroy" handler="destroy_window" swapped="no"/>
before the first <child...> tag.

Ruby gtk2: button.sensitive = false - events still queue

I use a button on my ruby gtk2 app which starts a longish job processing and I want to disable the button while processing so that the user can't accidentally run it twice. I figured setting button.sensitive = false would do the job and tested it with the following code:
button.signal_connect(:clicked) do
button.sensitive = false
puts "clicked"
sleep 5
button.sensitive = true
end
Clicking on the button after the job has started still seems to put :clicked events on the stack so if I click the button twice more during the sleep, 'clicked' is displayed three times in the console window when I expected it would appear only once.
Do I misunderstand how this is meant to work? If it won't work the way I expect, is there a way to clear the event stack once the job is finished?
Thank you, Torimus - pointed me in the right direction. Apparently it helps if you read the documentation, specifically that of Gtk.events_pending?! Added the following after setting button.sensitive to force the main loop to do its thing:
while Gtk.events_pending? do
Gtk.main_iteration
end

Ruby with Qt

I'm working on a multi form Ruby-Qt program using, and I'm having a problem with controlling secondary windows from the primary one.
How can i disable the primary window when any secondary one is open, also how to take a secondary window output to use it on the primary one, and finally, sorry this is a silly one, what is the appropriate method for closing any window (like this.close in .net) ????
You can make a dialog modal, this disables user interaction with other windows of your application until the user closes the modal window. Use Qt::Dialog.exec instead of Qt::Dialog.show to pop up the window as a modal dialog. This method returns Qt::Dialog::Accepted or Qt::Dialog::Rejected depending on how the user closed the dialog.
To use data from a dialog in the main application window, just save the data somewhere in the dialog class where the main program can access it. For example:
class MyDialog < Qt::Dialog
attr_reader :data
[...]
def updateData(new)
#data = new
end
end
dlg = MyDialog.new(self)
if (dlg.exec == Qt::Dialog::Accepted)
#aButton.text = dlg.data
end
If you are using a dialog, you need to exit it with accept() or reject(), in most cases these are connected to the OK and Cancel button:
connect(okButton, SIGNAL('clicked()'), self, SLOT('accept()'))
connect(cancelButton, SIGNAL('clicked()'), self, SLOT('reject()'))
Other windows can be closed with the close() method.

When does the finish event get called in shoes?

Using Shoes, I would like to have a block executed upon window destruction. I thought I could use the finish event, but in the following piece of code, "Starting" is displayed, but "Finished" is never shown.
Shoes.app(:title => "Test") do
flow do
start do |obj|
alert("Starting")
end
finish do |obj|
alert("Finished")
end
button "Hello"
end
end
After doing some more researching...
It appears that the finish event in Shoes is currently only used for downloads.
In addition, it appears there is no onclose event currently, but it has been discussed.
http://article.gmane.org/gmane.comp.lib.shoes/2976
Apologies for answering my own question, but putting the knowledge out there for others.

Resources