ruby shoes: create modal window - ruby

Is there an attribute to create a modal window e.g. one that has complete focus.
we cannot use confirm/alert etc. and i'm assuming i create a window/dialog, but its asynchronous and exits after creation?
i.e. something like
dialog :title => '', :modal=>true do
googled, been through all ruby manuals and no reference to modal/focus?
checked out the source code and had a look for modal/keep focus type parameters - couldn't find anything?
i've come across suggests for Ruby/Tk (similar type of stuff) where people have to wait in a loop for close events and exit. this seems a bit cumbersome?
any thoughts out there?
thanks
Ben

Not entirely sure what you are trying to do here. Are you trying to launch another window alongside the main window? That is well supported:
Shoes.app do
para 'first app'
button "launch second" do
Shoes.app width: 180, height: 60 do
para 'I am a second app!'
end
end
end
Although that is an entirely new window, so it might not be what you want. If you want an all focus window that is essentially part of the app and greys out the main application, then no that is not possible :( Created an issue for modals though.

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

Ruby: how to renew the path of an image after image has been changed?

I am trying to build a GUI with ruby Shoes to select a background image for my desktop. As I am struggling with a particular part of my idea I am going to describe just the problem part, the rest works fine.
Here's the code of my ruby Shoes app:
Shoes.app :title => "Images!" do
stack do
#img1 = image "desktop_pic", :width => 200, :height => 100
button "Change image", :margin => 25 do
#img1.path = "/home/njutik/preview_desktop_pic"
end
end
end
Here's what the result looks like when I start my Shoes app:
result_at_beginning
In the background there is a different ruby script running which generates a new background-image and stores it as preview_desktop_pic.
So when I click the Change image-button, the path of #img1 gets adjusted and I see the new image:
result_after_clicking_change_button
That's fine so far. My problem is, that nothing happens when I click the Change image-button again. Of course in the meantime there is already a new image preview_desktop_pic so the code line
#img1.path = "/home/njutik/preview_destkop_pic"
which is executed each time I press the Change image-button should show me a new picture but nothing happens. Even when I delete the preview_desktop_pic from the folder and then press the Change image-button there is still no change at all and all I see is the same picture shown after clicking the Change image-button for the first time.
So my question is: what am I doing wrong and how can I make the Shoes app show the current preview_desktop_pic every time I press the Change image-button?
Any hints would be really great!
Update: After the comment of 7stud I tried to define a singleton method for the button. Like this:
#change_image = button "Change image"
def #change_image.click
#img1.path = "/home/njutik/preview_login_background"
end
#change_image.click
But that did not help - nothing changed.
Then I tried this:
def #img1.reload
#img1.path = "/home/njutik/preview_login_background"
end
button "Change image" do
#img1.reload
end
But this also did not help. I thought that by defining singleton methods I would delete the cache memory.
Any further hints would be really helpful.
I don't think you are doing anything wrong. I think that what you are seeing has to do with image caching, which Shoes uses for efficiency. If ten windows all use the same image, Shoes does not fetch the file from disk (or download the file from the web) for each window. Instead, Shoes caches the image used in the first window, and when the other windows use the same path for the Image, Shoes retrieves the image from the cache. As a result, if the path for your Image doesn't change, it looks like Shoes uses the cached image. It would be nice if an Image in Shoes had a reload() method, which would force Shoes to ignore the cache and go get the image again.
In your app, the first button click works because the Image's path changes from "/desktop_pic" to "/home/njutik/preview_desktop_pic". Subsequent clicks do not change the Image's path.
To change the image, the new image must have a different name than the previous one. I do not know why but it works for me then.

Center a dialog in Pharo 3.0

If I 'do it' in a Workspace in Pharo 3.0
self confirm: 'test'
then the dialog is modal but no way to make it centered. Is there a workaround?
You and do this but in more complex way.
First of all you can try to do something with Spec like:
(LabelModel new text: 'test') openDialogWithSpec centered
But here you also have to handle the result of a dialog separately and so on (defined during the setup of the model).
This behavior is by design. The dialog is not modal, it is modal to the workspace
you started it in. There are therefore two good locations for the dialog: close to the
workspace and close to the cursor position (hand). Center of the screen is not as good.
Pharo uses close to the cursor position.
If you take a look at the implementors of confirm: and follow up to
UITheme>>questionWithoutCancelIn: aThemedMorph text: aStringOrText title: aString
you can create something similar and use a variant of
Morph>>openModal:
using the center of the screen instead.

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 design a linear GUI program

I'm making a simple Qt application. It has 4 screens/pages:
Start import
Select folder to import images to
Accept or reject each image in folder, and when no images left:
"No images left" and an OK button.
I can't figure out the best way to implement this. I started off with a QWidget, but this quickly got unmanageable.
Is a QWizard too constrained?
EDIT: Part of the problem with QWizard is it seems to always have "Back" and "Next" buttons. I don't want those as options in this program, so this leads me to believe that a wizard isn't exactly what I'm after.
I'm going to disagree slightly on using a QWizard here. It would be fairly easy to do, but in this case I think it might be easier to just use a QStackedWidget and swap the widget shown based on what you want the user to be able to do. This is likely what is done inside QWizard anyway, without some of the complication for running the buttons and moving back and forth. You also might want to take a look at the state machine stuff they're looking at adding soon, since you're application could so easily be split into states.
I think a QWizardPage is your best bet.
You can disable the 'back' on a QWizardPage by using setCommitPage(True) on it.
You'll also have to override nextId for the 'variable' amount of QWizardPages you want in between step 2 and 4.
here (basic) and here are examples of QWizards.
You can make QWizardPages for your screens and add them to a QWizard. With registerField() you can register fields to communicate between pages.
EDIT:
I didn't test this, but i guess you can control the button layout of QWizard with
setButtonLayout
Create a dialog with a "Start Import" button on top. When the user clicks this:
Populate a QFormLayout :
The layout should have a checkbox and the label is the name of the picture to import. I'm not sure of your requirements, but you could also display a thumbnail of the image.
The user just checks the images he wants.
Then at the bottom have a "Save..." button. When the user clicks this, a Save As dialog appears. You save all the checked images, discard the others.
If there are no images, change the "Save..." button text to "OK", and display a QLabel with the "No images left" string. You can switch between the QLabel and QFormLayout using a QStackedWidget.
Checkout this article on QFormLayout: http://doc.trolltech.com/qq/qq25-formlayout.html
Option: Get rid of the "Start Import" button. Have the app automatically populate the QFormLayout on startup (possibly in constructor if its fast enough).

Resources