Is the element draggable ? Capybara - ruby-on-rails

I'm developping my application with Rails.
So, my question is quite simple and though, I haven't find any answer on the Internet.
I have some div with class="draggable" on my view.
I applied to all the element having this class the method .draggable(); found here :
http://jqueryui.com/draggable/
Then, in my capybara test, I want to check if all of the ".draggable" element really are draggable or not.
And I don't know what to use to verify if they're actually draggable or not.
With the chrome jquery console, I noticed that when I run ('.draggable').data(), there a uiDraggable object.
Anyways, I don't know how to write my test. If someone has an idea....?
thank you

Related

Capybara not seeing updates to the DOM made by Javascript

I'm running Rails 5.x, with, Cucumber, Siteprism and Capybara through chromedriver. Most things work except..
I have a tiny bit of javascript that changes the class on an element in response to an event. But Capybara never sees the change. It only ever sees the class the element has when the page initially loaded.
Using Chrome, and debugging my Cucumber steps, I can see the element has the new class, but Capybara doesn't see it.
This must be an issue other people have encountered and solved, though I can't find the right subject title.
example coffeescript
$(document).on('focus', 'tbody#item-entry > tr > td > input', (e) ->
$(#).closest('tr').addClass('focused-row')
$(#).closest('td').addClass('focused-cell')
)
example html after the focus event has been triggered
<tr class="focused-row">
<td>ignore this </td>
</tr>
The purpose is to change the background colour of the row containing an input element that has focus. It works.
But Capybara, can't see the class, but it can see any classes added when the page is loaded. e.g.
expect(siteprism_stuff.root_element['class']).to match(/focused-row/)
Ignore the SitePrism stuff, that just gets the right element. root_element is the Capybara class for the dom node.
Now I know it's getting the right Capybara element because if I change my view to put stuff in the class for each row, then it sees that perfectly OK. What it can't see is the any new class added via Coffeescript. Although it's visible in the Chrome inspector, and changes the background color of the focused row as required.
You're specifying an "ends with" CSS attribute selector ($=)
input[class$='form-control']
which since the class attribute for the element you're interested in
<input type="search" class="form-control form-control-sm" placeholder="" aria-controls="universitiesTable">
doesn't end with 'form-control' is correctly not matching. You probably just want to use a normal CSS class selector input.form-control if continuing to do it the way you are. Any of the following options should find the search field and fill in the data you are trying to fill in.
fill_in 'Search:', with: string
fill_in type: 'search', with: string
find(:field, type: 'search').set(string)
find('input.form-control').set(string)
Note: Your question is still unclear as to whether you are seeing the class added in the inspector in test mode, and whether the line color is changing while the tests are running (or whether you're only seeing that in dev mode) - This answer assumes the JS is actually running in test mode and you're seeing the line color change while the tests are running.
You don't show how you're actually triggering the focus event but I'll assume you're clicking the element. The thing to understand when working with Capybara is that the browser works asynchronously, so when something like click has been done, the actions triggered by that click have not necessarily been done yet. Because of that, whenever doing any type of expectation with page elements you should always be using the matchers provided by Capybara rather than the basic matchers provided by RSpec. The Capybara provided matchers include waiting/retrying behavior to handle the asynchronous nature of dealing with the browser. In this case, assuming siteprism_stuff.root_element is the row element then you could be doing something like
expect(siteprism_stuff.root_element).to match_css('.focused-row')
or depending on exactly how your siteprism page objects are setup you could pass the class option to the siteprism existence checker
# `page_section` and `have_row` would need to be replaced with whatever is correct for your site prism page object
expect(page_section).to have_row(class: ['.focused-row'])

making unobtrusive validation work when using Select2 ASP.NET MVC

Select boxes converted to Select2, do not automatically integrate with unobtrusive validation mechanism in ASP.NET MVC framework.
For example, on a form which contains a regular select box (marked as required in model definition), submitting the form while no options have been selected in the select box, will cause the border and background of the select box to take a reddish color, and by using #Html.ValidationMessageFor, error messages, if any, can be displayed beside the box. However if the select box is converted to a Select2 component, then none of the mentioned features work any more. Even the validation error message will not show up.
It seems that the reason for even the validation error message not showing, is because Select2 changes the display CSS property of the original select box to none (display:none), and I guess the unobtrusive validation script does not bother generating error messages for invisible fields.
Any ideas / solutions?
This issue isn't really specific to Select2, but rather to the jQuery unobtrusive validator.
You can turn on validation for hidden fields as highlighted in this answer.
$.validator.setDefaults({
ignore: ''
});
As the comments noted, it didn't work inside an anonymous callback function within $(document).ready(). I had to put it at the top level.
I've run into similar issues with the select2 plugin. I don't know exactly which features you're using specifically, but in my experience, when you set an element as a select2 in the document.ready event, the plugin will change some of the element's attributes on the fly (inspect one of the elements after your page has finished loading - oftentimes you'll see the id and class properties are different than what you're seeing when you view source).
It's difficult to offer more without actually seeing the code, but here's a few ideas to get you started:
First off, obviously make sure you have the a link to your select2.css stylesheet in the header.
Then, since you're talking about form submissions, I'd recommend you examine whether or not you're getting a full postback or submitting via AJAX (if you're using jQueryMobile, you're using AJAX unless you override it in the jquerymobile.js file or set a data-ajax="false" in your form attributes). You can just look at the value returned by Request.IsAjaxRequest() for this. Obviously if you're submitting via ajax, you won't hit the document.ready event and the select2 won't initialize properly and you'd need to figure out a way around that. Try refreshing the page after the submit and see if it renders the select2 component.
Then I'd suggest examining the elements and see if they're not behaving like you'd expect because you're actually trying to work with classes that the plugin has reassigned at runtime. You can either just adjust your logic, or you can dig into the select2 code itself and change the behavior - it's actually fairly well-documented what the code is doing, and if you hop on the Google group for select2, Igor is usually pretty quick to follow up with questions.
like this
$('select').on('select2:select', function (evt){
$(this).blur();
});
$('body').on('change', 'select.m-select2', function () {
$(this).blur();
})

jQuery UI Autocomplete: How to target dropdown menu?

Problem: I have to color my results in the little dropdown based on their value.
Solution: Use the 'open' event hook to loop through options and assign a color.
Problem: So the documentation for the jQuery UI autocomplete says that the open event hook receives two arguments- 'ui' and 'event'. The problem is, 'ui' is just an empty object (someone filed a bug report about this, and the brilliant jQuery UI team said it's not a problem), and 'event' only has the input box, not the dropdown that's generated. At this point, the only way I can select my options list from here is to do this:
$( event.target ).nextUntil("ul.ui-autocomplete").last().next()
That's gross. Please tell me there's a better way?
PS: If anyone says "Just use $('ul.ui-autocomplete')!" you've obviously never worked on anything more complicated than.... something that's not complicated.
The official documentation is terrible, but after lots of exploring I figured it out:
$(event.target).data('autocomplete').menu.element
Are you writing a plugin? You can use this.element

How to bind backbone.js events to JQuery UI dialogue windows?

I'm very new to backbone.js but we're starting to use more and more JS on the front end and I wanted to use some framework to give the code structure - backbone seems to suit our needs.
So started off with a very simple test app that launches a dialogue window using jquery-ui. The problem I have is that since jquery-ui adds a wrapper DIV round the original template used by backbone, the events no longer fire.
I don't want to use the jquery-ui event model as I'd rather just use the one - how can I bind backbone's to this new structure?
It looks as though the call to _.template() is actually doing the wrapping in an extra div. The parent div with the events bound to it is being left behind appended to #well. A simple workaround is to call .parent() on the result of getting the element with the model class ID. See here for example
There's more than likely some information in the _ documentation that might shed some more light on the problem too.
OK - at the end of this project, I finally realised that I hadn't answered this. What happens is when you create a .dialog with JQueryUI, it actually detatches your original DOM element and moves it to the bottom of the DOM wrapped in it's own JQueryUI markup to turn it into a dialog.
Since the Backbone view's element has now been moved, Backbone doesn't pick up any events that fire as it's happening outside it's own "view" as far as it is concerned.
So what you have to do is reassign the view's element:
var dlg = this.$("#dialogue-properties").dialog({ ..});
this.setElement(dlg);
Where this is the view. This is done within the initialize method
You can create div wrapper in your view and modal it's content, as described here (first part of the post)
cocovan does a good job explaining the problem in his answer. However, as for the solution, the JQuery UI team actually added a method at the end of 2012 (Allow dialog to be attached to a element other than body) that takes care of this issue.
It is the appendTo(selector) method (jQuery Dialog appendTo method). So simply specify to which element you want the dialog appended.

TD reordering with jquery-ui sortable, chrome not rendering again

I am using jquery ui sortable to sort TD's in a TR. In chrome this seems to be indenting the TDs to the right. From a similar question found on SO, I reckon this is because of chrome adding an extra TD which is not even visible under console, and is only being rendered.
How do make this work?
I have found this snippet that tries to force chrome to render again, but this does not seem to work.
var n = document.createTextNode(' ');
$('TD:eq(0)').parent().get(0).appendChild(n);
n.parentNode.removeChild(n);
The similar question found on SO says that he was able to solve the issue by calling a function .render() on his view item, I presume this is a custom function for a library the asker was using.
Any ideas on how to force refresh the DOM?
To those who are facing a similar issue, I found that hiding the parent element and showing it after a small delay(10ms works), keeps things working.

Resources