/// <summary> /// This method evaluates if one IWebElement contains a child selected by the predicate. It can be used to select /// one container element based on its child elements. /// </summary> /// <param name="element">The container element to be evaludated.</param> /// <param name="childEnum">Enum Identifier of the child element, which carries the CSS selector.</param> /// <param name="predicate">Function to see if the child element meets predefined criteria.</param> /// <returns>"true": The container IWebElement contains the expected child IWebElement, otherwise "false".</returns> public static bool HasChildOf(this IWebElement element, Enum childEnum, Func <IWebElement, bool> predicate) { string childLocatorCss = childEnum.Css(); IWebElement child = null; try { //child = GenericWait<IWebElement>.Until(() => // element.FindElementByCss(childLocatorCss), null, 1000); ReadOnlyCollection <IWebElement> candidates = null; candidates = GenericWait <ReadOnlyCollection <IWebElement> > .Until( () => candidates = element.FindElementsByCss(childLocatorCss), x => x.Count != 0, 200 ); var qualified = candidates.Where(predicate).ToList(); bool result = qualified.Count != 0; return(result); } catch (Exception) { return(false); } }
/// <summary> /// Assuming Elements can be found with CSS selector within time specified by waitInMills, keep executing the /// FindElementsByCssSelector() of either Driver or a specific IWebElement until timeout. /// </summary> /// <param name="findFunc">The FindElementsByCssSelector() of either Driver or a specific IWebElement.</param> /// <param name="css">CSS Selector string.</param> /// <param name="waitInMills">Maximum time to wait when the findFunc returns 0-length collection.</param> /// <returns>The non-empty collection immediately or empty collection after time is out.</returns> public ReadOnlyCollection <IWebElement> WaitToFindElements(Func <string, ReadOnlyCollection <IWebElement> > findFunc, string css, int waitInMills) { return(GenericWait <ReadOnlyCollection <IWebElement> > .Until( () => findFunc(css), collction => collction.Count != 0, waitInMills )); }
//Stores the last calling of FindElement() to avoid InvalidElementStateException or StaleElementReferenceException. //private long lastTick = Int64.MinValue; #endregion /// <summary> /// Retrieve the IWebElement based on the CSS associated with the Enum Identifier, and optional /// the filters when Enum Identifier selects a collection. /// </summary> /// <param name="filters"> /// ByIndex to select one IWebElement from a collection of them, would be used by this Locator and its parents. /// For Locator of a single IWebElement whose IsCollection=="false", filters is always neglected. /// For Locator of multiple IWebElements whose IsCollection=="true", /// filters is used to choose from a Collection returned by FindElementsByCssSelector(). /// Noticeably, the filters is sticky. That is, if one Locator uses a filters, then it and its parent would store /// it for following calling of FindElement(null). /// The defintion of filters is similar to Queryable.Where<TSource> Method (IQueryable<TSource>, Expression<Func<TSource, Int32, Boolean>>) /// <seealso cref="http://msdn.microsoft.com/en-us/library/vstudio/bb548547(v=vs.100).aspx"/> /// </param> /// <returns> /// The IWebElement chosen by the CSS selector and the filters, and casted to IWebElement. /// </returns> public virtual IWebElement FindElement(Func <IWebElement, bool> filters = null, int waitInMills = DefaultWaitToFindElement) { //TODO: if it is necessary to validate the lastSelectedELement or regard it as valid within a limited time? //*/ Perform some simple operation to validate that the lastFoundElement is still valid if (lastFoundElement != null && (filters == null || filters == lastFilters) && lastFoundElement.IsValid()) { return(lastFoundElement); } /*/ * //Otherwise, using the Environment.Ticks to make sure each Element is valid for a limited time (1 second) * if (lastFoundElement != null && (filters == null || filters == lastFilters) * //&& (Environment.TickCount-lastTick<ValidElementStatePeriodInTicks) * ) * return lastFoundElement; * //*/ IWebElement parentElement = null; if (Parent != null && !Parent.Identifier.Equals(HtmlByCustom.Root)) { parentElement = Parent.FindElement(filters, waitInMills); if (parentElement == null || !parentElement.IsValid()) { return(null); } } string css = Identifier.Css(); //When CSS matches multiple IWebElements, further selection shall be applied by filters //Or, When selection is based on ByText mechanism, then EnumMember.Value is used if (Identifier.IsCollection() || Identifier.Mechanism().Equals(Mechanisms.ByText)) { Func <string, ReadOnlyCollection <IWebElement> > findFunc = (parentElement == null) ? (Func <string, ReadOnlyCollection <IWebElement> >)DriverManager.FindElementsByCssSelector : parentElement.FindElementsByCss; //Select the candidate element by calling FindElementsByCssSelector(css) of either the parentElement or the Driver var candidates = WaitToFindElements(findFunc, css, waitInMills); #if (STEP_BY_STEP) Console.WriteLine("There are {0} candidates selected by '{1}'", candidates.Count, css); #endif if (Identifier.IsCollection()) { //Store the filters if it is not null, otherwise use the last filters lastFilters = filters ?? lastFilters; if (lastFilters == null) { throw new NullReferenceException(); } var filtered = candidates.Where(lastFilters); lastFoundElement = filtered.FirstOrDefault(); } else { lastFoundElement = candidates.FirstOrDefault(item => item.Text.Equals(Identifier.Value())); } } //When CSS select one IWebElement, then filter is ignored //Notice the GenericWait.Until() would keeps running until timeout or no Exception is thrown else { Func <string, IWebElement> find = (parentElement == null) ? (Func <string, IWebElement>)DriverManager.FindElementByCssSelector : parentElement.FindElementByCss; lastFoundElement = GenericWait <IWebElement> .TryUntil(() => find(css), x => x != null& x.IsValid(), ImmediateWaitToFindElement); } //if(lastFoundElement == null) // throw new NoSuchElementException("Failed to find Element by CSS: " + css); //Keeps the moment of this opertion //lastTick = Environment.TickCount; return(lastFoundElement); }