Skip to content

Commit

Permalink
Improve capability validation
Browse files Browse the repository at this point in the history
  • Loading branch information
aristotelos committed Apr 18, 2024
1 parent c3b3fca commit 4a727d0
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 15 deletions.
69 changes: 68 additions & 1 deletion src/FlaUI.WebDriver.UITests/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ public void NewSession_AppNotExists_ReturnsError()
Assert.That(newSession, Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Starting app 'C:\\NotExisting.exe' with arguments '' threw an exception: An error occurred trying to start process 'C:\\NotExisting.exe' with working directory '.'. The system cannot find the file specified."));
}

[TestCase(123)]
[TestCase(false)]
public void NewSession_AppNotAString_Throws(object value)
{
var driverOptions = new FlaUIDriverOptions()
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:app", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:app must be a string"));
}

[Test]
public void NewSession_AppTopLevelWindow_IsSupported()
{
Expand Down Expand Up @@ -91,7 +105,7 @@ public void NewSession_AppTopLevelWindowTitleMatch_IsSupported(string match)
}

[Test, Ignore("Sometimes multiple processes are left open")]
public void NewSession_MultipleMatchingAppTopLevelWindowTitleMatch_ReturnsError()
public void NewSession_AppTopLevelWindowTitleMatchMultipleMatching_ReturnsError()
{
using var testAppProcess = new TestAppProcess();
using var testAppProcess1 = new TestAppProcess();
Expand Down Expand Up @@ -125,6 +139,33 @@ public void NewSession_AppTopLevelWindowTitleMatchNotFound_ReturnsError()
Assert.That(newSession, Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Process with main window title matching 'FlaUI Not Existing' could not be found"));
}

[TestCase(123)]
[TestCase(false)]
public void NewSession_AppTopLevelWindowTitleMatchNotAString_Throws(object value)
{
var driverOptions = new FlaUIDriverOptions()
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:appTopLevelWindowTitleMatch", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:appTopLevelWindowTitleMatch must be a string"));
}

[TestCase("(invalid")]
public void NewSession_AppTopLevelWindowTitleMatchInvalidRegex_Throws(string value)
{
var driverOptions = new FlaUIDriverOptions()
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:appTopLevelWindowTitleMatch", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:appTopLevelWindowTitleMatch '(invalid' is not a valid regular expression: Invalid pattern '(invalid' at offset 8. Not enough )'s."));
}

[TestCase("")]
[TestCase("FlaUI")]
public void NewSession_AppTopLevelWindowInvalidFormat_ReturnsError(string appTopLevelWindowString)
Expand All @@ -136,6 +177,20 @@ public void NewSession_AppTopLevelWindowInvalidFormat_ReturnsError(string appTop
Assert.That(newSession, Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo($"Capability appium:appTopLevelWindow '{appTopLevelWindowString}' is not a valid hexadecimal string"));
}

[TestCase(123)]
[TestCase(false)]
public void NewSession_AppTopLevelWindowNotAString_ReturnsError(object value)
{
var driverOptions = new FlaUIDriverOptions()
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:appTopLevelWindow", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:appTopLevelWindow must be a string"));
}

[Test]
public void GetTitle_Default_IsSupported()
{
Expand Down Expand Up @@ -197,5 +252,17 @@ public void NewCommandTimeout_NotExpired_DoesNotEndSession()

Assert.That(() => driver.Title, Throws.Nothing);
}

