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

Capabilities #22

Merged
merged 3 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The following capabilities are supported:
| Capability Name | Description | Example value |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| platformName | Must be set to `windows` (case-insensitive). | `windows` |
| appium:automationName | Must be set to `FlaUI` (case-insensitive). | `FlaUI` |
| appium:app | The path to the application, or in case of an UWP app, `<package family name>!App`. It is also possible to set app to `Root`. In such case the session will be invoked without any explicit target application. Either this capability, `appTopLevelWindow` or `appTopLevelWindowTitleMatch` must be provided on session startup. | `C:\Windows\System32\notepad.exe`, `Microsoft.WindowsCalculator_8wekyb3d8bbwe!App` |
| appium:appArguments | Application arguments string, for example `/?`. | |
| appium:appWorkingDir | Full path to the folder, which is going to be set as the working dir for the application under test. This is only applicable for classic apps. When this is used the `appium:app` may contain a relative file path. | `C:\MyApp\` |
Expand Down Expand Up @@ -57,6 +58,7 @@ public class FlaUIDriverOptions : DriverOptions
{
PlatformName = "windows"
};
options.AddAdditionalOption("appium:automationName", "flaui");
options.AddAdditionalOption("appium:app", path);
return options;
}
Expand All @@ -78,6 +80,7 @@ import { remote } from 'webdriverio'
const driver = await remote({
capabilities: {
platformName: 'windows',
'appium:automationName': 'flaui'
'appium:app': 'C:\\YourApp.exe'
}
});
Expand Down
46 changes: 38 additions & 8 deletions src/FlaUI.WebDriver.UITests/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using FlaUI.WebDriver.UITests.TestUtil;
using OpenQA.Selenium;
using System;
using System.Collections.Generic;

namespace FlaUI.WebDriver.UITests
{
Expand All @@ -16,17 +17,30 @@ public void NewSession_PlatformNameMissing_ReturnsError()

var newSession = () => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, emptyOptions);

Assert.That(newSession, Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
Assert.That(newSession, Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, capability 'appium:automationName' with value `FlaUI` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
}

[Test]
public void NewSession_AutomationNameMissing_ReturnsError()
{
var emptyOptions = FlaUIDriverOptions.Empty();
emptyOptions.AddAdditionalOption("appium:platformName", "windows");

var newSession = () => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, emptyOptions);

Assert.That(newSession, Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, capability 'appium:automationName' with value `FlaUI` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
}

[Test]
public void NewSession_AllAppCapabilitiesMissing_ReturnsError()
{
var emptyOptions = FlaUIDriverOptions.Empty();
emptyOptions.AddAdditionalOption("appium:platformName", "windows");
emptyOptions.AddAdditionalOption("appium:automationName", "windows");

var newSession = () => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, emptyOptions);

Assert.That(newSession, Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
Assert.That(newSession, Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, capability 'appium:automationName' with value `FlaUI` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
}

[Test]
Expand Down Expand Up @@ -58,6 +72,7 @@ public void NewSession_AppNotAString_Throws(object value)
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:automationName", "FlaUI");
driverOptions.AddAdditionalOption("appium:app", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Expand All @@ -76,14 +91,31 @@ public void NewSession_AppWorkingDir_IsSupported()
Assert.That(title, Is.EqualTo("FlaUI WPF Test App"));
}

[Test]
public void NewSession_Timeouts_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
driverOptions.AddAdditionalOption("timeouts", new Dictionary<string, int>()
{
["script"] = 10000,
["pageLoad"] = 50000,
["implicit"] = 3000
});
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

Assert.That(driver.Manage().Timeouts().AsynchronousJavaScript, Is.EqualTo(TimeSpan.FromSeconds(10)));
Assert.That(driver.Manage().Timeouts().PageLoad, Is.EqualTo(TimeSpan.FromSeconds(50)));
Assert.That(driver.Manage().Timeouts().ImplicitWait, Is.EqualTo(TimeSpan.FromSeconds(3)));
}

[Test]
public void NewSession_NotSupportedCapability_Throws()
{
var driverOptions = FlaUIDriverOptions.TestApp();
driverOptions.AddAdditionalOption("unknown:unknown", "value");

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
Throws.TypeOf<InvalidOperationException>().With.Message.EqualTo("Required capabilities did not match. Capability `platformName` with value `windows` is required, capability 'appium:automationName' with value `FlaUI` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability (SessionNotCreated)"));
}

[Test]
Expand Down Expand Up @@ -179,6 +211,7 @@ public void NewSession_AppTopLevelWindowTitleMatchNotAString_Throws(object value
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:automationName", "FlaUI");
driverOptions.AddAdditionalOption("appium:appTopLevelWindowTitleMatch", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Expand All @@ -188,11 +221,7 @@ public void NewSession_AppTopLevelWindowTitleMatchNotAString_Throws(object value
[TestCase("(invalid")]
public void NewSession_AppTopLevelWindowTitleMatchInvalidRegex_Throws(string value)
{
var driverOptions = new FlaUIDriverOptions()
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:appTopLevelWindowTitleMatch", value);
var driverOptions = FlaUIDriverOptions.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."));
Expand All @@ -217,6 +246,7 @@ public void NewSession_AppTopLevelWindowNotAString_ReturnsError(object value)
{
PlatformName = "Windows"
};
driverOptions.AddAdditionalOption("appium:automationName", "FlaUI");
driverOptions.AddAdditionalOption("appium:appTopLevelWindow", value);

Assert.That(() => new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions),
Expand Down
3 changes: 3 additions & 0 deletions src/FlaUI.WebDriver.UITests/TestUtil/FlaUIDriverOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static FlaUIDriverOptions App(string path)
{
PlatformName = "Windows"
};
options.AddAdditionalOption("appium:automationName", "FlaUI");
options.AddAdditionalOption("appium:app", path);
return options;
}
Expand All @@ -30,6 +31,7 @@ public static DriverOptions AppTopLevelWindow(string windowHandle)
{
PlatformName = "Windows"
};
options.AddAdditionalOption("appium:automationName", "FlaUI");
options.AddAdditionalOption("appium:appTopLevelWindow", windowHandle);
return options;
}
Expand All @@ -40,6 +42,7 @@ public static DriverOptions AppTopLevelWindowTitleMatch(string match)
{
PlatformName = "Windows"
};
options.AddAdditionalOption("appium:automationName", "FlaUI");
options.AddAdditionalOption("appium:appTopLevelWindowTitleMatch", match);
return options;
}
Expand Down
36 changes: 30 additions & 6 deletions src/FlaUI.WebDriver/Controllers/SessionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public async Task<ActionResult> CreateNewSession([FromBody] CreateSessionRequest
return WebDriverResult.Error(new ErrorResponse
{
ErrorCode = "session not created",
Message = "Required capabilities did not match. Capability `platformName` with value `windows` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability"
Message = "Required capabilities did not match. Capability `platformName` with value `windows` is required, capability 'appium:automationName' with value `FlaUI` is required, and one of appium:app, appium:appTopLevelWindow or appium:appTopLevelWindowTitleMatch must be passed as a capability"
});
}
if (TryGetStringCapability(capabilities, "appium:app", out var appPath))
Expand Down Expand Up @@ -95,6 +95,15 @@ public async Task<ActionResult> CreateNewSession([FromBody] CreateSessionRequest
{
session.NewCommandTimeout = TimeSpan.FromSeconds(newCommandTimeout);
}
if (capabilities.TryGetValue("timeouts", out var valueJson))
{
var timeoutsConfiguration = JsonSerializer.Deserialize<TimeoutsConfiguration>(valueJson);
if (timeoutsConfiguration == null)
{
throw WebDriverResponseException.InvalidArgument("Could not deserialize timeouts capability");
}
session.TimeoutsConfiguration = timeoutsConfiguration;
}
_sessionRepository.Add(session);
_logger.LogInformation("Created session with ID {SessionId} and capabilities {Capabilities}", session.SessionId, capabilities);
return await Task.FromResult(WebDriverResult.Success(new CreateSessionResponse()
Expand All @@ -117,30 +126,40 @@ private bool IsMatchingCapabilitySet(IDictionary<string, JsonElement> capabiliti
return false;
}

if (TryGetStringCapability(capabilities, "appium:automationName", out var automationName)
&& automationName.ToLowerInvariant() == "flaui")
{
matchedCapabilities.Add("appium:automationName", capabilities["appium:automationName"]);
}
else
{
return false;
}

if (TryGetStringCapability(capabilities, "appium:app", out var appPath))
{
matchedCapabilities.Add("appium:app", capabilities["appium:app"]);

if (appPath != "Root")
{
if(TryGetStringCapability(capabilities, "appium:appArguments", out _))
if(capabilities.ContainsKey("appium:appArguments"))
{
matchedCapabilities.Add("appium:appArguments", capabilities["appium:appArguments"]);
}
if (!appPath.EndsWith("!App"))
{
if (TryGetStringCapability(capabilities, "appium:appWorkingDir", out _))
if (capabilities.ContainsKey("appium:appWorkingDir"))
{
matchedCapabilities.Add("appium:appWorkingDir", capabilities["appium:appWorkingDir"]);
}
}
}
}
else if (TryGetStringCapability(capabilities, "appium:appTopLevelWindow", out _))
else if (capabilities.ContainsKey("appium:appTopLevelWindow"))
{
matchedCapabilities.Add("appium:appTopLevelWindow", capabilities["appium:appTopLevelWindow"]);
}
else if (TryGetStringCapability(capabilities, "appium:appTopLevelWindowTitleMatch", out _))
else if (capabilities.ContainsKey("appium:appTopLevelWindowTitleMatch"))
{
matchedCapabilities.Add("appium:appTopLevelWindowTitleMatch", capabilities["appium:appTopLevelWindowTitleMatch"]);
}
Expand All @@ -149,11 +168,16 @@ private bool IsMatchingCapabilitySet(IDictionary<string, JsonElement> capabiliti
return false;
}

if (TryGetNumberCapability(capabilities, "appium:newCommandTimeout", out _))
if (capabilities.ContainsKey("appium:newCommandTimeout"))
{
matchedCapabilities.Add("appium:newCommandTimeout", capabilities["appium:newCommandTimeout"]); ;
}

if (capabilities.ContainsKey("timeouts"))
{
matchedCapabilities.Add("timeouts", capabilities["timeouts"]);
}

return matchedCapabilities.Count == capabilities.Count;
}

Expand Down
Loading