Skip to content

Commit 4501fb1

Browse files
authored
[Selenium 4][Appium 5] Migration to Appium-5 beta version (#33) +semver: breaking
* [Selenium 4][Appium 5] Migration to Appium-5 beta version +semver: breaking * Fix missed documentation * Fix GDI issue in tests * Update README with setup instructions * Add pl and uk localizations
1 parent 34686ae commit 4501fb1

25 files changed

+280
-78
lines changed

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Applications/Application.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
using Aquality.Selenium.Core.Applications;
33
using Aquality.Selenium.Core.Configurations;
44
using Aquality.Selenium.Core.Localization;
5+
using OpenQA.Selenium;
56
using OpenQA.Selenium.Appium;
67
using OpenQA.Selenium.Appium.Service;
7-
using OpenQA.Selenium.Remote;
88
using System;
99

1010
namespace Aquality.Appium.Mobile.Applications
@@ -16,7 +16,7 @@ public class Application : IMobileApplication
1616

1717
private TimeSpan timeoutImplicit;
1818

19-
public Application(AppiumDriver<AppiumWebElement> driver, AppiumLocalService driverService = null)
19+
public Application(AppiumDriver driver, AppiumLocalService driverService = null)
2020
{
2121
Driver = driver;
2222
DriverService = driverService;
@@ -31,11 +31,11 @@ private void SetImplicitlyWaitToDriver(TimeSpan timeout)
3131
timeoutImplicit = timeout;
3232
}
3333

34-
RemoteWebDriver IApplication.Driver => Driver;
34+
WebDriver IApplication.Driver => Driver;
3535

3636
public bool IsStarted => Driver.SessionId != null;
3737

38-
public AppiumDriver<AppiumWebElement> Driver { get; }
38+
public AppiumDriver Driver { get; }
3939

4040
public AppiumLocalService DriverService { get; }
4141

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Applications/ApplicationFactory.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public abstract class ApplicationFactory : IApplicationFactory
1515
{
1616
public abstract IMobileApplication Application { get; }
1717

18-
protected virtual AppiumDriver<AppiumWebElement> GetDriver(Uri serviceUrl)
18+
protected virtual AppiumDriver GetDriver(Uri serviceUrl)
1919
{
2020
var platformName = AqualityServices.ApplicationProfile.PlatformName;
2121
var driverOptions = AqualityServices.ApplicationProfile.DriverSettings.AppiumOptions;
@@ -24,17 +24,17 @@ protected virtual AppiumDriver<AppiumWebElement> GetDriver(Uri serviceUrl)
2424
() => CreateSession(platformName, serviceUrl, driverOptions, commandTimeout));
2525
}
2626

27-
protected virtual AppiumDriver<AppiumWebElement> CreateSession(PlatformName platformName, Uri serviceUrl, AppiumOptions driverOptions, TimeSpan commandTimeout)
27+
protected virtual AppiumDriver CreateSession(PlatformName platformName, Uri serviceUrl, AppiumOptions driverOptions, TimeSpan commandTimeout)
2828
{
2929
AqualityServices.LocalizedLogger.Info("loc.application.driver.remote", serviceUrl);
30-
AppiumDriver<AppiumWebElement> driver;
30+
AppiumDriver driver;
3131
switch (platformName)
3232
{
3333
case PlatformName.Android:
34-
driver = new AndroidDriver<AppiumWebElement>(serviceUrl, driverOptions, commandTimeout);
34+
driver = new AndroidDriver(serviceUrl, driverOptions, commandTimeout);
3535
break;
3636
case PlatformName.IOS:
37-
driver = new IOSDriver<AppiumWebElement>(serviceUrl, driverOptions, commandTimeout);
37+
driver = new IOSDriver(serviceUrl, driverOptions, commandTimeout);
3838
break;
3939
default:
4040
throw GetLoggedWrongPlatformNameException(platformName.ToString());

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Applications/IMobileApplication.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface IMobileApplication : IApplication
1212
/// <summary>
1313
/// Provides instance of Appium Driver for current application.
1414
/// </summary>
15-
new AppiumDriver<AppiumWebElement> Driver { get; }
15+
new AppiumDriver Driver { get; }
1616

1717
/// <summary>
1818
/// Closes application and disposes <see cref="DriverService"/> if it not null.

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Aquality.Appium.Mobile.csproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,27 @@
1414
<RepositoryType>git</RepositoryType>
1515
<PackageTags>appium mobile ios android automation</PackageTags>
1616
<PackageLicenseFile>LICENSE</PackageLicenseFile>
17-
<Copyright>Copyright 2020 Aquality Automation</Copyright>
17+
<Copyright>Copyright 2022 Aquality Automation</Copyright>
1818
<IsPackable>true</IsPackable>
1919
</PropertyGroup>
2020

21-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
21+
<PropertyGroup>
2222
<DocumentationFile>Aquality.Appium.Mobile.xml</DocumentationFile>
2323
<NoWarn>1701;1702;1591</NoWarn>
2424
</PropertyGroup>
2525

2626
<ItemGroup>
2727
<None Remove="Resources\Localization\be.json" />
2828
<None Remove="Resources\Localization\en.json" />
29+
<None Remove="Resources\Localization\pl.json" />
2930
<None Remove="Resources\Localization\ru.json" />
31+
<None Remove="Resources\Localization\uk.json" />
3032
<None Remove="Resources\settings.json" />
3133
</ItemGroup>
3234

3335
<ItemGroup>
36+
<EmbeddedResource Include="Resources\Localization\pl.json" />
37+
<EmbeddedResource Include="Resources\Localization\uk.json" />
3438
<EmbeddedResource Include="Resources\Localization\be.json" />
3539
<EmbeddedResource Include="Resources\Localization\en.json" />
3640
<EmbeddedResource Include="Resources\Localization\ru.json" />
@@ -45,8 +49,8 @@
4549
</ItemGroup>
4650

4751
<ItemGroup>
48-
<PackageReference Include="Appium.WebDriver" Version="4.3.1" />
49-
<PackageReference Include="Aquality.Selenium.Core" Version="1.6.1" />
52+
<PackageReference Include="Appium.WebDriver" Version="5.0.0-beta01" />
53+
<PackageReference Include="Aquality.Selenium.Core" Version="2.0.2" />
5054
</ItemGroup>
5155

5256
</Project>

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Aquality.Appium.Mobile.xml

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using OpenQA.Selenium.Appium;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace Aquality.Appium.Mobile.Configurations
7+
{
8+
/// <summary>
9+
/// Class to support known capabilities that became properties of <see cref="AppiumOptions"/>.
10+
/// </summary>
11+
public abstract class CapabilityBasedSettings
12+
{
13+
protected virtual IDictionary<string, Action<AppiumOptions, object>> KnownCapabilitySetters => new Dictionary<string, Action<AppiumOptions, object>>();
14+
15+
protected virtual void SetCapability(AppiumOptions options, KeyValuePair<string, object> capability)
16+
{
17+
try
18+
{
19+
options.AddAdditionalAppiumOption(capability.Key, capability.Value);
20+
}
21+
catch (ArgumentException exception)
22+
{
23+
if (exception.Message.StartsWith("There is already an option"))
24+
{
25+
SetKnownProperty(options, capability, exception);
26+
}
27+
else
28+
{
29+
throw;
30+
}
31+
}
32+
}
33+
34+
private void SetKnownProperty(AppiumOptions options, KeyValuePair<string, object> capability, ArgumentException exception)
35+
{
36+
if (KnownCapabilitySetters.ContainsKey(capability.Key))
37+
{
38+
KnownCapabilitySetters[capability.Key](options, capability.Value);
39+
}
40+
else
41+
{
42+
SetOptionByPropertyName(options, capability, exception);
43+
}
44+
}
45+
46+
private void SetOptionByPropertyName(AppiumOptions options, KeyValuePair<string, object> option, Exception exception)
47+
{
48+
var optionProperty = options
49+
.GetType()
50+
.GetProperties()
51+
.FirstOrDefault(property => IsPropertyNameMatchOption(property.Name, option.Key) && property.CanWrite)
52+
?? throw exception;
53+
var propertyType = optionProperty.PropertyType;
54+
var valueToSet = IsEnumValue(propertyType, option.Value)
55+
? ParseEnumValue(propertyType, option.Value)
56+
: option.Value;
57+
optionProperty.SetValue(options, valueToSet);
58+
}
59+
60+
private bool IsPropertyNameMatchOption(string propertyName, string optionKey)
61+
{
62+
return propertyName.Equals(optionKey, StringComparison.InvariantCultureIgnoreCase)
63+
|| optionKey.ToLowerInvariant().Contains(propertyName.ToLowerInvariant());
64+
}
65+
66+
private object ParseEnumValue(Type propertyType, object optionValue)
67+
{
68+
return optionValue is string
69+
? Enum.Parse(propertyType, optionValue.ToString(), ignoreCase: true)
70+
: Enum.ToObject(propertyType, Convert.ChangeType(optionValue, Enum.GetUnderlyingType(propertyType)));
71+
}
72+
73+
private bool IsEnumValue(Type propertyType, object optionValue)
74+
{
75+
var valueAsString = optionValue.ToString();
76+
if (!propertyType.IsEnum || string.IsNullOrEmpty(valueAsString))
77+
{
78+
return false;
79+
}
80+
var normalizedValue = char.ToUpper(valueAsString[0]) +
81+
(valueAsString.Length > 1 ? valueAsString.Substring(1) : string.Empty);
82+
return propertyType.IsEnumDefined(normalizedValue)
83+
|| propertyType.IsEnumDefined(valueAsString)
84+
|| (IsValueOfIntegralNumericType(optionValue)
85+
&& propertyType.IsEnumDefined(Convert.ChangeType(optionValue, Enum.GetUnderlyingType(propertyType))));
86+
}
87+
88+
private bool IsValueOfIntegralNumericType(object value)
89+
{
90+
return value is byte || value is sbyte
91+
|| value is ushort || value is short
92+
|| value is uint || value is int
93+
|| value is ulong || value is long;
94+
}
95+
}
96+
}

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Configurations/DeviceSettings.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using Aquality.Selenium.Core.Configurations;
22
using Aquality.Selenium.Core.Logging;
33
using Aquality.Selenium.Core.Utilities;
4-
using OpenQA.Selenium.Appium;
54
using System.Collections.Generic;
6-
using System.Linq;
75

86
namespace Aquality.Appium.Mobile.Configurations
97
{
@@ -28,16 +26,6 @@ private ISettingsFile GetDevicesSettings()
2826
return new JsonSettingsFile(devicesProfile);
2927
}
3028

31-
public AppiumOptions AppiumOptions
32-
{
33-
get
34-
{
35-
var deviceOptions = new AppiumOptions();
36-
Capabilities.ToList().ForEach(capability => deviceOptions.AddAdditionalCapability(capability.Key, capability.Value));
37-
return deviceOptions;
38-
}
39-
}
40-
41-
private IReadOnlyDictionary<string, object> Capabilities => settingsFile.GetValueDictionaryOrEmpty<object>($"{deviceKey}.capabilities");
29+
public IReadOnlyDictionary<string, object> Capabilities => settingsFile.GetValueDictionaryOrEmpty<object>($"{deviceKey}.capabilities");
4230
}
4331
}

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Configurations/DriverSettings.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Aquality.Appium.Mobile.Configurations
1010
{
11-
public class DriverSettings : IDriverSettings
11+
public class DriverSettings : CapabilityBasedSettings, IDriverSettings
1212
{
1313
private const string ApplicationPathKey = "applicationPath";
1414
private const string AppCapabilityKey = "app";
@@ -39,12 +39,12 @@ public virtual AppiumOptions AppiumOptions
3939
get
4040
{
4141
var options = new AppiumOptions();
42-
Capabilities.ToList().ForEach(capability => options.AddAdditionalCapability(capability.Key, capability.Value));
42+
Capabilities.ToList().ForEach(capability => SetCapability(options, capability));
4343
if (HasApplicationPath && ApplicationPath != null)
4444
{
45-
options.AddAdditionalCapability(AppCapabilityKey, ApplicationPath);
45+
SetCapability(options, new KeyValuePair<string, object> (AppCapabilityKey, ApplicationPath));
4646
}
47-
DeviceOptions.ToDictionary().ToList().ForEach(capability => options.AddAdditionalCapability(capability.Key, capability.Value));
47+
DeviceCapabilities.ToList().ForEach(capability => SetCapability(options, capability));
4848
return options;
4949
}
5050
}
@@ -59,13 +59,13 @@ public virtual string ApplicationPath
5959
}
6060
}
6161

