Capybara/Poltergeist, expanding Accordion Table expanding intermittent failure - capybara

I have an issue with expanding a table/row. I did some googling and haven't found any information on such a scenario.
Basically I have a table with 5 or so columns. When clicked on will expand the table and reveal more information. Inside this new information is a button im trying to click on.
Here is the HTML for the row for the table iself:
<thead>
<tbody>
<tr class="odd" data-row="0" data-source="/user/01" role="row">
<td class="sort_1">John Doe</td>
<td>Person</td>
<td>Yes</td>
<td>Yes</td>
<td> Building 2 </td>
</tr>
<more table rows here repeated>
</tbody>
</table>
The table html itself has an id and everything, but I need to click on any one of the columns within the row itself to get it to expand.
I tried this: page.find('td', :text => 'John Doe').click and while it did work successfully, after taking a screenshot it didn't seem to show anything expanded...so im not sure an expanding table will work in this instance. HOWEVER upon re-running it , it worked....but it's not consistent.
The screenshots that poltergeist show have the expanded table barely showing (like just a sliver of expansion). So maybe it's not waiting enough time for it to appear? Im not sure.
When the table expands it basically makes a new <tr user-data-row="5"> that will appear underneath the row above when expanded.
For reference here is the HTML for the button that I click after expansion:
<div class="col-xs-12 col-sm-5 col-md-5" style="">
<ul class="list-unstyled">
<ul class="list-inline">
<li>
<a class="btn btn-sm btn-default" href="/stuff/site" data-method="post" rel="no-follow" data-confirm="Generic: Confirmation Dialogue">The Button</a>
</li>
<li> </li>
</ul>
</div>
I thought at first that it was tripping up on the confirmation dialogue popup, but Poltergeist is supposed to avoid those by default. And it seems to do that....so Im not sure what could possible be the problem. With it being inconsistent Im thinking possibly it's a timing issue. My Capybara.default_max_wait_time = 15 since the DB is on a VM, but it hasn't been an issue so far....
Edit:
The code that fails seems to be after this:
page.find('td', :text => 'John Doe').click
click_link('The Button')
It seems to work about 50% of the time.

When using some Capybara drivers with animations it's possible for clicks to miss their targets. This happens because the location for the mouse click to occur is calculated, the mouse event is created, and submitted to the "browsers" queue for processing. Before the event is processed it's possible for the destination element to move enough that the click misses the element. There are a few ways to handle this
The best solution all around (speed, reliability, validity of mouse events) is to disable animation in the test environment. This can usually be done by conditionally setting a few JS variables depending on the libraries being used.
Sleep for a small time before attempting to click so the animation has time to complete - This is slower, but still keeps "real" mouse clicks that travel through the pages event processing like a users would.
Use #trigger to simulate a click - element.trigger(:click) - this will be quick but can short circuit of lot of things that are done to make sure tests validly replicate what a user can do and can therefore lead to tests that miss failures. It should be used as the method of last resort

Related

Capybara: click on text within <span>

I had a bot that would apply on indeed.com jobs. It would collect jobs then apply to them one by one. However, indeed recently made things a lot harder. Used to be able to just locate the button's id and use that but now the id is dynamic: changes from different job positions.
Does anyone know how it is possible to link to the "Apply Now" button (not really a botton) if the code below is:
<a class="indeed-apply-button" href="javascript:void(0);" id="indeed-ia-1532137767182-0">
<span class="indeed-apply-button-inner" id="indeed-ia-1532137767182-0inner">
<span class="indeed-apply-button-label" id="indeed-ia-1532137767182-0label">Apply Now</span>
<span class="indeed-apply-button-cm">
<img src="https://d3fw5vlhllyvee.cloudfront.net/indeedapply/s/14096d1/check.png" style="border: 0px;">
</span>
</span>
</a>
Many ways to click that element, the 3 simplest would probably be
click_link('Apply Now') # find link by partial text and click it
click_link(class: 'indeed-apply-button') # find and click link by class
find('span', text: 'Apply Now').click # find span by text and click in it
in my case I had to select an option shown using js-chosen library inside an iframe. So, its not a regular select tag. So had to do following to select:-
within_frame(find("#cci_form")) do
find('#showing_listing_id_chosen').click
find('[data-option-array-index="2"]').click
end
But, no luck using
find('.active-result[data-option-array-index="2"]').click
or
find('li[data-option-array-index="2"]').click

Capybara unable to select radio button in data tables

