Skip to content

Commit

Permalink
[Testing] Implement ContextMenu UITest extension methods (dotnet#25340)
Browse files Browse the repository at this point in the history
* Add ContextMenu UITest extension methods

* Simplified test
  • Loading branch information
jsuarezruiz authored Nov 14, 2024
1 parent 6e56d11 commit a11555b
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NUnit.Framework;
#if ANDROID
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

Expand All @@ -12,17 +13,13 @@ public Bugzilla59580(TestDevice testDevice) : base(testDevice)

public override string Issue => "Raising Command.CanExecutChanged causes crash on Android";

// [Test]
// [Category(UITestCategories.TableView)]
// [FailsOnIOSWhenRunningOnXamarinUITest]
// public void RaisingCommandCanExecuteChangedCausesCrashOnAndroid()
// {
// App.WaitForElement(c => c.Marked("Cell"));

// App.ActivateContextMenu("Cell");

// App.WaitForElement(c => c.Marked("Fire CanExecuteChanged"));
// App.Tap(c => c.Marked("Fire CanExecuteChanged"));
// App.WaitForElement("Cell");
// }
}
[Test]
[Category(UITestCategories.TableView)]
[FailsOnIOSWhenRunningOnXamarinUITest]
public void RaisingCommandCanExecuteChangedCausesCrashOnAndroid()
{
App.ActivateContextMenu("Cell");
App.Tap("Fire CanExecuteChanged");
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Interactions;
using OpenQA.Selenium.Interactions;
using UITest.Core;

namespace UITest.Appium
{
public class AppiumAndroidContextMenuActions : ICommandExecutionGroup
{
const string ActivateContextMenuCommand = "activateContextMenu";
const string DismissContextMenuCommand = "dismissContextMenu";

protected readonly AppiumApp _app;
readonly List<string> _commands = new()
{
ActivateContextMenuCommand,
DismissContextMenuCommand,
};

public AppiumAndroidContextMenuActions(AppiumApp app)
{
_app = app;
}

public bool IsCommandSupported(string commandName)
{
return _commands.Contains(commandName, StringComparer.OrdinalIgnoreCase);
}

public CommandResponse Execute(string commandName, IDictionary<string, object> parameters)
{
return commandName switch
{
ActivateContextMenuCommand => ActivateContextMenu(parameters),
DismissContextMenuCommand => DismissContextMenu(parameters),
_ => CommandResponse.FailedEmptyResponse,
};
}

protected CommandResponse ActivateContextMenu(IDictionary<string, object> parameters)
{
parameters.TryGetValue("element", out var value);

if (value is null)
return CommandResponse.FailedEmptyResponse;

string elementString = (string)value;
var element = GetAppiumElement(elementString);

// If cannot find an element by Id, just try to find using the text.
if (element is null)
element = _app.Driver.FindElement(OpenQA.Selenium.By.XPath("//*[@text='" + elementString + "']"));

if (element is not null)
{
OpenQA.Selenium.Appium.Interactions.PointerInputDevice touchDevice = new OpenQA.Selenium.Appium.Interactions.PointerInputDevice(PointerKind.Touch);
var longPress = new ActionSequence(touchDevice, 0);

longPress.AddAction(touchDevice.CreatePointerMove(element, 0, 0, TimeSpan.FromMilliseconds(0)));
longPress.AddAction(touchDevice.CreatePointerDown(PointerButton.TouchContact));
longPress.AddAction(touchDevice.CreatePointerMove(element, 0, 0, TimeSpan.FromMilliseconds(2000)));
longPress.AddAction(touchDevice.CreatePointerUp(PointerButton.TouchContact));
_app.Driver.PerformActions(new List<ActionSequence> { longPress });

return CommandResponse.SuccessEmptyResponse;
}

return CommandResponse.FailedEmptyResponse;
}

protected CommandResponse DismissContextMenu(IDictionary<string, object> parameters)
{
_app.Back();

return CommandResponse.SuccessEmptyResponse;
}

static AppiumElement? GetAppiumElement(object element)
{
if (element is AppiumElement appiumElement)
{
return appiumElement;
}
else if (element is AppiumDriverElement driverElement)
{
return driverElement.AppiumElement;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System.Drawing;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Interactions;
using OpenQA.Selenium.Interactions;
using UITest.Core;

namespace UITest.Appium
{
public class AppiumAppleContextMenuActions : ICommandExecutionGroup
{
const string ActivateContextMenuCommand = "activateContextMenu";
const string DismissContextMenuCommand = "dismissContextMenu";

protected readonly AppiumApp _app;
readonly List<string> _commands = new()
{
ActivateContextMenuCommand,
DismissContextMenuCommand,
};

public AppiumAppleContextMenuActions(AppiumApp app)
{
_app = app;
}

public bool IsCommandSupported(string commandName)
{
return _commands.Contains(commandName, StringComparer.OrdinalIgnoreCase);
}

public CommandResponse Execute(string commandName, IDictionary<string, object> parameters)
{
return commandName switch
{
ActivateContextMenuCommand => ActivateContextMenu(parameters),
DismissContextMenuCommand => DismissContextMenu(parameters),
_ => CommandResponse.FailedEmptyResponse,
};
}

protected CommandResponse ActivateContextMenu(IDictionary<string, object> parameters)
{
parameters.TryGetValue("element", out var value);

if (value is null)
return CommandResponse.FailedEmptyResponse;

string elementString = (string)value;
var element = GetAppiumElement(elementString);

// If cannot find an element by Id, just try to find using the text.
if (element is null)
element = _app.Driver.FindElement(OpenQA.Selenium.By.XPath("//*[@text='" + elementString + "']"));

if (element is not null)
{
var target = _app.WaitForElement(element.Id);
var rect = target.GetRect();
var rootViewRect = GetRootViewRect(_app);

var centerX = rect.Width / 2;
var centerY = rect.Height / 2;
var width = Math.Max(250, rect.Width);

if ((rect.X + width) > rootViewRect.Width)
{
width = rootViewRect.Width - rect.X;
}

int fromX = (int)(rect.X + (0.95f * width));
int fromY = centerY;
int toX = (int)(rect.X + (0.05f * width));
int toY = centerY;

OpenQA.Selenium.Appium.Interactions.PointerInputDevice touchDevice = new OpenQA.Selenium.Appium.Interactions.PointerInputDevice(PointerKind.Touch);
var dragSequence = new ActionSequence(touchDevice, 0);
dragSequence.AddAction(touchDevice.CreatePointerMove(CoordinateOrigin.Viewport, fromX, fromY, TimeSpan.Zero));
dragSequence.AddAction(touchDevice.CreatePointerDown(PointerButton.TouchContact));
dragSequence.AddAction(touchDevice.CreatePointerMove(CoordinateOrigin.Viewport, toX, toY, TimeSpan.FromMilliseconds(250)));
dragSequence.AddAction(touchDevice.CreatePointerUp(PointerButton.TouchContact));
_app.Driver.PerformActions([dragSequence]);

return CommandResponse.SuccessEmptyResponse;
}

return CommandResponse.FailedEmptyResponse;
}

protected CommandResponse DismissContextMenu(IDictionary<string, object> parameters)
{
try
{
var rootViewRect = GetRootViewRect(_app);

var centerX = rootViewRect.Width / 2;
var centerY = rootViewRect.Height / 2;

_app.TapCoordinates(centerX, centerY);

return CommandResponse.SuccessEmptyResponse;
}
catch
{
return CommandResponse.FailedEmptyResponse;
}
}

static AppiumElement? GetAppiumElement(object element)
{
if (element is AppiumElement appiumElement)
{
return appiumElement;
}
else if (element is AppiumDriverElement driverElement)
{
return driverElement.AppiumElement;
}

return null;
}

static Rectangle GetRootViewRect(AppiumApp app)
{
var rootElement = app.FindElement(AppiumQuery.ByXPath("/*"));
var rootViewRect = rootElement.GetRect();

return rootViewRect;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Drawing;
using OpenQA.Selenium.Appium;
using UITest.Core;

namespace UITest.Appium
{
public class AppiumWindowsContextMenuActions : ICommandExecutionGroup
{
const string ActivateContextMenuCommand = "activateContextMenu";
const string DismissContextMenuCommand = "dismissContextMenu";

protected readonly AppiumApp _app;
readonly List<string> _commands = new()
{
ActivateContextMenuCommand,
DismissContextMenuCommand,
};

public AppiumWindowsContextMenuActions(AppiumApp app)
{
_app = app;
}

public bool IsCommandSupported(string commandName)
{
return _commands.Contains(commandName, StringComparer.OrdinalIgnoreCase);
}

public CommandResponse Execute(string commandName, IDictionary<string, object> parameters)
{
return commandName switch
{
ActivateContextMenuCommand => ActivateContextMenu(parameters),
DismissContextMenuCommand => DismissContextMenu(parameters),
_ => CommandResponse.FailedEmptyResponse,
};
}

protected CommandResponse ActivateContextMenu(IDictionary<string, object> parameters)
{
parameters.TryGetValue("element", out var value);

if (value is null)
return CommandResponse.FailedEmptyResponse;

string elementString = (string)value;
var element = GetAppiumElement(elementString);

// If cannot find an element by Id, just try to find using the text.
if (element is null)
element = _app.Driver.FindElement(OpenQA.Selenium.By.XPath("//*[@text='" + elementString + "']"));

if (element is not null)
{
_app.Driver.ExecuteScript("windows: click", new Dictionary<string, object>
{
{ "elementId", element.Id },
{ "button", "right" },
});

return CommandResponse.SuccessEmptyResponse;
}

return CommandResponse.FailedEmptyResponse;
}

protected CommandResponse DismissContextMenu(IDictionary<string, object> parameters)
{
try
{
var screenbounds = GetRootViewRect(_app);

var centerX = screenbounds.Width / 2;
var centerY = screenbounds.Height / 2;

_app.TapCoordinates(centerX, centerY);

return CommandResponse.SuccessEmptyResponse;
}
catch
{
return CommandResponse.FailedEmptyResponse;
}
}

static AppiumElement? GetAppiumElement(object element)
{
if (element is AppiumElement appiumElement)
{
return appiumElement;
}
else if (element is AppiumDriverElement driverElement)
{
return driverElement.AppiumElement;
}

return null;
}

static Rectangle GetRootViewRect(AppiumApp app)
{
var rootElement = app.FindElement(AppiumQuery.ByXPath("/*"));
var rootViewRect = rootElement.GetRect();

return rootViewRect;
}
}
}
1 change: 1 addition & 0 deletions src/TestUtils/src/UITest.Appium/AppiumAndroidApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private AppiumAndroidApp(Uri remoteAddress, IConfig config)
_commandExecutor.AddCommandGroup(new AppiumAndroidSpecificActions(this));
_commandExecutor.AddCommandGroup(new AppiumAndroidVirtualKeyboardActions(this));
_commandExecutor.AddCommandGroup(new AppiumAndroidAlertActions(this));
_commandExecutor.AddCommandGroup(new AppiumAndroidContextMenuActions(this));
_commandExecutor.AddCommandGroup(new AppiumAndroidStepperActions(this));
}

Expand Down
1 change: 1 addition & 0 deletions src/TestUtils/src/UITest.Appium/AppiumCatalystApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public AppiumCatalystApp(Uri remoteAddress, IConfig config)
_commandExecutor.AddCommandGroup(new AppiumCatalystMouseActions(this));
_commandExecutor.AddCommandGroup(new AppiumCatalystTouchActions(this));
_commandExecutor.AddCommandGroup(new AppiumCatalystAlertActions(this));
_commandExecutor.AddCommandGroup(new AppiumAppleContextMenuActions(this));
}

public override ApplicationState AppState
Expand Down
1 change: 1 addition & 0 deletions src/TestUtils/src/UITest.Appium/AppiumIOSApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public AppiumIOSApp(Uri remoteAddress, IConfig config)
_commandExecutor.AddCommandGroup(new AppiumIOSVirtualKeyboardActions(this));
_commandExecutor.AddCommandGroup(new AppiumIOSThemeChangeAction(this));
_commandExecutor.AddCommandGroup(new AppiumIOSAlertActions(this));
_commandExecutor.AddCommandGroup(new AppiumAppleContextMenuActions(this));
_commandExecutor.AddCommandGroup(new AppiumIOSThemeChangeAction(this));
_commandExecutor.AddCommandGroup(new AppiumIOSStepperActions(this));
}
Expand Down
Loading

0 comments on commit a11555b

Please sign in to comment.