Watir-webdriver can't click on visible link which appears in popup - ruby

I am using watir-webdriver + ruby + rspec + gem parallel_tests.
And my test case has to be able to cancel and then delete item from items list. "Cancel" and "Delete" links appears from pop-up menu.
Following method verifies cancel link gear_dropdown_menu.cancel_job = browser.link(:text, 'Cancel') is visible.
Timer.repeat_until_true(30, 1) do
sleep 1
gear_dropdown_menu.cancel_job.visible?
end
When link appeared and I can see it, next I try to click it using such code:
browser.execute_script("
id = $('div.job-actions:visible').data('id');
$('a[href*=\"jobs/'+ id +'/cancel\"]').show().click()
")
div.job-actions:visible - actions popup, where links are placed
I use data-id attribute to specify direct link href. But it looks like watir can't see it.
The problem is when I execute tests in parallel, 2 from 3 tries it fails.
When I execute test non-parallel, looks overall good.
What may be the cause of the problem?
Updated: add code of popup. It's not a separate window. Just hidden div element which appears when I click a button.
<div class="job-actions" data-id="8769" style="top: 329px; right: 0px; display: block;">
<section>
<header>Actions</header>
<ul>
<li class="pause">
Pause Job
</li>
<li class="cancel">
Cancel Job
</li>
</ul>
</section>
</div>

(text: 'Cancel') is going to match the first cancel button on your list. If you are running tests in parallel you are likely getting race conditions to cancel/delete the same items from the list.
A couple other tips:
You don't have to use Timer. Your code is functionally equivalent to the built in method:
gear_dropdown_menu.cancel_job.wait_until_present
Also, is your popup a separate window? If so, you don't need to use execute_script (which should be avoided when possible). you can use:
browser.window(title: 'Your Popup Title').use { browser.link(href: /cancel/).click } (or whatever selector makes sense)

If your problem only occurs when running in parallel then it sounds like you may have either dependencies or interferences between tests, which end up creating a race condition.
If you are doing Create, Update, Delete type operations, then each test needs to work with unique objects, created with unique identifiers (names? id's? ) if not then either an object might not exist when you need it (not created yet, deleted, etc) or you could run into conflicts caused because the object names/identifiers are not unique (so a test could re-create a new copy of an object another test just deleted, causing the second test to think the delete failed, etc)
Without seeing a LOT more of your code it would be difficult to give you more precise advice, but in my experience when something works when done in series, but not if the order is altered, or the test run solo, or tests run in parallel, then you likely have dependencies between tests, or a situation like one test deleting something before the other test can try to cancel or update the same something.

Related

Capybara wait until button is enabled?

Surprised I actually haven't come across this, but I have a simple button that is disabled until a dropdown is selected. Sometimes the page isn't fast enough to "enable" the button to be clicked on after the previous dropdown is selected causing it to fail.
I could throw in a "sleep" of a second or two and fix this, but that seems like a lazy/poor way to do this.
Is there a way in capybara (or purely selenium) that I can make it wait until the button is actually enabled? I'd like to throw this is the page model method for this button (as im trying to avoid API specific methods/selenium/etc... in the actual test specs (Although I can if I need to).
FWIW this is specifically for Ruby's capybara framework but pure selenium is fine as well.
Assuming the button you're referring to is actually a button (<button> element, or <input> element with type submit, reset, image, or button) then Capybaras :button selector will (by default) wait for it to be non-disabled.
click_button('Something')
or
find_button('button_id').click
or
find(:button, 'button_value').click
If any of the finder or action methods aren't waiting long enough for a specific element you can always increase the maximum wait time for a specific finder/action by passing a :wait option
find(:button, 'Something', wait: 10).click
If you're not using selector types (if not, why not) and instead are just using raw CSS to locate the element then you can use the :enabled pseudo class along with your existing CSS and something like
find('#my_button:enabled', wait: 10).click
If the element you're calling a button isn't actually a button but some other type of element (<a> etc) styled to look like a button, then you'll need to explain exactly how you're disabling the "button".
In Python you can do something like this:
def wait_until_clickable(driver, xpath, timeout = 1):
while timeout > 0:
try:
element = driver.find_element_by_xpath(xpath)
element.click()
return element
except:
time.sleep(0.1)
timeout = timeout - 0.1
return False

Handling Element is not clickable at point (x, y). Other element would receive the click: With Capybara, ruby, Webdriver

I am writing a UI test that goes to a list of articles and selects the load_more button until this is no longer available (because all articles have loaded). The problem is the button has been hidden at this point rather than removed and is returning this error
Selenium::WebDriver::Error::UnknownError: unknown error: Element <div
class="">...</div> is not clickable at point (x, y). Other element
would receive the click: <div class="" id="" data-module-name="">...
</div>
(Session info: chrome=65.0.3325.181)
(Driver info: chromedriver=2.36.540469
ruby-2.5.1/gems/selenium-webdriver
3.11.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
I have tried a few loop types but for this, if we assume I'm using
while world.page.has_load_more?
world.page.load_more.click
break if world.page.has_load_more? == false
end
The question is how do I tell Selenium this is ok and not to error, so I can continue with the next step in my test case(cucumber). I realise that the choice of loop type may also be incorrect, so feel free to suggest changes there as well.
Firstly, as Rajagopalan has pointed out, the error that you're getting is not that your element is not visible (as in hidden by CSS) but that it is overlapped by another element. You can call save_and_open_screenshot to see what element is overlapping the button (possibly a fixed footer, etc?). Make sure you've set the window size large enough so you don't have elements unintentionally overlapping and make sure you're running the latest version of Capybara since it attempts to deal with overlapped elements in selenium by scrolling them into view if possible.
Secondly, the loop in your question doesn't make a lot of sense having both the while and break since they check the same thing. Also, assuming the "load more" button is loading and appending to the page via AJAX, you probably need to wait for the new elements to load before checking if the button exists (click does not wait for actions to complete since it has no idea what actions could/would be triggered). That would then become a structure something like
while page.has_button?('Load More')
# Get the current number of visible articles
article_count = page.all('div.article').count # Use whatever selector would match an article in the list
# click the button
page.click_button('Load More')
# ensure more articles have loaded
expect(page).to have_css('div.article', minimum: article_count + 1)
end
Note: You'll probably want to change that to use whatever page object methods you are using (has_load_more?, etc) but the general logic should be correct

Loading default XSLTForms subform on body onload event

I'm trying to use subforms and I had a problem: I saw the example provided in the oficcial XSLTForms but in that case the elements that load/unload the forms are always in the "main form" and, in my case, I need them to desappear, because I'm trying to build something like a wizard, So the first subform must desappear when the user press "Next" and then subform2 is loaded, and so on. This presents two problems:
1) If I include the first subform elements in the "main page", when I press the trigger the elements of subform1 are never unloaded. The other subforms do it, but that initial one is treated as part of the structure that never changes... And I really need to desappear. SO I think I have to put all the content of subform1 outside, in a separate xml and load it in the same way as the other subforms, but there is another problem:
2) I need it be loaded by default, and I tryed to put a load element directly in the main form body, but it didn't work.
I "patched it" temporally with a trigger in the main form, which loads the firms subform, but it is so... ugly, and I still have the same problem: I can navigate through subforms but that initial trigger never dissapears... So, any idea will be welcome! Thanks in advance!
<xf:model xmlns="" >
<xf:action ev:event="xforms-ready">
<xf:load show="embed" targetid="subform" resource="FirstSubform.xml"/>
</xf:action>
</xf:model>

why does this happen in x-editable + ng-repeat

I have been exploring Angular X-editable project for a while and came across this odd behavior.
Whenever I click on any one of the 3 'select' components and try to change the value from the dropdown, the method 'showStatus(..)' gets fired for other components as well that are inside the ng-repeat boundary. (which you can check through the console.)
Can you please tell me why is this happening? Am I missing something.. ?
EDITED LINK -> Fiddle : http://jsfiddle.net/hrr4M/4/
<span ng-repeat="d in list" >
<a href="#" editable-select="d.status" e-ng-options="s.value as s.text for s in statuses">
{{ showStatus(d.status) }}
</a> <br/>
</span>
The problem you have is that your binding
{{ showStatus(d.status) }}
fires up the function for every item, because as every item gets populated, it just refreshes and fires again, all of them.
This is not the right place to set this up.
I set up a modified fiddle for you: http://jsfiddle.net/hrr4M/13/
Inside the link statement I added
onaftersave="showStatus($data)"
That way, you can fire a function after an item has been selected (see docs for onaftersave vs onbeforesave), and you can get the selected value using $data.
Now it works already. The problem is, that your binding is still using the same function, and therefore firing up all the time.
I duplicated the function and renamed it to repeatFiller with the same functionality but omitting the console logs, so you can see it works. You might tweak that a bit but I think it does what you need.

Interacting with VB6 client using hidden control in embedded web browser control

I'm having difficulty trapping a programmatically triggered click event on a hidden button control from a ASP.NET MVC 4 web app inside a VB6 thick client (which is using a web browser control). I'm able to trap the click event itself using the following:
Private WithEvents WebDoc As HTMLDocument
Private Function WebDoc_onclick() As Boolean
Select Case WebDoc.activeElement.iD
Case "A"
Do something
Case "C"
Do something else
End Select
WebDoc_onclick = True
End Function
And this works just fine if the control is visible. But if the control is invisible:
<div class="HideBtnDiv">
<input id="C" name="NoItems" type="button" class="BtnDiv" style="display:none"/>
</div>
and I try to trigger a programmatic click via one of the following:
$("#C").('click');
$("#C").trigger('click');
$("#C").triggerhandler("click");
$("#C").focus();
$("#C").trigger('click');
I'm getting an empty string for the "id" attribute and as a result I can't distinguish which button was clicked. This button serves no purpose other than to indicate to the VB6 app that a certain criteria has been met and that's the reason why I need it to be hidden. Does anyone have any idea why the id is getting stripped? Or is there any other way to communicate back to the client?
I've also tried filtering by element style using
Select Case WebDoc.activeElement.Style
Case "display:none"
Do something else
End Select
but it came back as "[Object]" so no luck there either. Please let me know if there is a way around this.
Thanks,
Lijin
You seem to have tried several ways of dynamically triggering the click event, but did you try the most obvious way:
$("#C").click();
???
But here is what I would do:
1- Make all of your buttons visible, by removing "display:none" from their style
2- Wrap the buttons you want to hide in a new DIV
3- Set "display:none" style in the newly created DIV
4- You can then trigger the .click() event of any button even if not visible by calling $(id).click();
Thanks, Ahmad. Actually I meant .click() not .('click'). Sorry about that.
Anyway, I tried your suggestion and made the button visible and set the style of the wrapping div to display:none but the id attribute was still coming through as an empty string.
However, I did figure out another way to get this to work. If I keep the wrapping div and button as visible and then focus and click when the condition is met and then do a hide(), my problem is resolved!
$("#C").focus();
$("#C").trigger('click');
$("#C").hide();
The button doesn't get displayed and VB6 still passes the id on the click event. The weird thing is it requires the focus() call to still be made. Without it, I'm back to square one. Not sure if this is a bug.

Resources