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

Enhancement - Playwright Window Explorer And Visual Testing Action Support #3850

Merged
merged 7 commits into from
Aug 12, 2024
Merged
12 changes: 8 additions & 4 deletions Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/GingerWebDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ limitations under the License.
using GingerCore.Drivers.Common;
using GingerCoreNET.SolutionRepositoryLib.RepositoryObjectsLib.PlatformsLib;
using HtmlAgilityPack;
using Microsoft.Graph;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static GingerCore.Platforms.PlatformsInfo.PlatformInfoBase;

#nullable enable
namespace Amdocs.Ginger.CoreNET.Drivers.CoreDrivers.Web
Expand Down Expand Up @@ -515,6 +512,9 @@ private protected async Task<HTMLElementInfo> CreateHtmlElementAsync(IBrowserEle
elementType = tag;
}

Size size = await browserElement.SizeAsync();
Point position = await browserElement.PositionAsync();
Comment on lines +515 to +516
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling for asynchronous calls.

The asynchronous calls to SizeAsync and PositionAsync should include error handling to manage potential exceptions that may arise.

-  Size size = await browserElement.SizeAsync();
-  Point position = await browserElement.PositionAsync();
+  Size size;
+  try {
+    size = await browserElement.SizeAsync();
+  } catch (Exception ex) {
+    // Handle or log the exception as needed
+    throw new InvalidOperationException("Failed to retrieve size.", ex);
+  }
+  
+  Point position;
+  try {
+    position = await browserElement.PositionAsync();
+  } catch (Exception ex) {
+    // Handle or log the exception as needed
+    throw new InvalidOperationException("Failed to retrieve position.", ex);
+  }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Size size = await browserElement.SizeAsync();
Point position = await browserElement.PositionAsync();
Size size;
try {
size = await browserElement.SizeAsync();
} catch (Exception ex) {
// Handle or log the exception as needed
throw new InvalidOperationException("Failed to retrieve size.", ex);
}
Point position;
try {
position = await browserElement.PositionAsync();
} catch (Exception ex) {
// Handle or log the exception as needed
throw new InvalidOperationException("Failed to retrieve position.", ex);
}


HTMLElementInfo newHtmlElement = new()
{
ElementObject = browserElement,
Expand All @@ -525,6 +525,10 @@ private protected async Task<HTMLElementInfo> CreateHtmlElementAsync(IBrowserEle
XPath = await GenerateXPathFromBrowserElementAsync(browserElement),
ElementType = elementType ?? string.Empty,
ElementTypeEnum = POMLearner.GetElementType(tag, typeAttributeValue),
Width = size.Width,
Height = size.Height,
X = position.X,
Y = position.Y,
};

return newHtmlElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ public override void RunAction(Act act)
ActGotoURLHandler actGotoURLHandler = new(actGotoURL, _browser);
actGotoURLHandler.HandleAsync().Wait();
break;
case ActVisualTesting actVisualTesting:
if (actVisualTesting.VisualTestingAnalyzer != ActVisualTesting.eVisualTestingAnalyzer.Applitools)
{
actVisualTesting.Execute(this);
}
else
{
act.Error = $"{actVisualTesting.VisualTestingAnalyzer} is not supported by Playwright driver, use Selenium driver instead.";
}
break;
default:
act.Error = $"This Action is not supported for Playwright driver";
break;
Expand All @@ -172,7 +182,7 @@ public bool IsActionSupported(Act act, out string message)
{
message = string.Empty;

if (act is ActWithoutDriver)
if (act is ActWithoutDriver or ActScreenShot)
{
return true;
}
Expand Down Expand Up @@ -219,9 +229,10 @@ public bool IsActionSupported(Act act, out string message)
}
return isLocatorSupported && isOperationSupported;
}
else if (act is ActScreenShot)
else if (act is ActVisualTesting actVisualTesting)
{
return true;
message = $"{actVisualTesting.VisualTestingAnalyzer} is not supported by Playwright driver, use Selenium driver instead.";
return actVisualTesting.VisualTestingAnalyzer != ActVisualTesting.eVisualTestingAnalyzer.Applitools;
}
else
{
Expand Down Expand Up @@ -731,13 +742,14 @@ public List<ElementInfo> GetElementChildren(ElementInfo elementInfo)
{
await SwitchToFrameOfElementAsync(elementInfo);
string xpath = GenerateXPathFromHTMLElementInfo(htmlElementInfo);
IEnumerable<IBrowserElement> browserElements = await _browser.CurrentWindow.CurrentTab.GetElementsAsync(eLocateBy.ByXPath, xpath);
string childrenXPath = GenerateChildrenXPath(xpath);
IEnumerable<IBrowserElement> browserElements = await _browser.CurrentWindow.CurrentTab.GetElementsAsync(eLocateBy.ByXPath, childrenXPath);
List<HTMLElementInfo> htmlElements = [];
foreach (IBrowserElement browserElement in browserElements)
{
HTMLElementInfo newHtmlElement = await CreateHtmlElementAsync(browserElement);

if (string.IsNullOrEmpty(newHtmlElement.ID))
if (string.IsNullOrEmpty(newHtmlElement.ID) && htmlElementInfo.HTMLElementObject != null)
{
newHtmlElement.ID = htmlElementInfo.HTMLElementObject.Id;
}
Expand Down Expand Up @@ -767,6 +779,27 @@ public List<ElementInfo> GetElementChildren(ElementInfo elementInfo)
}).Result;
}

