So I'm in the process of creating a BaseIFrame
Class to generically parse nested iFrames from any page without utilizing IDs, xpaths or anything DOM specific. I used recursion to catch all iFrames on a page and stacks to collect them. However, after debugging I discovered my recursive function loops over the same web element over and over before I hit a stack overflow.
How can I rectify this?
BaseIFrame
class:
namespace [Confidental namespace] {
public class BaseIFrame {
protected IWebDriver _driver;
private ReadOnlyCollection<IWebElement> _iframes;
public BaseIFrame(IWebDriver _driver) {
this._driver = _driver;
_iframes = _driver.FindElements(By.TagName("frame"));
_iframesSize = _iframes.Count();
}
public ReadOnlyCollection<IWebElement> getFrames() {
return _iframes;
}
private static IEnumerable GetAllFramesRecursive<T>(IWebElement frame, IWebDriver driver)
{
var result = new Stack<IWebElement>();
BaseIFrame baseClass = new BaseIFrame(driver);
var iFrameList = baseClass._iframes;
foreach (var i in iFrameList)
{
result.Push(i);
foreach (IWebElement e in GetAllFramesRecursive<IWebElement>(i, driver))
{
result.Push(e);
}
}
return result;
}
// Public callable method for unit test
public void GetAlliFramesRecursivePublic<T>(IWebElement test, IWebDriver driver)
{
GetAllFramesRecursive<IWebElement>(test, driver);
}
}
}
BaseIFrameTest
class:
[TestMethod]
public void AssertiFrameLength()
{
Login();
var expected = 0;
BaseIFrame bif = new BaseIFrame(_driver);
bif.GetAlliFramesRecursivePublic<IWebElement>(bif.getFrames()[0], _driver);
var elementSize = _driver.FindElements(By.TagName("*")).Count;
for (var i = 0; i < elementSize; i++)
{
var iframesSize = _driver.FindElements(By.TagName("frame")).Count;
expected += iframesSize;
}
var actual = bif.getFrames();
Assert.AreEqual(expected, actual.Count);
}
A basic depth first iterator of a tree would look something like this:
var stack = new Stack<T>();
stack.Push(rootItem);
while (stack.Count > 0)
{
var current = stack.Pop();
// Process the current item
foreach (var child in current.ChildItems)
{
stack.Push(child);
}
}
If there is a risk that a node occurs multiple times (ie it is a graph rather than a tree) you need some way to avoid repeated visits, for example using a hashSet:
var stack = new Stack<T>();
stack.Push(rootItem);
var visited = new HashSet<T>();
while (stack.Count > 0)
{
var current = stack.Pop();
if (visited.Add(current))
{
// Process the current item
foreach (var child in current.ChildItems)
{
stack.Push(child);
}
}
}
Using iteration instead of recursion both avoids problems with stackOverflows, and you avoid the need to create a new collection for each node in the tree. In my examples you can simply do a yield return current
as the process step to produce an iterator that is lazily evaluated. Replace current.ChildItems
with whatever you need to generate the edges/children for the current node.
One thing that looks odd in your example is that I cannot see where the frame
parameter is used. Only the driver
seem to be used to generate the list of iFrames, but the driver seem to be the same in every invocation.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.