I have a bit of an issue, im trying to select a radio button in a data-tables data. Im able to filter the single data-tables row down to one so it's the only one appearing on the page...but i've tried page.choose, page.find(<xpath>), page.find(<css>), i've also tried doing all the previous within a certain css selection and I can't quite figure out what's left to try.
The relevant HTML is here, unfortunately as this is a work item I can't post everything. however I AM able to click on the label if I specify the id via:
find(:xpath, "//label[#for='approve_row_5']").click however this doesn't actually seem to 'select' the radio button. I've also tried doing a wait after I filter the data-table results
anyways, here is the HTML for the 2 radio buttons after the row has been filtered (the radio buttons reside in a column)
<td class=" align-middle">
<div class="radio">
<input type="radio" name="approve_deny_row_5" id="approve_deny_row_5_approve" value="person_approve" data-ui-verify-key="test_approve" data-ui-verify-title="2017-07-13 14:59:46 -0400">
<label for="approve_deny_row_5_approve">
<span>Approve</span>
</label>
</div>
<div class="radio">
<input type="radio" name="approve_deny_row_5" id="approve_deny_row_5_deny" value="person_deny" data-ui-verify-key="test_deny" data-ui-verify-title="2017-07-13 14:59:46 -0400" data-ui-verify-url="/irrelevant/stuff">
<label for="approve_deny_row_5_deny">
<span>Deny</span>
</label>
</div>
</td>
I thought about just finding the span by the text and clicking it, which passes....but doesn't actually select the radio button. Also I tried searching by the specific value selector as well via a find('input[value="test_approve"]').click but that had no luck either
Any ideas?
Assuming the actual radio inputs are visible on the page (and not hidden to allow for styling) the methods that should work for this are
choose("Approve")
choose("Deny")
or
choose("approve_deny_row_5_approve")
choose("approve_deny_row_5_deny")
If those tell you they can't find the elements then it's most likely the input elements are actually hidden (for styling reasons) and you should be able to use the
choose('Approve', allow_label_click: true)
which will click on the label element associated with the input rather than the input element. That should produce the same result (setting the radio button) unless the behavior you're looking for is based on JS looking for a click on a very specific element (rather than the change event on the input). If that happens to be the case then you need to figure out exactly what element the JS is looking for clicks on, or fix the JS to behave in a more intuitive manner.

How to open a view form server site and display its link in Asp.net MVC?

