Objective
In this post we will try to look into the usage of text similarity algorithms like Levenshtein distance to accurately locate the items in case of dynamically altering pages where the object properties like id, name keep on changing (e.g.: Salesforce Lightening)
Groundwork
A typical web page is always having group of web objects structured to complement each other. for example:
Here, Username label and the input box are structured together.
Full XPath of label: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]
Full XPath of Input: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]/input[1]
The part of XPaths marked as bold are simililarity between two XPaths.
Similarly, Password: label and corresponding password box are structured together.
Full XPath of label: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[2]
Full XPath of Password: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[2]/input[1]
As you can see above, structured objects are always close to each other in terms of Xpath
Logic
Due to the ever-changing development happening in the production pages, Full XPath is not an ideal option for locating web objects. But the labels and associated inputs are always associated with each other in terms of visual and XPath terms as explained above.
Let us try to locate the user name input.
Step 1: Locate the label with referential XPath: //label[contains(.,”Username:”)]
Step 2: Find the Full XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]
Step 3: Find all inputs on the screen and corresponding Full XPaths
var inputList = IWeb.FindElements(By.XPath(“//input”));
and find full XPaths for all of the elements.
XPath: /html[1]/body[1]/nav[1]/div[2]/div[1]/div[3]/div[1]/form[1]/div[1]/input[1]
XPath: /html[1]/body[1]/section[1]/div[2]/div[1]/div[3]/div[1]/form[1]/div[1]/input[1]
XPath:
/html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/input[1]
XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]/input[1]
XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[2]/input[1]
If we apply the string similarity algorithm (create string array by splitting the string with / and then comparing how many components of XPaths do match) and compare label full XPath with the full XPath of each input
Label XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]
XPath: /html[1]/body[1]/nav[1]/div[2]/div[1]/div[3]/div[1]/form[1]/div[1]/input[1] . Match: 3
XPath: /html[1]/body[1]/section[1]/div[2]/div[1]/div[3]/div[1]/form[1]/div[1]/input[1] . Match: 5
XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/input[1] . Match: 8
XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]/input[1] . Match: 10
XPath: /html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[2]/input[1] . Match: 9
The most match found in XPath: html[1]/body[1]/section[1]/div[2]/div[2]/div[1]/form[1]/div[1]/label[1]/input[1] which exactly matches the user name input
Comparison Code
public static int CalculateSimilarity(string source1, string source2)
{
//return matrix[source1Length, source2Length];
string[] Source1Array= source1.Split('/');
string[] Source2Array = source2.Split('/');
int x = 0;
int source1Length = Source1Array.Length, source2Length = Source2Array.Length;
int minLen = Math.Min(source1Length, source2Length);
for (i=0;i< minLen; i++)
{
if (Source1Array[i] == Source2Array[i])
{
x++;
}
else
{
break;
}
}
return x;
}
Code to find Full XPath of any web Object
public string GetItemFullXpath(IWebElement iret)
{
string Xpathtrail = "";
try
{
string RefTag = iret.TagName;
IWebElement prnt;
IWebElement curr = iret;
int icnt;
int i;
while (RefTag!= "html")
{
prnt = GetParent(curr);
var children = prnt.FindElements(By.XPath("*"));
icnt = children.Count;
int Tagcnt = 1;
for (i = 0; i < icnt; i++)
{
IWebElement pi = children[i];
if (pi.TagName == RefTag)
{
if (pi.Equals(curr))
{
Xpathtrail = "/" + RefTag + "[" + Tagcnt.ToString() + "]" + Xpathtrail;
break;
}
Tagcnt++;
}
}
curr = prnt;
RefTag = curr.TagName;
}
Xpathtrail = "/html[1]" + Xpathtrail;
}
return Xpathtrail;
}
catch (Exception )
{
}
}
-------------------------------------------------------
public IWebElement GetParent(IWebElement IWebe)
{
try
{
return IWebe.FindElement(By.XPath("parent::*"));
}
catch
{
return null;
}
}
Leave a Reply