I have a web page with a grid, and some columns have a link in the column header that will sort the values by that column [by round-tripping to the server].
I want to write a single Watir test that will identify those sorting links and click each one in succession. This is a smoke test that ensures there are no runtime exceptions resulting from the sort expression.
My question is, what is the best way to (1) identify these links, and (2) click each one in succession? This is what I have so far:
$ie.table(:id, "myGrid").body(:index, 1).each do | row |
row.each do | cell |
## todo: figure out if this contains a sort link
## todo: click the link now, or save a reference for later?
end
end
I think it won't be possible to click each link inside the loop since clicking a link will trigger a page load and I believe this invalidates the other Watir Link Elements.
So, what I suggest is that you take the IDs of the links and then loop over each other doing the clicks.
For instance, I would add an specific class to those sorting links and a meaningful id too.
Something along the lines of:
<a id='sortby-name' class='sorting' href='...'>Name</a>
Then you can pick them with this:
$ie.table(:id, "myGrid").body(:index, 1).each do | row |
# pick sorting links' ids
sorting_link_ids = row.links.select{|x| x.class_name == "sorting"}.map{|x| x.id}
end
And then, you do your clicking like this:
sorting_link_ids.each do |link_id|
$ie.table(:id, "myGrid").body(:index, 1).link(:id, link_id).click
end
Hope that helps!
Related
I have several buttons to click on the same page. How do I iterate and click on each of them?
def btnConectar()
elements = all("button[data-control-name='srp_profile_actions']").count
puts elements
first("button[data-control-name='srp_profile_actions']").click
find("section[class=modal]")
find("button[class='button-primary-large ml1']").click
end
all returns an Array like Capybara::Result object. You can iterate through that using the standard ruby enumerable methods.
all("button[data-control-name='srp_profile_actions']").each do |el|
el.click
find("section[class=modal]") # Not sure what this is for - if it's an expectation/assertion it should be written as such
click_button(class: %w(button-primary-large ml1)
end
That will work as long as clicking on the button doesn't cause the browser to move to another page.
If clicking does cause the browser to move to another page then all the rest of the elements in the Capybara::Result object will become stale (resulting in a stale element reference error on the next iteration) and you won't be able to iterate any more. If that is your case then details on what exactly you're doing will be necessary. Questions like does the original button still exist on the page after clicking the button-primary-large button, or can you iterate by just clicking the first matching button over and over? If it does still exist is it changed in any way to indicate it's already been clicked, or is the number/order of buttons on the page guaranteed to be stable? It would probably help to understand if you posted a fragment of the HTML for the first and second iteration.
def btnConectar()
page.all("button[data-control-name='srp_profile_actions']").each do |el|
while page.has_css?("button[data-control-name='srp_profile_actions']")
el.click #Click the button
find("section[class=modal]") #Modal mapping
click_button(class: %w(button-primary-large ml1)) #Click the button
sleep 3
end
end
end
I have an view that extends the current project view, where we add multiple tabs (notebook pages) to show information from other parts of a project.
One of these pages is an overview page that summarizes what is under the other tabs, and I'd like to link the headlines for each section directly to each displayed page. I've currently solved this by using the index of each tab and calling bootstrap's .tab('show') method on the link within the tab:
$(".overview-link").click(function (e) {
e.preventDefault();
var sel = '.nav-tabs a:eq(' + $(this).data('tab-index') + ')';
$(sel).tab('show');
});
This works since I've attached a data-tab-index="<int>" to each header link in my widget code, but it's brittle - if someone adds a tab later, the current indices will be broken. Earlier I relied on the anchor on each tab, but that broke as well (and would probably break if a new notebook page were inserted as well).
Triggering a web client redirect / form link directly works, but I want to show a specific page in the view:
this.do_action({
type: 'ir.actions.act_window',
res_model: 'my.model.name',
res_id: 'my.object.id',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'current'
});
Is there any way to link / redirect the web client directly to a specific notebook page tab through the do_action method or similar on FormWidget?
If I understood well you want to select the tab from the JavaScript (jQuery) FormWidget taking into account that the id could change if anybody install another module that adds another tab
Solution 0
You can add a class to the page in the xml form view. You can use the id of the element selected by this class name in order to call the right anchor and select the right tab item. This should happen when the page is completely loaded:
<page class="nb_page_to_select">
$('a[href=#' + $('.nb_page_to_select').attr('id') + ']').click()
NOTE: As you have said the following paragrah I assume that you know where to run this instruction. The solution I suggest is independent of the index.
This works since I've attached a data-tab-index="<int>" to each
header link in my widget code, but it's brittle - if someone adds a
tab later, the current indices will be broken. Earlier I relied on the
anchor on each tab, but that broke as well (and would probably break
if a new notebook page were inserted as well).
Solution 1
When the page is loaded you can get the tab list DOM object like this:
var tablist = $('ul[role="tablist"]')
And then you can click on the specifict tab, selecing by the text inside the anchor. So you don't depend on the tab index:
tablist.find('a:contains("Other Information")').click()
I think if you have two tabs with the same text does not make any sense, so this should be sufficient.
Solution 2
Even if you want to be more specific you can add a class to the notebook to make sure you are in the correct notebook
<notebook class="nt_to_change">
Now you can use one of this expressions in order to select the tab list
var tablist = $('div.nt_to_change ul.nav-tabs[role="tablist"]')
// or
var tablist = $('div.nt_to_change ul[role="tablist"]')
Solution 3
If the contains selector doesn't convince you because it should be equal you can do this as well to compare and filter
tablist.find('a').filter(function() {
return $.trim($(this).text()) === "Other Information";
}).click();
Where "Other Information" is the string of the notebook page
I didn't tried the solution I'm giving to you, but if it doesn't work at least may be it makes you come up with some idea.
There's a parameter for XML elements named autofocus (for buttons and fields is default_focus and takes 1 or 0 as value). If you add autofocus="autofocus" to a page in XML, this page will be the displayed one when you open the view.
So, you can try to add this through JavaScript, when the user clicks on the respective link -which honestly, I don't know how to achieve that by now-. But you can add a distinctive context parameter to each link in XML, for example context="{'page_to_display': 'page x'}". When you click on the link, I hope these context keys will arrive to your JS method.
If not, you can also modify the fields_view_get method (here I wrote how to do that: Odoo - Hide button for specific user) to check if you get the context you've added to your links and add the autofocus parameter to the respective page.
As you said:
This works since I've attached a data-tab-index="" to each header
link in my widget code, but it's brittle - if someone adds a tab
later, the current indices will be broken.
I assume that your app allow multi-user interaction in realtime, so you have to integrate somewhere in your code, an update part function.
This function will trig if something has changed and cleanout the data to rebuilt the index in order to avoid that the current indices will be broken.
I am using VS2015 to develop and need insert items into a CListCtrl object. I use InsertItem() to add the new item at the end of the list. Below is my code
int nIdx = m_SessionTimesListCtrl.InsertItem(
m_SessionTimesListCtrl.GetItemCount(), IFMT("%s/%s/%s", getTime(), getWeekDay(), getTimeZone()));
My intention is to get below list
03:00:00/MON/US
17:00:00/TUS/US
17:00:00/WED/US
17:00:00/THU/US
however, I got this list
03:00:00/MON/US
17:00:00/THU/US
17:00:00/TUS/US
17:00:00/WED/US
the only explain is CListCtrl sorts the inputs despites I give it the index to be inserted.
I checked my resource file and there is no sorting attribute been used.
CONTROL "",IDC_LIST_SESSION_TIMES,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,152,58
So, my question is how I can disable the auto sorting of CListCtrl?
The LVS_REPORT style is normally used when there is a need to display the items in a sortable fashion. Using this style may result in the list control having a CHeaderCtrl upon which one could click to sort the list items.
If sorting is not desired, and if there's no need to display a column header, you might want to not use the LVS_REPORT style. Choose something like LVS_LIST style instead.
If a column header is desired, but no sorting is required, you might want to disable the sorting properties of the control by doing something like:
m_SessionTimesListCtrl.ModifyStyle(LVS_SORTASCENDING|LVS_SORTDESCENDING, 0);
Here's the item in question - specifically that little caret:
http://screencast.com/t/NMPOM9Ok58q
As you can see there are multiples of those within the same page, they all have the same class etc.
I've tried several different routes and I've not been able to successfully click on that item.
I always want to click on the last of them present on that page (the number of them are dynamic so sometimes it's the 2nd one and sometimes it's the 6th - so referring to it with a specific number doesn't work)
Thanks for the help (my tests are written in ruby, using selenium and testunit)
Here are some things I've tried and a few variations of these as well (none of which actually produce a click on that item)
#driver.find_element(:class, "dropdown-toggle")[-1].click
#driver.find_element(:css, "(//*[contains,'a.dropdown-toggle')]").click
element_present?(:css, "div.dropdown.open > a.dropdown-toggle").click
#driver.find_element(:css, "div.dropdown.open > a.dropdown-toggle").click
#driver.find_elements(:css, "caret")[-1].click
#driver.find_element(:css, "caret:last-of-type").click
#driver.find_element(:css, "div.dropdown.open > a.dropdown-toggle:last-child").click
#driver.find_element(:class, "span1").find_element(:tag_name, "a").click
^ This one actually is the only one that clicks anything - but it only clicks the first carat.
Ultimately what I'm doing with this test is adding a filter, closing the filter window, re-opening the filter window, deleting the previous filter, adding a new one and closing the window.
how about using the CSS last child selector?
#driver.find_element(:css, "div.dropdown.open > a.dropdown-toggle:last-child").click
If <div class="span1"> is unique, you can try someth like:
#driver.find_element(:class, "span1").find_element(:tag_name, "a").click
ok, so, if you need to click all links, or just someth of it, then:
#links = #driver.find_element(:class, "span1").find_elements(:tag_name, "a")
#links[0].click - for first link
#links[1].click - for second link
etc.
I have a cucumber feature file which looks like below,
Given I am logged in to the console
When I navigate to the "href1" page
When I navigate to the "href2" page
When I navigate to the "href3" page
When I navigate to the "href4" page
Instead on writing the when command 4 times I want to know if I can somehow pass the value of the href and call it in the when statement.
I have a step definition which looks like this :
When(/^I navigate to the "(.*)" page$/) do |navigation_link|
#browser.link(:href => navigation_link).when_present.click
end
Let me know if you need more information.
Multiple options to do it:
a) If you need to test that you can navigate to each of the pages independtly, you can use a Scenario Outline like:
Scenario Outline: Test navigation
Given I am logged in to the console
When I navigate to the "<href>" page
Examples:
|href |
|<actual_url1>|
|.... |
|<actual_url4>|
In this case, your step doesn't need to be modified, and should work as is.
b) If you need to navigate to the pages in a dependent manner, i.e., reaching page 4 requires you to follow through page1 -> page2 ->... page X -> page 4, you can use a table like:
Scenario: Test navigation
Given I am logged in to the console
When I navigate through the following pages:
|<actual_url1>|
|.... |
|<actual_url4>|
And then in your step definition:
When(/^i navigate through the following pages:$/) do |table|
table.raw.each do |navigation_link|
#browser.link(:href => navigation_link.join).when_present.click
end
end
(Read more on tables & outlines here: http://cukes.info/step-definitions.html)
Though, I wouldn't suggest using either of them if you just want to check that you can navigate to page4. I'd rather create the step to only navigate to page 4, and within that step's definition-> take the url_paths in variables (much better to have in a separate paths file and load from there)-> navigate to other pre-requisite pages-> Navigate to the final page.
This way you would only describe the behaviour and not literal navigation points.