I am trying to use protractor to get text from several pages, however the recursion seems not working...
The idea is:
getText() from elements with the same "name"
push the text into an array
browser.wait until the push is completed, and then check if the next page button is enable.
If enabled, go to the next page, and call the function again (keep pushing text in the next page to the array).
The codes are (coffeescript):
# define function getMyList
getMyList = (CL, CLTemp) ->
console.log "In function"
tabs = element.all(By.css "[name='some selector']").map((elm) -> elm.getText())
tabs.then((text) ->
CL.push text
return)
browser.wait(->
CL.length != CLTemp.length).then ->
console.log CL.length
nextPage = element(By.css "[title='Next Page']")
# hasClass is a function determining if the button is enabled or not
return hasClass("[title='Next Page']", 'disabled').then (class_found) ->
if class_found
console.log CL
return CL
else
console.log "Enabled"
CLTemp = CL
# scroll down to the button
browser.executeScript("arguments[0].scrollIntoView();", nextPage.getWebElement()).then ->
nextPageGo = element(By.css "[title='Next Page'] span[class=ng-binding]")
nextPageGo.click().then ->
getMyList (CL, CLTemp)
return
return
return
# Run the function. myList = [] and myListTemp = []
getMyList (myList, myListTemp)
The results print:
In function
1
Enabled
In function
Meaning it can run to the first recursion (i.e., goes to the next page), but stops at getting text of second page?
I've scratched my head for two days but still cannot figure out why the recursion is not working...
I am new to protractor. It will be great if I can get some hints!! Thank you very much.
Related
I'd like to move the focus to the next cell of my Jupyter notebook. I've written a Javascript function that is fired when a user presses an HTML button. I added this button to my notebook by making use of functionality from IPython.core.display.HTML. This is the HTML part for the button: button onclick="set_params();" [in html brackets]. In the function - yes, with name set_params - I prepare a command that should be executed by the Python kernel. I establish that this is actually done when the button is pressed. Afterwards I want the cursor to move to the next cell - and that is not happening unfortunately. Here is part of my Javascript function:
var command = 'input_params = [' + names + ']';
IPython.notebook.kernel.execute(command);
i = IPython.notebook.get_selected_index();
IPython.notebook.select(i+1);
I even checked with some extra code whether the cell with index i+1 is really a cell and it is. Why is the focus not moving?
If you import Javascript:
from IPython.display import Javascript
You can do something like:
def goto_cell_two():
display(Javascript("Jupyter.notebook.execute_cells([2])"))
go_to_cell_two()
Cells start with index 0
A few million years later ....
I end up calling 3 javascript functions consequently, namely select_next(), focus_cell(), and execute_selected_cells(). All these calls can be combined in one procedure in the first cell, and then call this function every time you want to jump into the next cell.
def run_next(ev):
display(Javascript('IPython.notebook.select_next()'))
display(Javascript('IPython.notebook.focus_cell()'))
display(Javascript('IPython.notebook.execute_selected_cells()'))
So in the "linear" part of a code without any input from the user, this function is called at the end of each cell
run_next(None)
However, in interface cells, this function should be called only when the user selects/accepts parameters and these parameters are correct.
recacceptbtn = widgets.Button(
description='Accept',
disabled=False,
button_style='success', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Click to accept',
icon='check-square'
)
def rec_check_update_runnext(evn):
# testing parameters
if recfile.value is not None and recprobe.value is not None:
# saving parameters
run_next(evn)
else:
display(Markdown('**You must select file and probe!**'))
recacceptbtn.on_click(rec_check_update_runnext)
I haven't tested this approach extensively, but it works so far and works pretty well.
We are defining variables from the elements on one page of the website, clicking on the edit button, which is opening the next page. In this page we need to assert that the data captured on the earlier page matches the data shown on the 2nd page. Our problem is, once the test moves to the 2nd page, it fails to recall the variables that we defined on the 1st page. below is our code snippets:
it ('Student ID Validation', function(){
// get rows
var rows = tableData_Dashboard.all(by.tagName("tr"));
// get cell values
var cells = rows.all(by.tagName("td"));
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
});
Edit_Button_1.click();
browser.sleep(2000);
expect(Student_ID_on_Reg_Page.getAttribute('value')).toEqual(Student_ID);
after execution, we get the following error
Message:
Expected '123456' to equal undefined.
We were suspecting that it may be due to asynchronization, but that is not the case. the test moves to page 2 after it stores the variable from page 1, so we are at a loss why this is happening. How can we fix this and use the variables for assertion purpose?
The problem is that you've specified the then() callback where you just log the value but don't return it:
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
});
As nothing is returned, Student_ID would become a promise which would resolve into undefined.
You either need a return:
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
return SID;
});
Or, remove the custom callback completely:
var Student_ID = cells.get(0).getText();
actually, the following part is causing the problem. Once we removed this part, the test is working fine.
.then(function(SID){
console.log(SID);
});
I'm trying to realize a loop with "while' construction for scrolling down a table with loading dynamic data until some text like "n of N items displayed
(scroll for more)" changes to text like "N of N items displayed" in label element. New data are loaded by blocks in 30 rows scrolling to the bottom of table. I'm trying to realize a loop:
var Needed_text = 'needed_text';
var rows = element.all(by.xpath('table'));
var text = element(by.xpath('element').getText();
while(expect((text).not.toEqual(Needed_text)){
last_row = rows.last();
row_count_number = rows.count(rows);
browser.actions().mouseMove(last_row).perform();
browser.sleep(3000);
text = element(by.xpath('element').getText();
});
All a time when I'm trying to realize something like that I face error :
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap
out of memory
Trying to fix this bug with changing parameters max_old_space and etc. didn't help me. How can i rebuild this loop for scrolling?
First of all you need to declare a boolean variable that will change once you get the desired label. And then, you look for that element inside the while loop, but in order to check the desired text you will have to resolve the promise. Something like this:
var bool = true;
while (bool) {
element(by.xpath('element').getText().then(function(resolvedText){
if (resolvedText == NeededText){
do all you need here;
bool = false;
}
})
}
if your logic is correct below code should work.
getText() method returns a promise and it needs to be resolved .
element(by.xpath('element').getText().then(function(text){
while(expect((text).not.toEqual(Needed_text)){
last_row = rows.last();
row_count_number = rows.count(rows);
browser.actions().mouseMove(last_row).perform();
browser.sleep(3000);
text = element(by.xpath('element').getText();
});
});
I have a jsTree with lazy loading. I want to be able to select the next and previous nodes using an external function. The api suggests tree.get_next_dom() but this only seem to work on nodes that have already been loaded, not ones that may require loading first.
$("[href='#next']").click(function () {
var tree = $('#container').jstree(true),
curr = tree.get_selected(false);
tree.deselect_all();
var n = tree.get_next_dom(curr);
tree.select_node(n);
});
see full fiddle for a example showing the lazy load ..
http://jsfiddle.net/6L7twnka/3/
So "next" would see if the node had children and then expand it, loading if required. Previous would have to look at get_prev_dom and then expand it and then possibly expand all the last nodes inside it (recursively) in order to then put the selection on the most immediately previous node. I got a bit lost trying to do this.
Maybe I've already solved, but I leave here a suggestion.
After the method call "tree.deselect_all ();" you invoke the "tree.open_all ()" method.
$ ("[href = '# next']"). click (function () {
var tree = $ ('# container'). jstree (true)
curr = tree.get_selected (false);
tree.deselect_all ();
tree.open_all ();
var n = tree.get_next_dom (curr);
tree.select_node (n);
});
Sincerely,
Charles Oliveira
I'm writing cucumber feature specs using capybara, with poltergeist as the Javascript driver.
In one scenario, a step asks capybara to fill in a number field:
When /^I set the group count to (\d+)$/ do |group_count|
within 'form#new_practitioner_activity' do
find('input#number_of_groups').set group_count
end
end
For some reason, at that step a request gets generated (I've used Poltergeist's page.driver.network_traffic object to check it out) and the whole scenario is derailed as the request is redirected to the welcome page.
Changing the value of the field does trigger some Javascript (that's the point of writing the scenario to test it), but when I run in Chrome and watch the network panel, I don't see any requests there, and the JS code isn't intended to make any calls back to the server. (The script picks up a nested form, clones it, and appends it, if that matters.)
Here's the Coffeescript relevant to the field change. I'm eliding a good bit of each of these classes to try to clarify it.
class GroupCountElement
constructor: (#controller, #form) ->
# On instantiation we set up instance attributes for a div, and an input
# contained in it
#input = #form.find('.input.group-count')
#inputField = #input.find('input#number_of_groups')
#form.on('change', 'input#number_of_groups', #countChanged)
countChanged: =>
#form.trigger('groupcount:changed')
val: (newValue) =>
# This method just lets us treat the object like an input element
if newValue?
#inputField.val(newValue)
else
#inputField.val()
Here's what #form does with that groupcount:changed event:
class Form
constructor: (#controller, #form, #academicYearId) ->
# ... other setup stuff...
#groupInputs = []
#form.find('.group-activity').each (i, row) =>
#groupInputs.push(new GroupActivity(#controller, #form, row))
#groupCount = new GroupCountElement(#controller, #form)
#form.on('groupcount:changed', #groupCountChanged)
groupCountChanged: (e) =>
delta = #groupCount.val() - #groupInputs.length
if delta > 0
#groupInputs.push(new GroupActivity(#controller, #form)) for i in [1..Math.min(delta, 10-#groupInputs.length)]
else if delta < 0
#groupInputs.pop().delete() for i in [Math.max(delta, 1-#groupInputs.length)..-1] unless #groupInputs.length is 1
Then this is the GroupActivity class which is having instances of itself pushed and popped on the Form:
class GroupActivity
constructor: (#controller, #form, #groupForm) ->
if #groupForm?
# This means we're creating an instance reflecting DOM already in the form
#$groupForm = $(#groupForm)
else
# This means we're cloning an existing chunk of DOM - a row - and adding
# it to the form
#$groupForm = #form.find('.group-activity').last().clone()
# Because the nested form elements we just cloned have names
# and IDs with an array index, our new row needs to have those
# attributes updated with the index +1. #incrementFormAttrs()
# handles that.
#incrementFormAttrs()
#append() # Sticks the cloned DOM into the form
#reset() # Clears any values in the cloned DOM
incrementFormAttrs: =>
oldIndex = #$groupForm.find('.input.instructional-focus label').attr('for').match(/practitioner_activity_group_activities_attributes_(\d+)/)[1]
newIndex = parseInt(oldIndex, 10) + 1
#$groupForm.find('label').attr 'for', (i, val) ->
val.replace(/practitioner_activity_group_activities_attributes_(\d+)/, "practitioner_activity_group_activities_attributes_#{newIndex}")
#$groupForm.find('select, input').attr
id: (i, val) -> val.replace(/practitioner_activity_group_activities_attributes_(\d+)/, "practitioner_activity_group_activities_attributes_#{newIndex}")
name: (i, val) -> val.replace(/practitioner_activity\[group_activities_attributes\]\[(\d+)\]/, "practitioner_activity[group_activities_attributes][#{newIndex}]")
append: =>
#$groupForm.insertBefore( #form.find('.actions') )
reset: =>
if #$groupForm
new Deselector(#$groupForm.find('select'))
#$groupForm.find('input').val(undefined)
delete: =>
#$groupForm.remove()
So, to trace it out: the test changes the value of the #inputField in the GroupCountElement. That sends a groupcount:changed event to the Form, which calls its groupCountChanged() method. In this case the number is most likely to go UP, so we're adding at least one GroupActivity instance, which in its constructor will call incrementFormAttrs(), append(), and reset().
None of these should trigger a request as I understand them. What could be kicking off this extra request which is breaking my test?