[TestCase("123")]
[TestCase(false)]
[TestCase("not a number")]
public void NewCommandTimeout_InvalidValue_Throws(object value)
{
var driverOptions = FlaUIDriverOptions.TestApp();
driverOptions.AddAdditionalOption("appium:newCommandTimeout", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<WebDriverArgumentException>().With.Message.EqualTo("Capability appium:newCommandTimeout must be a number"));
}
}
}
62 changes: 48 additions & 14 deletions src/FlaUI.WebDriver/Controllers/SessionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -42,25 +43,24 @@ public async Task<ActionResult> CreateNewSession([FromBody] CreateSessionRequest
Message = "Required capabilities did not match. Capability `platformName` with value `windows` is required"
});
}
if (capabilities.TryGetValue("appium:app", out var appPath))
if (TryGetStringCapability(capabilities, "appium:app", out var appPath))
{
if (appPath.GetString() == "Root")
if (appPath == "Root")
{
app = null;
}
else
{
bool hasArguments = capabilities.TryGetValue("appium:appArguments", out var appArgumentsValue);
var appArguments = hasArguments ? appArgumentsValue.GetString()! : "";
{
TryGetStringCapability(capabilities, "appium:appArguments", out var appArguments);
try
{
if (appPath.GetString()!.EndsWith("!App"))
if (appPath.EndsWith("!App"))
{
app = Core.Application.LaunchStoreApp(appPath.GetString()!, appArguments);
app = Core.Application.LaunchStoreApp(appPath, appArguments);
}
else
{
var processStartInfo = new ProcessStartInfo(appPath.GetString()!, appArguments);
var processStartInfo = new ProcessStartInfo(appPath, appArguments ?? "");
app = Core.Application.Launch(processStartInfo);
}
}
Expand All @@ -70,24 +70,24 @@ public async Task<ActionResult> CreateNewSession([FromBody] CreateSessionRequest
}
}
}
else if(capabilities.TryGetValue("appium:appTopLevelWindow", out var appTopLevelWindowString))
else if (TryGetStringCapability(capabilities, "appium:appTopLevelWindow", out var appTopLevelWindowString))
{
Process process = GetProcessByMainWindowHandle(appTopLevelWindowString.GetString()!);
Process process = GetProcessByMainWindowHandle(appTopLevelWindowString);
app = Core.Application.Attach(process);
}
else if (capabilities.TryGetValue("appium:appTopLevelWindowTitleMatch", out var appTopLevelWindowTitleMatch))
else if (TryGetStringCapability(capabilities, "appium:appTopLevelWindowTitleMatch", out var appTopLevelWindowTitleMatch))
{
Process? process = GetProcessByMainWindowTitle(appTopLevelWindowTitleMatch.GetString()!);
Process? process = GetProcessByMainWindowTitle(appTopLevelWindowTitleMatch);
app = Core.Application.Attach(process);
}
else
{
throw WebDriverResponseException.InvalidArgument("One of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability");
}
var session = new Session(app);
if(capabilities.TryGetValue("appium:newCommandTimeout", out var newCommandTimeout))
if(TryGetNumberCapability(capabilities, "appium:newCommandTimeout", out var newCommandTimeout))
{
session.NewCommandTimeout = TimeSpan.FromSeconds(newCommandTimeout.GetDouble());
session.NewCommandTimeout = TimeSpan.FromSeconds(newCommandTimeout);
}
_sessionRepository.Add(session);
_logger.LogInformation("Created session with ID {SessionId} and capabilities {Capabilities}", session.SessionId, capabilities);
Expand All @@ -98,6 +98,40 @@ public async Task<ActionResult> CreateNewSession([FromBody] CreateSessionRequest
}));
}

private static bool TryGetStringCapability(Dictionary<string, JsonElement> capabilities, string key, [MaybeNullWhen(false)] out string value)
{
if(capabilities.TryGetValue(key, out var valueJson))
{
if(valueJson.ValueKind != JsonValueKind.String)
{
throw WebDriverResponseException.InvalidArgument($"Capability {key} must be a string");
}

value = valueJson.GetString();
return value != null;
}

value = null;
return false;
}

private static bool TryGetNumberCapability(Dictionary<string, JsonElement> capabilities, string key, out double value)
{
if (capabilities.TryGetValue(key, out var valueJson))
{
if (valueJson.ValueKind != JsonValueKind.Number)
{
throw WebDriverResponseException.InvalidArgument($"Capability {key} must be a number");
}

value = valueJson.GetDouble();
return true;
}

value = default;
return false;
}

private static Process GetProcessByMainWindowTitle(string appTopLevelWindowTitleMatch)
{
Regex appMainWindowTitleRegex;
Expand Down

0 comments on commit 4a727d0

Please sign in to comment.