private string GenerateChildrenXPath(string parentXPath)
{
string[] parentXPathSegments = parentXPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
string elementType = parentXPathSegments[^1];

int index = elementType.IndexOf('[');
if (index != -1)
{
elementType = elementType.AsSpan(0, index).ToString();
}

if (string.Equals(elementType, "iframe") || string.Equals(elementType, "frame"))
{
return "/html/*";
}
else
{
return parentXPath + "/*";
}
}
Comment on lines +782 to +801
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize the XPath generation logic.

The method generates XPath for child elements but can be optimized for readability and performance.

-  string[] parentXPathSegments = parentXPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
-  string elementType = parentXPathSegments[^1];
-  int index = elementType.IndexOf('[');
-  if (index != -1)
-  {
-    elementType = elementType.AsSpan(0, index).ToString();
-  }
-  if (string.Equals(elementType, "iframe") || string.Equals(elementType, "frame"))
-  {
-    return "/html/*";
-  }
-  else
-  {
-    return parentXPath + "/*";
-  }
+  var elementType = parentXPath.Split("/", StringSplitOptions.RemoveEmptyEntries).Last().Split('[').First();
+  return (elementType.Equals("iframe", StringComparison.OrdinalIgnoreCase) || elementType.Equals("frame", StringComparison.OrdinalIgnoreCase)) 
+    ? "/html/*" 
+    : $"{parentXPath}/*";
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private string GenerateChildrenXPath(string parentXPath)
{
string[] parentXPathSegments = parentXPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
string elementType = parentXPathSegments[^1];
int index = elementType.IndexOf('[');
if (index != -1)
{
elementType = elementType.AsSpan(0, index).ToString();
}
if (string.Equals(elementType, "iframe") || string.Equals(elementType, "frame"))
{
return "/html/*";
}
else
{
return parentXPath + "/*";
}
}
private string GenerateChildrenXPath(string parentXPath)
{
var elementType = parentXPath.Split("/", StringSplitOptions.RemoveEmptyEntries).Last().Split('[').First();
return (elementType.Equals("iframe", StringComparison.OrdinalIgnoreCase) || elementType.Equals("frame", StringComparison.OrdinalIgnoreCase))
? "/html/*"
: $"{parentXPath}/*";
}


public ObservableList<ControlProperty> GetElementProperties(ElementInfo elementInfo)
{
if (elementInfo is not HTMLElementInfo htmlElementInfo)
Expand Down Expand Up @@ -1494,7 +1527,7 @@ public void ChangeAppWindowSize(int width, int height)

while (true)
{
string s_Script = $"return document.elementFromPoint({ptX}, {ptY});";
string s_Script = $"document.elementFromPoint({ptX}, {ptY});";

IBrowserElement? ele = await _browser.CurrentWindow.CurrentTab.GetElementAsync(s_Script);

Expand Down
Loading