62-
private AppiumOptions DeviceOptions
62+
private IReadOnlyDictionary<string, object> DeviceCapabilities
6363
{
6464
get
6565
{
6666
var deviceKey = settingsFile.GetValueOrDefault<string>($"{DriverSettingsPath}.{DeviceKeyKey}");
6767
var deviceSettings = new DeviceSettings(deviceKey);
68-
return deviceSettings.AppiumOptions;
68+
return deviceSettings.Capabilities;
6969
}
7070
}
7171
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using OpenQA.Selenium.Appium;
2+
using System.Collections.Generic;
23

34
namespace Aquality.Appium.Mobile.Configurations
45
{
@@ -8,8 +9,8 @@ namespace Aquality.Appium.Mobile.Configurations
89
public interface IDeviceSettings
910
{
1011
/// <summary>
11-
/// Options (capabilities) related to desired device.
12+
/// Capabilities related to desired device.
1213
/// </summary>
13-
AppiumOptions AppiumOptions { get; }
14+
IReadOnlyDictionary<string, object> Capabilities { get; }
1415
}
1516
}

Aquality.Appium.Mobile/src/Aquality.Appium.Mobile/Configurations/LocalServiceSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected virtual AppiumOptions AppiumOptions
2727
get
2828
{
2929
var options = new AppiumOptions();
30-
Capabilities.ToList().ForEach(capability => options.AddAdditionalCapability(capability.Key, capability.Value));
30+
Capabilities.ToList().ForEach(capability => options.AddAdditionalAppiumOption(capability.Key, capability.Value));
3131
return options;
3232
}
3333
}

0 commit comments

Comments
 (0)