I wrote a Web Application. In which the users can make inputs which will be saved
in a database. (with a [HttpPost] function). From these functions I open other Views. My problm is that althought the Views show up without any problem the path in the link stays the same. This is problematic when I want to go back or I want to copy the link... My idea was to somehow display the link per javascript everytime I load a View but that seems to be a very unprofessional way of doing it.
Simple,
<div class="table-outer">
<table width="50%" align="center" border="1">
<tr>
<td>Column1</td>
<td>Column2</td>
<td>Column3</td>
<td>Column4</td>
</tr>
</table>
</div>
.table-outer{width:100%; background:#ddd;padding:20px}
automatically align center
https://jsfiddle.net/kamatchikumar/3u5qwqms/

AngularDart table data binding not working

In a Chrome Packaged app, using angular.dart, in a component html I have the below table which is dispaying paginated contents of an object list; exp. jobs :
<tbody>
<tr id="{{job.jobID}}" ng-repeat="job in jComp.jobs | pagination:jComp.paginator.instance" ng-model="job" ng-click="jComp.selectJob(job.jobID)">
<td>{{job.jobID}}</td>
<td>
{{job.productID}}<br>
{{job.productDescription}}<br>
</td>
<td>{{job.productType}}</td>
<td><img alt="No thumb" ng-src="{{job.thumbFURL}}"/></td>
<td>{{job.status}}</td>
</tr>
</tbody>
then in my component I am showing a popup modal div to ask user to choose an action. When the user clicks to change the job.status, there is a second layer of confirmation modal, and once confirmed in the component I am finding the job from the jComp.jobs List and updating the status. In side the popup modal the status gets updated but on the table it still shows the old status. And then when I paginate to next page and come back it gets updated. Before page switch it stays the same ? I tried calling scope.apply() after updating the jobs list, but it doesn't help. I like to know what is paginator.goNext() or paginator.goPrev() doing that causes the table list to refresh. Not clear about how the two way data binding working in this scenario. ( pagination is done using angular_pagination package )
I tried using :
<td ng-bind="job.status"/>
instead of:
<td>{{job.status}}</td>
and it fixed this issue. I am not sure why double brackets were not registering the object in the list to the change listeners / watchers. As far as I understand two way binding should be working with {{}} but in this case something was breaking it. It might be a bug in double brackets.

Knockout, JQMobile, and generating a collapsible-set doesn't quite seem to work right

I've checked out a number of samples, but none are quite the same as what I'm trying to do.
What I've got works, mostly, but it doesn't quite work right.
Here's a fiddle to illustrate the issue.
http://jsfiddle.net/5yA6G/4/
Notice that the top set works fine, but it's statically defined.
The bottom set (Tom, steve, bob) "work" basically, but the header element ends up both in the collapsible header AND in the portion of the collapsible that gets hidden.
Seems like I must be doing something wrong, but I haven't been able to figure out what.
Any ideas?
Just for reference and for anyone else running into this problem, it turns out to be at least somewhat obvious in hindsight.
Knockout's built in "anonymous" templating works great in many cases, but with JQMobile, it can be a tad quirky.
That's because JQMobile will adjust the content of the anonymous template when the page loads, just as it does with all the other content.
Then, later, when you use knockout's ApplyBindings function, knockout will add the applicable elements, just as it should. As many posts and answers have hinted at, you then MUST call collapsible() on the newly created elements, via something like this.
$("div[data-role='collapsible']").collapsible({refresh: true});
No problem there. HOWEVER, if JQM has already applied formatting, then the anonymous template has already been "rendered" by JQM, so rendering it again by calling collapsible causing all sorts of funky results, including doubled heading, nested collapsibles, etc.
The solution for me was to use Knockout's "Named Template" feature, and just put the template to render the collapsible elements into a tag, like this:
<script type="text/html" id="alarm-template">
<div data-role="collapsible" data-collapsed="true" data-collapsed-icon="arrow-d" data-expanded-icon="arrow-u" data-enhance="false">
<h3 data-bind="text:name"></h3>
<p>The content here</p>
<p data-bind="text: name"></p>
</div>
</script>
Doing this prevents JQM from "rendering" the template elements when the page loads, so they'll be rendered properly when they're actually generated.
EDIT: The above works fine for collapsibles NOT in a collapsible-set, but, if they're in a set, I've found the styling of the elements (particularly, the corner rounding to indicate belonging to a set) doesn't work right.
From what I can tell, there are 2 problems:
The first is that just triggering "Create" doesn't actually refresh the styling of all the collapsibles in the set. to do that you have to do...
$("div[data-role='collapsible-set']").collapsibleset('refresh');
But, there's a worse problem. JQM "marks" the last item in the set as the "last item". That fact then gets used to determine how to style the last item as it's being expanded/collapsed.
Since Knockout doesn't actually rebuild the entire set (for speed), each time you call the refresh method, JQM dutifully marks the last item as "last", but never removes the marks on previous items. As a result, if you start from an empty list, EVERY item ends up being marked "last" and the styling fails because of this.
I've detailed the fix for that at github in an issue report.
https://github.com/jquery/jquery-mobile/issues/4645
I actually found a much easier way to do this:
Set up your foreach binding as you normally would for me it looked like this
<div data-bind="foreach: promotions">
<h3 data-bind="text: Title"></h3>
<p>Creator:<span data-bind="text: Creator"></span></p>
<p>Effective Date:<span data-bind="text: EffectiveDate"></span></p>
<span data-bind="text: Description"></span>
<a data-bind="text: ButtonText, attr: {href: ButtonLink}"></a>
Wrap that in a div with class="collapsible like so
<div data-role="collapsible-set" data-bind="foreach: promotions">
<div class="collapsible">
<h3 data-bind="text: Title"></h3>
<p>Creator:<span data-bind="text: Creator"></span></p>
<p>Effective Date:<span data-bind="text: EffectiveDate"></span></p>
<span data-bind="text: Description"></span>
<a data-bind="text: ButtonText, attr: {href: ButtonLink}"></a>
Apply the collapsible widget via jquery mobile after you do your binding like so:
$(document).ready(function () {
ko.mapping.fromJS(data, dataMappingOptions, PromotionViewModel.promotions);
ko.applyBindings(PromotionViewModel);
$('.collapsible').collapsible();
});
For a collapsible set the same idea can be applied just set the class="collapsible-set" on your foreach div. Hope this helps

Resources