Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectors improvements #73

Closed
sergey-tihon opened this issue Apr 20, 2013 · 5 comments
Closed

Selectors improvements #73

sergey-tihon opened this issue Apr 20, 2013 · 5 comments

Comments

@sergey-tihon
Copy link
Collaborator

It will be nice to have more powerful css-selectors in canopy

Sub-element selectors.

Something like this:

cssSelector:string -> element:IWebElement ->IWebElement list

There are very useful if you need to make search into DOM sub-tree.

Option as result for select-one selectors (like element)

Expected behavior is to get

  • Some(IWebElement) if exactly one element exists
  • None if there is no such elements
  • an exception in other cases.
@lefthandedgoat
Copy link
Owner

Hi Sergey, thanks for opening a ticket!

For the first part you would want to see something like:

let subElement = elementWithin (element "#someSection") ".subElement"

elementWithin being the new method with the signature similar to what you provided?

let subElements = elementsWithin (element "#someSection") ".subElement"
for list of elements


For the second part something like this?

let e = someElement ".selector"

Currently 'element' will try over and over to find an element up to the aloted timeout before failing (mainly useful for ajaxy sites). Would someElement do the same or be fast, returning None if nothing exists?
Under what cases do you think it should throw an exception?

@sergey-tihon
Copy link
Collaborator Author

Yes, you are right for the first part: elementWithin and elementsWithin will be fine.

let elementsWithin cssSelector (element:IWebElement) = 
    element.FindElements(By.CssSelector(cssSelector))

to be able use it in pipelining

root |> elementWithin "a" |> getHref
root |> elementWithin "p.title" |> getText

For the second part will be nice to have two methods: someElement, someElementWithin.

I think it should try over and over up to timeout (to be sure that such element really does not exist).

Now I use the following code:

let someElement s =
    match (s |> elements) with
    | [x] -> Some(x)
    | [] -> None
    | _ -> failwith "Selected too much elements"

It fails when user expects for one element, but in fact there are more. (I think, it means that script is wrong and we have to tell the user about this)

Current implementation of element function throw an exception in the case if element does not found. In such implementation is not very convenient to write the conditions for the existence of an element. It is may be better to rename element function to the firstElement to better understand what it is actually do.


One more useful selector is a parentElement function

let parentElement (element:IWebElement) =
    element.FindElement(By.XPath(".."))

or even maybe something more generic

let elementByXPath xPath (element:IWebElement) =
    element.FindElement(By.XPath(xPath))

@sergey-tihon
Copy link
Collaborator Author

According to my current understanding, that it will be as follows:

let elementsWithin cssSelector (element:IWebElement) = 
    element.FindElements(By.CssSelector(cssSelector)) |> Seq.toList


let private someElementFromList elementsList =
    match elementsList with
    | [x] -> Some(x)
    | [] -> None
    | _ -> failwith "Selected too much elements"

let someElement cssSelector =
    cssSelector |> elements |> someElementFromList

let someElementWithin cssSelector element =
    element |> elementsWithin cssSelector |> someElementFromList


let elementsByXPath xPath (element:IWebElement) =
    element.FindElements(By.XPath(xPath)) |> Seq.toList

let someElementByXPath xPath element =
    element |> elementsByXPath xPath |> someElementFromList

let parentElement element =
    element |> someElementByXPath ".." |> Option.get

I think that I do not need elementWithin because I do not use element function (I do not want to handle exceptions). someElementWithin will be enough.

@lefthandedgoat
Copy link
Owner

Very cool, I will need to build in retry-ability into elementsWithin which may take some time. I will try to have a new build with these additions today. Thanks!

@lefthandedgoat
Copy link
Owner

Sergey,

canopy 0.7.3 is released and pushed to nuget. I will have the documentation updated by end of day tomorrow. If you need to see examples before then you can look into the commits linked to this issue.

I didn't add the xpath version of the methods, instead I added implicit xpath searching which should work with all existing operations.
ie:
"/some/xpath/query" << "some value"
"/some/xpath/query" == "some value"
let results = elements "xpath/query"
etc.

Thanks for the suggestions, let me know if something is not working as you expected or have more suggestions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants