I want to traverse table with Selenium using Node and webdriverJS:
<table>
<tr>
<td class="name">Peter</td>
<td class="count">1</td>
</tr>
<tr>
<td class="name">John</td>
<td class="count">3</td>
</tr>
</table>
I want for every row to look at the names and the rows cells.
What I have:
driver.findElements(By.tagName('tr')).then(function(rows){
// for every row
for (var i = 0; i< rows.length; i++){
// check the name cell
rows[i].findElement(By.class('name')).getInnerHtml().then(function(name){
// do some stuff
});
// check the count cell
rows[i].findElement(By.class('count')).getInnerHtml().then(function(count){
// do some stuff
});
}
});
This works for the first some rows, but with many rows it fails at a certain point.
My theory: the findElement calls in the for-loop are passed to the manager, then the for-loop finishes. Then the garbage collector removes the rows array. Once the manager executes the the findElement calls, the array and its elements do not exist anymore and fail. The error I get is:
StaleElementReferenceException : The Element is not Attached to the DOM
It does work for the first row as the array still exists early-on in the execution.
My questions:
what am I doing wrong?
Is my theory correct?
How can I bind the row[i] references to the findElement calls for them to persist longer than the original array?
---- Edit ----
When I remove one of the inner findElement calls and only look for one cell per row, I am able to cover more rows. This made me think that, with this implementation, time plays a role. This should not be the case, so I am doing probably something wrong.
Is there anything like a forEach function in Selenium?
I found the problem:
I am using a website implemented with Sencha EXTjs.
The table is created on top of a data store: Apparently, the store is called twice and the whole table is recreated in-between the calls.
So I somehow have to wait until the table has loaded for the second time...
This will be the next challenge.
Related
Actually i want to read emails one by one in junk folder of "outlook:live" and mark emails "Not spam".
emails = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.XPATH,"//div[#class = 'xoCOIP8PzdTVy0T6q_uG6']")))
This xpath matches 400 instances. I want to make a loop to select one email at a time like select first email, click on the div and perform action and then 2nd email and so on. I'm trying this
emails = WebDriverWait(driver,
5).until(EC.element_to_be_clickable((By.XPATH,"//div[#class =
'xoCOIP8PzdTVy0T6q_uG6']")))
for count in range(0,len(emails)):
(emails)[count+1].click()
Please help me know where im doing wrong. Thanks in advance
It appears that the function you're using to return the clickable elements is only returning a single element, so you'll have to use a different function, make a change in your logic, etc.
For instance, you could use Selenium's find_elements_by_xpath("//div[#class = 'xoCOIP8PzdTVy0T6q_uG6']") which will return a list of WebElement object(s) if the element(s) are found, or an empty list if the element(s) is not found. This will, of course, not take into consideration the possibility of the elements not being completely loaded on the page. In my experience, just slapping a time.sleep(10) after you open the page is "'good enough".
I recommend making sure your elements can be discovered and interacted with first to make sure this isn't all in vain, if you haven't already.
Another option is to add another function, something like a elements_to_be_clickable() function, to the Expected Conditions source code.
From the Expected Condition documentation, I've done some research and it looks like the element_to_be_clickable() function only returns a single element. Moreover, from the source code, said function mainly makes use of the visibility_of_element_located() function. I believe you could follow similar logic to the element_to_be_clickable() function, but instead use the visibility_of_all_elements_located() function, in order to return multiple WebElements (since visibiilty_of_all_elements_located() returns a list of WebElements).
In ReactJS, I get the point of SRP. But I think I'm thinking about it the wrong way. My problem is I don't know when to stop with regards to SRP. Like for the example in Thinking in React. I could easily say that the ProductRow component could be further be broken down to this:
ProductRow - displays a row for each product
ProductRowName - displays the name for each product row
ProductRowPrice - displays the price for each product row
My question now is, how do you know when enough is enough for SRP? What's your cue? I'm hoping to pick up how react devs think so I could apply it on my codes.
Functionality first.
The great thing about react is how it handles reusability of your code. You want to take advantage of that as much as possible. And to best do that, I think both the example and your changes to it fall short of the real goal.
What is the functionality of ProductRow? It's meant to take in an object and then output a table row with information from that object.
If we define exactly what the object is, or exactly which pieces of the object should be output, then we've boxed ourselves into a corner. ProductRow is now only usable for product objects, and requires that those objects have a name and price attribute.
Can we strip some of that specificity out? Can we make a component that has a single responsibility, but without dictating the shape of it's input?
Sure!
var Row = React.createClass({
render: function() {
var item = this.props.item;
var columnNames = this.props.columnNames;
return (
<tr>
{columnNames.map(function(columnName){
return <td> {item[columnName]} </td>;
})}
</tr>
);
}
});
//USAGE: <Row item={product} columnNames={['name', 'price']} />
Now the failing here is that we no longer have unique formatting per column (red name depending on stock status). But that is where we actually reach an issue for SRP. It's time to make a second component, who's responsibility is to create a color map, based on conditions of the input.
I use AngularJS ng-repeat in order to view my table elements (it shouldn't be used very often - I know - but I don't know how to do it in an other way)
Here my example how I'am showing the containerObjects in table:
http://jsfiddle.net/NfPcH/10390/
ng-repeat=...
I have a lot containedObjects (with start, end and containerType) (around 600 per page) which are shown in table.
It took about 3 Seconds to show the view.
My question now would be, if something can be improved in order to improve performance. Is there a possibility to replace/change ng-repeat to decrease loading time.
Thanks a lot!
[EDIT]
I also have this function invocation but I have no idea how to prevent the function invocation. Does have anyone any idea how I could improve this?
Thanks a lot !
ng-repeat="serviceSchedule in getServiceScheduler(institutionUserConnection)">
function getServiceScheduler(institutionUserConnection) {
if(institutionUserConnection.scheduler != null) {
var serviceSchedules = institutionUserConnection.scheduler.serviceSchedules;
return serviceSchedules[Object.keys(serviceSchedules)[0]];
} else {
return null;
}
}
Improvement #1:
As #Petr Averyanov suggested, use a variable instead of a function call.
instead of:
ng-repeat="serviceSchedule in getServiceScheduler(institutionUserConnection)"
change your logic to:
ng-repeat="serviceSchedule in preCalculatedServicesObjectInScope"
To do this, you need other logic changes in controller, you have to manage when the result of getServiceScheduler(institutionUserConnection)" actually changes yourself but it WILL increase the performance.
Improvement #2:
Use variables that are evaluated once if values of rows never change once they are rendered. This will reduce watchers drastically and help you improve overall performance:
```html
<tr ng-repeat="institutionUserConnection in someScopeVariable">
<td>{{ ::institutionUserConnection.user.firstname }} {{ ::institutionUserConnection.user.lastname }}</td>
</tr>```
Improvement #3:
Use track by statement in ng-repeat. Angular.js normally checks/tries to identify which object is which using a special hash function $id in ng-repeat. However, you naturally have an identifier for each object in array (and you should) you can make ng-repeat use this instead of creating it's own id. For example:
<tr ng-repeat="x in someScopeVariable track by x.id">
Improvement #4: (getting hackier here)
Whatever you do, since your array is large, you may not be able to increase the performance enough. Then you should ask this question, do people really see 600 items in one look? No, it won't fit to page. So, you can chop the part that doesn't fit the page, reducing DOM elements to be rendered at once. To do that you can use limitTo filter:
<tr ng-repeat="x in someScopeVariable | limitTo: visual.limitValue">
You can make visual.limitValue a reasonable count such as 10 in the beginning and you can increase it on table's scroll to increase it when use is about to reach the bottom, resulting partial appending of DOM to page. However this hack requires both style and code changes in the page.
Improvement #5: (hackiest one, but it works)
If dimensions of each row is constant, using scroll position and dimension of rows, you can calculate which rows should be visible and you can render them only. I've written a directive for practice, named it uber-repeat and tested it with 60.000 rows and performance was amazing, even in mobile phones!
I think there is a project uses somewhat same approach here: angular-vs-repeat
I have the following code
<table>
<thead><td>Id</td><td>Name</td><td>Ratings</td></thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td><div ng-repeat="item in items">{{item.rating}}</div></td>
</tr>
</tbody>
</table>
users is an array of user objects with only id and name. number of user objects in array - 150
items is an array of item objects with only id and rating. number of item objects in array - 150
When i render this in browser, it takes about 250MB of heap memory when i tried profiling in my chrome - v23.0.1271.95.
I am using AngularJS v1.0.3.
Is there an issue with angular or am i doing anything wrong here?
Here is the JS fiddle
http://jsfiddle.net/JSWorld/WqSGR/5/
Well it's not the ng-repeat per se. I think it's the fact that you are adding bindings with the {{item.rating}}.
All those bindings register watches on the scope so:
150 * 2 = 300(for the 2 user infos)
150 * 150 = 22500(for the rating info)
Total of 22800 watch functions + 22800 dom elements.
That would push the memory to a conceivable value of 250MB
From Databinding in angularjs
You can't really show more than about 2000 pieces of information to a
human on a single page. Anything more than that is really bad UI, and
humans can't process this anyway.
I want to say the leak is in the second array because you are potentially looping through the same array and displaying every item for every user row in users so depending on how large your test data is that view could get rather large. I could do a little more investigating. btw your fiddle is something entirely different.
Right now you are looping through 150 X 150 = 22500 items. And registering a watch (or through a directive just adding item rating) to each one.
Instead - consider adding the user's rating to the user object itself. It will increase the size of each user object but you will only loop through 150 items and register watches only on them.
Also - consider looking into Indexes. It's apparent that there could be similar users or item ratings. Just index them, so instead of looping through heavy objects, you can reduce them.
One more thing - if you are going to be running the directive the same instance, at least change the code:
var text = myTemplate.replace("{{rating}}",myItem.rating);
to a concat style string calculation:
var text = '<div>' + myItem.rating + '</div>';
This will save you a HUGE chunk on calculation. I've made a JSperf for this case, notice the difference, it's about 99% faster ;-)
The documentation for the ui:repeat tag in JSF 2.0 says you can iterate over a ResultSet but my code:
<ui:repeat value="#{bean.resultSet}" var="row" varStatus="status">
#{row.string("mySQLColumn")}
</ui:repeat>
produces this error:
javax.faces.FacesException: Iteration start index is greater than the number of available rows.
at com.sun.faces.facelets.component.UIRepeat.validateIterationControlValues(UIRepeat.java:682)
at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:505)
at com.sun.faces.facelets.component.UIRepeat.encodeChildren(UIRepeat.java:974)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1756)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:168)
Even though it seems to be supported as argument for iteration, it would be best to first transform it to a List and then iterate.
That way you won't be propagating the database-access technology to the view layer.