everyone!
I'm stuck with following problem:
There is some
SearchContext searchContext;
By by;
which could be WebDriver or WebElement.
Assume that both of them are already initialized (and we don't know how);
Now we want to find elements with such xpath, seems to do following
List<WebElement> elements = searchContext.findElements(by);
But, if searchContext is WebElement and
by = By.xpath("//div");
it wouldn't work! (no elements would be found), because we need to do
by = By.xpath("./div");
(see Locating child nodes of WebElements in selenium)
But, how i've mentioned, we know nothing about how by was initialized;
So, my question is: is there any way to find elements properly despite the problems above?
I've got two variables: by and searchContext, and I should to find specified element in searchContext.
You can do it in some helper method which will throw Exception if this happens
public List<WebElement> returnSearchContext(By by, SearchContext searchContext){
List<WebElement> elements = searchContext.findElements(by);
if (elements.getSize()>0){
return elements;}
else{
throw new ElementNotFoundException();
}
}
I am writing this without access to any IDE, so I might do some errors in my example code. For instance, I think that the exception will need some parameters. But I hope you get the idea.
As I understand, there is now way to do it.
Only way to do it - is manually specify By.xpath properly:
By child = By.xpath("/div");
By descendant = By.xpath("//div");
in case of WebDriver.isAssignableFrom(searchCOntext.getCLass()),
and
By child = By.xpath("div");
By descendant = By.xpath(".//div");
in case of WebElement.isAssignableFrom(searchCOntext.getCLass()).
IMHO, it's bad.
Related
The problem is to sort all child divs of a root node according to their top CSS property.
Here is my code:
val elements = global.document.getElementById("root").childNodes.asInstanceOf[dom.NodeList]
val clones = (for (i <- (0 to (elements.length - 1)) if (elements(i).isInstanceOf[dom.raw.HTMLDivElement])) yield {
val clone = elements(i).cloneNode(true)
val style = clone.attributes.getNamedItem("style").value
val parts = style.split("top: ")
val parts2 = parts(1).split("px")
val px = parts2(0).toDouble
Tuple2[dom.Node, Double](clone, px)
}).toList
val sorted = clones.sortWith((a, b) => a._2 > b._2)
global.document.getElementById("root").innerHTML = ""
for (e <- sorted) {
global.document.getElementById("root").appendChild(e._1)
}
I'm new to Scala.js and it took quite an effort to come up with this solution. It compiles and seems to work, however I'm not sure how legitimate it is.
For example I can only get the top property of the node in a very complicated way. Also I suspect that for deleting all child nodes global.document.getElementById("root").innerHTML = "" is a backdoor way. I'm not sure if this sorting can be done in place without creating clones. I welcome any suggestions for improvement and I hope that some beginner out there may find even this code useful.
Various suggestions, some pertaining to Scala and some to the underlying browser environment:
First, jQuery (actual JavaScript library) (Scala.js facade) is your friend. Trying to do anything with the raw DOM is a pain in the ass, and I don't recommend it for anything but the simplest toy applications. (This has nothing to do with Scala.js, mind -- that's just the reality of working in the browser, and is all true of JavaScript as well.)
Using jQuery, getting the elements is just:
val elements = $("root").children
Second, essentially nobody loops using indexes in Scala like that -- it's legal, but extremely rare. Instead, you get each element directly in the for. And you can stick the value assignments right into the for itself, keeping the yield clause clean.
jQuery lets you get at CSS properties directly. (Although I think you still have to parse out the "px".) Again, everything is much harder if you try to use the raw DOM functions.
And it's very rare to spell out Tuple2 -- you just use parens for a tuple. Putting it all together, it would look something like this:
for {
element <- elements
if (element.isInstanceOf[dom.raw.HTMLDivElement])
clone = element.clone()
top = clone.css("top")
px = top.dropRight(2).toDouble
}
yield (clone, px)
Mind, I haven't actually tried out the above code -- there are probably some bugs -- but that's more like what idiomatic Scala.js + jQuery code would look like, and is worth considering as a starting point.
I need your help with following issue:
In my code I have a list of elements, I need to sort this list according to 2 attributes: season and number.
List example:
<episode_list>
<episode id="280" number="13" season="1">
<title><![CDATA[Bowl Game]]></title>
</episode>
<episode id="314" number="12" season="1">
<title><![CDATA[Piss Test]]></title>
</episode>
<episode id="730" number="11" season="1">
I use Collections.sort(), but getting exception. As I understand I can't use it with JDOM Elements:
List<Element> episodes;
Collections.sort(episodes, new Comparator<Element>() {
#Override
public int compare(Element elem1, Element elem2) {
Integer seasonNumber1 = Integer.valueOf(myService.valueOfAttribute("season", elem1));
Integer seasonNumber2 = Integer.valueOf(myService.valueOfAttribute("season", elem2));
int seasonComp = seasonNumber1.compareTo(seasonNumber2);
if (seasonComp != 0) {
return seasonComp;
} else {
Integer episodeNumber1 = Integer.valueOf(myService.valueOfAttribute("number", elem1));
Integer episodeNumber2 = Integer.valueOf(myService.valueOfAttribute("number", elem2));
return episodeNumber1.compareTo(episodeNumber2);
}
}
});
Exception: java.util.Collections$UnmodifiableList$1.set(Unknown Source)
java.util.Collections.sort(Unknown Source)
Actually I don't need sorted xml, the only thing I need is the episode attribute "id" (for the lowest season and the lowest episode number).
What could you recommend? I have another implementation, where I go through all elements, but I don't think it's a nice solution...I also can create Java class Episode(id, episode, season), transform List to List and sort it, but also don't think it's a good idea. There is also sortContent method for Element, but I'm not sure how to implement it.
I'll appreciate any help.
Content attached to JDOM Elements cannot be sorted using the standard Collections.sort() mechanism because that process does not honour the only-attached-at-one-place-at-a-time rule for XML content.
JDOM has sort() methods built in to the Element class that allows you to sort the chile Elements or other Child content: See The Element.sortChildren() Javadoc for the JDOM way to do it.
Update: Also, for your reference, the error you are getting is because at some point you created an unmodifiable version of the List.... this is not something that happens from JDOM method calls. The error you are getting is because you are trying to modify a List that has been intentionally made read-only.
What's wrong with going through the list and finding the minimum. It is O(n), while sorting is O(n*log(n)). You might use a generic min function, such as the one in guava
Element firstEpisode = Ordering.from(your-comparator).min(episodes.iterator());
If you really want to sort it, why don't you sort new ArrayList<Element>(episodes) (I agree with rolfl that you cannot use Collections.sort for JDOM lists and that the error comes from your use of unmodifiable list).
I running into some performance issues with a jquery script I wrote when running in IE so I'm going through it trying to optimize any way possible. Apparently using for loops is way faster than using the jQuery .each method. This has led me to a question regarding the equivalent of $(this) inside a for loop. I'm simplifying what I'm doing in my loop down to just using an attr() function as it gets across my main underlying question.
Im doing this with each(simplified)
var existing = $('#existing').find('div');
existing.each(function(){
console.log($(this).attr('id'));
});
And I've tried rewriting it as a for loop as such:
var existing = $('#existing').find('div');
for(var i = 0;i < existing.length;i++)
{
console.log(existing[i].attr('id'));
}
Its throwing an error saying:
Uncaught TypeError: Object #<HTMLDivElement> has no method 'attr'
You need existing.eq() to get jQuery object, existing[] gives you DOM object. The function attr() should be called with jQuery object but not with DOM (javascript) object.
var existing = $('#existing');
for(var i = 0;i < existing.length;i++)
{
console.log(existing.eq(i).attr('id'));
}
You can use each to get index without for loop.
existing.each(function(index, item){
alert(index);
alert(item);
});
To get the id of an element just do
existing[i].id
Note that you jQuery loop would also be faster as
existing.each(function(){
console.log(this.id);
});
More generally, you should not use attr('id'), especially if you're concerned by performances, as a DOM object has a property id.
.I have to ask you a question before I give my answer, why would you need to perform a loop on a single element, #existing is an Id not, therefore it's a unique element on your page.
you could simply do
$('#existing').prop('id');
In case your have more than one elements, you should be using a class or another attribute, if that is the case, you could use the following:
$.each('.existing',function(index,item){
console.log(item.prop('id'));
});
better use prop() insted of attr() as attr is deprecated
i'm just starting prototype, i was on jquery before.
I can't find easy examples on the internet about how :
Selecting all elements having the same id on a page
(I'm doing this but it only works for the first element : $('mydiv').hide() )
Selecting a div that is contained in another div by their id.
hiding all elements that have myClass class.
As mentioned above you shouldn't have the same ID on a page more then once. Besides being against standards it's a recipe for potential problems since you don't know how your JavaScript will react to it. Uses classes instead.
Selecting all elements having the same
id class on a page (i'm doing this but it
only works for the first element :
$('mydiv').hide() )
Use $$:
$$('.myclass')
Selecting a div that is contained in
another div by their id.
Use $$:
$$('div#outer div#inner')
hiding all elements that have myClass
class.
Use $$, each(), and hide()
$$('.myClass').each(function(d) {
d.hide();
});
$$ is your friend.
A few things i would add.
$$('.myClass').each(function(d) {
d.hide();
});
can be replaced with this:
$$('.myClass').invoke("hide");
Also, be careful with your use of $$, within a page with a large dom it is usually faster to target a parent element with $ and then use select() for your selector
so
$$('div#outer div#inner') etc....
can be rewritten like this:
$('parent_of_inner_and_outer').select('div#outer div#inner') etc....
This isn't particularly pretty, but if you run into a situation like I did recently, where there could potentially be multiple items with the same id on the page and you don't have the ability to change that, then something like this will work. In my case, I at least knew they were all in span tags.
var spans = $$('span');
for (i = 0; i < spans.length; i++) {
var span = spans[i];
if (span.id == 'add_bookmark_image_' + id) {
span.hide();
}
if (span.id == 'is_bookmarked_image_' + id) {
span.show();
}
}
So I have an abstract data type called RegionModel with a series of values (Region), each mapped to an index. It's possible to remove a number of regions by calling:
regionModel.removeRegions(index, numberOfRegionsToRemove);
My question is what's the best way to handle a call when the index is valid :
(between 0 (inclusive) and the number of Regions in the model (exclusive))
but the numberOfRegionsToRemove is invalid:
(index + regionsToRemove > the number of regions in the model)
Is it best to throw an exception like IllegalArgumentException or just to remove as many Regions as I can (all the regions from index to the end of the model)?
Sub-question: if I throw an exception what's the recommended way to unit test that the call threw the exception and left the model untouched (I'm using Java and JUnit here but I guess this isn't a Java specific question).
Typically, for structures like this, you have a remove method which takes an index and if that index is outside the bounds of the items in the structure, an exception is thrown.
That being said, you should be consistent with whatever that remove method that takes a single index does. If it simply ignores incorrect indexes, then ignore it if your range exceeds (or even starts before) the indexes of the items in your structure.
I agree with Mitchel and casperOne -- an Exception makes the most sense.
As far as unit testing is concerned, JUnit4 allows you to exceptions directly:
http://www.ibm.com/developerworks/java/library/j-junit4.html
You would need only to pass parameters which are guaranteed to cause the exception, and add the correct annotation (#Test(expected=IllegalArgumentException.class)) to the JUnit test method.
Edit: As Tom Martin mentioned, JUnit 4 is a decent-sized step away from JUnit 3. It is, however, possible to also test exceptions using JUnit 3. It's just not as easy.
One of the ways I've tested exceptions is by using a try/catch block within the class itself, and embedding Assert statements within it.
Here's a simple example -- it's not complete (e.g. regionModel is assumed to be instantiated), but it should get the idea across:
public void testRemoveRegionsInvalidInputs() {
int originalSize = regionModel.length();
int index = 0;
int numberOfRegionsToRemove = 1,000; // > than regionModel's current size
try {
regionModel.removeRegions(index, numberOfRegionsToRemove);
// Since the exception will immediately go into the 'catch' block this code will only run if the IllegalArgumentException wasn't thrown
Assert.assertTrue("Exception not Thrown!", false);
}
catch (IllegalArgumentException e) {
Assert.assertTrue("Exception thrown, but regionModel was modified", regionModel.length() == originalSize);
}
catch (Exception e) {
Assert.assertTrue("Incorrect exception thrown", false);
}
}
I would say that an argument such as illegalArgumentException would be the best way to go here. If the calling code was not passing a workable value, you wouldn't necessarily want to trust that they really wanted to remove what it had them do.