mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 00:03:22 +00:00
Added UI test project
This commit is contained in:
@@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UiTests", "src\UiTests\UiTests.csproj", "{23FB637B-1705-485F-9464-078FCAF361A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||
@@ -446,6 +448,36 @@ Global
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -464,6 +496,7 @@ Global
|
||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
{23FB637B-1705-485F-9464-078FCAF361A8} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||
|
||||
12
src/UiTests/Categories/SmokeTest.cs
Executable file
12
src/UiTests/Categories/SmokeTest.cs
Executable file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Bit.UITests.Categories
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
public class SmokeTestAttribute : CategoryAttribute
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
||||
{
|
||||
}
|
||||
}
|
||||
28
src/UiTests/Extensions/IAppExtension.cs
Normal file
28
src/UiTests/Extensions/IAppExtension.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
namespace Bit.UITests.Extensions
|
||||
{
|
||||
public static class IAppExtension
|
||||
{
|
||||
public static void Wait(this IApp app, float seconds)
|
||||
{
|
||||
var waitTime = DateTime.Now + TimeSpan.FromSeconds(seconds);
|
||||
|
||||
app.WaitFor(() => DateTime.Now > waitTime);
|
||||
}
|
||||
|
||||
public static void WaitAndTapElement(this IApp app, Func<AppQuery, AppQuery> elementQuery)
|
||||
{
|
||||
app.WaitForElement(elementQuery);
|
||||
app.Tap(elementQuery);
|
||||
}
|
||||
|
||||
public static void WaitAndTapElement(this IApp app, Func<AppQuery, AppWebQuery> elementQuery)
|
||||
{
|
||||
app.WaitForElement(elementQuery);
|
||||
app.Tap(elementQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/UiTests/Helpers/AppState.cs
Normal file
17
src/UiTests/Helpers/AppState.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Bit.UITests.Helpers
|
||||
{
|
||||
public static class AppState
|
||||
{
|
||||
public static void CallBackdoor(this IApp app, string paramExample)
|
||||
{
|
||||
var args = new object[]
|
||||
{
|
||||
paramExample,
|
||||
};
|
||||
|
||||
app.Invoke("Zamboni", args);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/UiTests/Helpers/CustomWaitTimes.cs
Normal file
27
src/UiTests/Helpers/CustomWaitTimes.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Xamarin.UITest.Utils;
|
||||
|
||||
namespace Bit.UITests.Helpers
|
||||
{
|
||||
public class CustomWaitTimes : IWaitTimes
|
||||
{
|
||||
private readonly TimeSpan _timeout;
|
||||
public static readonly TimeSpan DefaultCustomTimeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
public CustomWaitTimes()
|
||||
{
|
||||
_timeout = DefaultCustomTimeout;
|
||||
}
|
||||
|
||||
public CustomWaitTimes(TimeSpan timeoutTimeSpan)
|
||||
{
|
||||
_timeout = timeoutTimeSpan;
|
||||
}
|
||||
|
||||
public TimeSpan GestureCompletionTimeout => _timeout;
|
||||
|
||||
public TimeSpan GestureWaitTimeout => _timeout;
|
||||
|
||||
public TimeSpan WaitForTimeout => _timeout;
|
||||
}
|
||||
}
|
||||
51
src/UiTests/Pages/Accounts/EnvironmentPage.cs
Normal file
51
src/UiTests/Pages/Accounts/EnvironmentPage.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Bit.UITests.Setup;
|
||||
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||
|
||||
namespace Bit.UITests.Pages.Accounts
|
||||
{
|
||||
public class EnvironmentPage : BasePage
|
||||
{
|
||||
private readonly Query _saveButton;
|
||||
private readonly Query _serverUrlInput;
|
||||
|
||||
public EnvironmentPage()
|
||||
: base()
|
||||
{
|
||||
if (OnAndroid)
|
||||
{
|
||||
_saveButton = x => x.Marked("save_button");
|
||||
_serverUrlInput = x => x.Marked("server_input");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OniOS)
|
||||
{
|
||||
_saveButton = x => x.Marked("save_button");
|
||||
_serverUrlInput = x => x.Marked("server_input");
|
||||
}
|
||||
}
|
||||
|
||||
protected override PlatformQuery Trait => new PlatformQuery
|
||||
{
|
||||
Android = x => x.Marked("server_input"),
|
||||
iOS = x => x.Marked("server_input"),
|
||||
};
|
||||
|
||||
public EnvironmentPage TapSaveAndNavigate()
|
||||
{
|
||||
App.Tap(_saveButton);
|
||||
WaitForPageToLeave();
|
||||
return this;
|
||||
}
|
||||
|
||||
public EnvironmentPage InputServerUrl(string serverUrl)
|
||||
{
|
||||
App.Tap(_serverUrlInput);
|
||||
App.EnterText(serverUrl);
|
||||
App.DismissKeyboard();
|
||||
App.Screenshot("After inserting the server url, I can see the field filled");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/UiTests/Pages/Accounts/HomePage.cs
Normal file
50
src/UiTests/Pages/Accounts/HomePage.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Bit.UITests.Setup;
|
||||
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||
|
||||
namespace Bit.UITests.Pages.Accounts
|
||||
{
|
||||
public class HomePage : BasePage
|
||||
{
|
||||
private readonly Query _loginButton;
|
||||
private readonly Query _environmentButton;
|
||||
|
||||
public HomePage()
|
||||
: base()
|
||||
{
|
||||
if (OnAndroid)
|
||||
{
|
||||
_loginButton = x => x.Marked("homepage_login_button");
|
||||
|
||||
//TODO a11y uses the same fields as the UI tests and we're prioritising that
|
||||
// improve this by getting the app runtime locale and use the i18n service here instead
|
||||
_environmentButton = x => x.Marked("Options");
|
||||
//_environmentButton = x => x.Marked("environment_button");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OniOS)
|
||||
{
|
||||
_loginButton = x => x.Marked("homepage_login_button");
|
||||
_environmentButton = x => x.Marked("Options");
|
||||
}
|
||||
}
|
||||
|
||||
protected override PlatformQuery Trait => new PlatformQuery
|
||||
{
|
||||
Android = x => x.Marked("logo_image"),
|
||||
iOS = x => x.Marked("logo_image"),
|
||||
};
|
||||
|
||||
public HomePage TapLoginAndNavigate()
|
||||
{
|
||||
App.Tap(_loginButton);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HomePage TapEnvironmentAndNavigate()
|
||||
{
|
||||
App.Tap(_environmentButton);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/UiTests/Pages/Accounts/LoginPage.cs
Normal file
90
src/UiTests/Pages/Accounts/LoginPage.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using Bit.UITests.Setup;
|
||||
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||
|
||||
namespace Bit.UITests.Pages.Accounts
|
||||
{
|
||||
public class LoginPage : BasePage
|
||||
{
|
||||
private readonly Query _loginButton;
|
||||
private readonly Query _cancelButton;
|
||||
private readonly Query _passwordVisibilityToggle;
|
||||
|
||||
private readonly Query _emailInput;
|
||||
private readonly Query _passwordInput;
|
||||
|
||||
|
||||
public LoginPage()
|
||||
: base()
|
||||
{
|
||||
if (OnAndroid)
|
||||
{
|
||||
_loginButton = x => x.Marked("loginpage_login_button");
|
||||
_cancelButton = x => x.Marked("cancel_button");
|
||||
|
||||
//TODO a11y uses the same fields as the UI tests and we're prioritising that
|
||||
// improve this by getting the app runtime locale and use the i18n service here instead
|
||||
_passwordVisibilityToggle = x => x.Marked("Toggle Visibility");
|
||||
|
||||
_emailInput = x => x.Marked("email_input");
|
||||
_passwordInput = x => x.Marked("password_input");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OniOS)
|
||||
{
|
||||
_loginButton = x => x.Marked("loginpage_login_button");
|
||||
_cancelButton = x => x.Marked("cancel_button");
|
||||
_passwordVisibilityToggle = x => x.Marked("Toggle Visibility");
|
||||
|
||||
_emailInput = x => x.Marked("email_input");
|
||||
_passwordInput = x => x.Marked("password_input");
|
||||
}
|
||||
}
|
||||
|
||||
protected override PlatformQuery Trait => new PlatformQuery
|
||||
{
|
||||
Android = x => x.Marked("email_input"),
|
||||
iOS = x => x.Marked("email_input"),
|
||||
};
|
||||
|
||||
public LoginPage TapLoginAndNavigate()
|
||||
{
|
||||
App.Tap(_loginButton);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage TapCancelAndNavigate()
|
||||
{
|
||||
App.Tap(_cancelButton);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage TapPasswordVisibilityToggle()
|
||||
{
|
||||
App.Tap(_passwordVisibilityToggle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage InputEmail(string email)
|
||||
{
|
||||
App.Tap(_emailInput);
|
||||
App.EnterText(email);
|
||||
App.DismissKeyboard();
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage InputPassword(string password)
|
||||
{
|
||||
App.Tap(_passwordInput);
|
||||
App.EnterText(password);
|
||||
App.DismissKeyboard();
|
||||
|
||||
App.Screenshot("After I input the email and password fields, I can see both fields filled");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
54
src/UiTests/Pages/ExamplePage.cs
Normal file
54
src/UiTests/Pages/ExamplePage.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using Bit.UITests.Setup;
|
||||
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||
|
||||
namespace Bit.UITests.Pages
|
||||
{
|
||||
public class ExamplePage : BasePage
|
||||
{
|
||||
private readonly Query _loginButton;
|
||||
private readonly Query _passwordInput;
|
||||
|
||||
public ExamplePage()
|
||||
: base()
|
||||
{
|
||||
if (OnAndroid)
|
||||
{
|
||||
_loginButton = x => x.Marked("loginpage_login_button");
|
||||
_passwordInput = x => x.Marked("password_input");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OniOS)
|
||||
{
|
||||
_loginButton = x => x.Marked("loginpage_login_button");
|
||||
_passwordInput = x => x.Marked("password_input");
|
||||
}
|
||||
}
|
||||
|
||||
protected override PlatformQuery Trait => new PlatformQuery
|
||||
{
|
||||
Android = x => x.Marked("password_input"),
|
||||
iOS = x => x.Marked("password_input"),
|
||||
};
|
||||
|
||||
public ExamplePage TapLogin()
|
||||
{
|
||||
App.Tap(_loginButton);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExamplePage InputPassword(string password)
|
||||
{
|
||||
App.Tap(_passwordInput);
|
||||
App.EnterText(password);
|
||||
App.DismissKeyboard();
|
||||
|
||||
App.Screenshot("After I input the email and password fields, I can see both fields filled");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
26
src/UiTests/Pages/TabsPage.cs
Normal file
26
src/UiTests/Pages/TabsPage.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Bit.UITests.Setup;
|
||||
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||
|
||||
namespace Bit.UITests.Pages
|
||||
{
|
||||
public class TabsPage : BasePage
|
||||
{
|
||||
private readonly Query _vaultTab;
|
||||
private readonly Query _sendTab;
|
||||
|
||||
public TabsPage()
|
||||
: base()
|
||||
{
|
||||
|
||||
_vaultTab = x => x.Marked("My Vault");
|
||||
_sendTab = x => x.Marked("Send");
|
||||
}
|
||||
|
||||
protected override PlatformQuery Trait => new PlatformQuery
|
||||
{
|
||||
Android = x => x.Marked("Send"),
|
||||
iOS = x => x.Marked("Send"),
|
||||
};
|
||||
}
|
||||
}
|
||||
93
src/UiTests/Setup/AppManager.cs
Normal file
93
src/UiTests/Setup/AppManager.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Bit.UITests.Helpers;
|
||||
using Bit.UITests.Setup.SimulatorManager;
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Bit.UITests.Setup
|
||||
{
|
||||
internal static class AppManager
|
||||
{
|
||||
private static readonly string _slnPath = GetSlnPath();
|
||||
|
||||
private const string AndroidPackageName = "com.x8bit.bitwarden";
|
||||
private const string IosBundleId = "com.x8bit.bitwarden";
|
||||
private static readonly string _apkPath = Path.Combine(_slnPath, "Android", "bin", "Debug", $"{AndroidPackageName}.apk");
|
||||
private static readonly string _iosPath = Path.Combine("..", "..", "..", $"{IosBundleId}.app");
|
||||
|
||||
private static IApp _app;
|
||||
|
||||
private static Platform? _platform;
|
||||
|
||||
public static IApp App
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_app == null)
|
||||
{
|
||||
throw new NullReferenceException("'AppManager.App' not set. Call 'AppManager.StartApp()' before trying to access it.");
|
||||
}
|
||||
|
||||
return _app;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Platform Platform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_platform == null)
|
||||
{
|
||||
throw new NullReferenceException("'AppManager.Platform' not set.");
|
||||
}
|
||||
|
||||
return _platform.Value;
|
||||
}
|
||||
|
||||
set => _platform = value;
|
||||
}
|
||||
|
||||
public static void StartApp()
|
||||
{
|
||||
Console.WriteLine($"TestEnvironment.IsTestCloud: {TestEnvironment.IsTestCloud}");
|
||||
Console.WriteLine($"TestEnvironment.Platform: {TestEnvironment.Platform}");
|
||||
Console.WriteLine($"Platform: {Platform}");
|
||||
|
||||
switch (Platform, TestEnvironment.IsTestCloud)
|
||||
{
|
||||
case (Platform.Android, false):
|
||||
_app = ConfigureApp
|
||||
.Android
|
||||
.InstalledApp(AndroidPackageName)
|
||||
//.ApkFile(_apkPath)
|
||||
.StartApp();
|
||||
break;
|
||||
case (Platform.iOS, false):
|
||||
_app = ConfigureApp
|
||||
.iOS
|
||||
.SetDeviceByName("iPhone X") //NOTE Get Devices name in terminal: xcrun instruments -s devices
|
||||
.AppBundle(_iosPath)
|
||||
// .InstalledApp(IosBundleId)
|
||||
.StartApp();
|
||||
break;
|
||||
case (Platform.Android, true):
|
||||
_app = ConfigureApp.Android.WaitTimes(new CustomWaitTimes()).StartApp();
|
||||
break;
|
||||
case (Platform.iOS, true):
|
||||
_app = ConfigureApp.iOS.WaitTimes(new CustomWaitTimes()).StartApp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string GetSlnPath()
|
||||
{
|
||||
string currentFile = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
|
||||
var fi = new FileInfo(currentFile);
|
||||
string path = fi.Directory!.Parent!.Parent!.Parent!.FullName;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/UiTests/Setup/BasePage.cs
Normal file
70
src/UiTests/Setup/BasePage.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Bit.UITests.Extensions;
|
||||
using Bit.UITests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Bit.UITests.Setup
|
||||
{
|
||||
public abstract class BasePage
|
||||
{
|
||||
|
||||
protected BasePage()
|
||||
{
|
||||
AssertOnPage(CustomWaitTimes.DefaultCustomTimeout);
|
||||
App.Screenshot("On " + GetType().Name);
|
||||
}
|
||||
|
||||
protected IApp App => AppManager.App;
|
||||
|
||||
protected bool OnAndroid => AppManager.Platform == Platform.Android;
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
protected bool OniOS => AppManager.Platform == Platform.iOS;
|
||||
|
||||
protected abstract PlatformQuery Trait { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the trait is still present. Defaults to no wait.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to wait before the assertion fails</param>
|
||||
public void AssertOnPage(TimeSpan? timeout = default(TimeSpan?))
|
||||
{
|
||||
var message = "Unable to verify on page: " + GetType().Name;
|
||||
|
||||
if (timeout == null)
|
||||
{
|
||||
Assert.IsNotEmpty(App.Query(Trait.Current), message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotThrow(() => App.WaitForElement(Trait.Current, timeout: timeout), message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the trait is no longer present. Defaults to a 5 second wait.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to wait before the assertion fails</param>
|
||||
public void WaitForPageToLeave(TimeSpan? timeout = default(TimeSpan?))
|
||||
{
|
||||
timeout ??= TimeSpan.FromSeconds(5);
|
||||
var message = "Unable to verify *not* on page: " + GetType().Name;
|
||||
|
||||
Assert.DoesNotThrow(() => App.WaitForNoElement(Trait.Current, timeout: timeout), message);
|
||||
}
|
||||
|
||||
|
||||
public BasePage Wait(int seconds)
|
||||
{
|
||||
App.Wait(seconds);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BasePage Back()
|
||||
{
|
||||
App.Back();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/UiTests/Setup/BaseTestFixture.cs
Normal file
52
src/UiTests/Setup/BaseTestFixture.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Bit.UITests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Bit.UITests.Setup
|
||||
{
|
||||
|
||||
[TestFixture(Platform.Android)]
|
||||
[TestFixture(Platform.iOS)]
|
||||
public abstract class BaseTestFixture
|
||||
{
|
||||
protected IApp App => AppManager.App;
|
||||
|
||||
protected bool OnAndroid => AppManager.Platform == Platform.Android;
|
||||
|
||||
protected bool OniOS => AppManager.Platform == Platform.iOS;
|
||||
|
||||
protected BaseTestFixture(Platform platform)
|
||||
{
|
||||
AppManager.Platform = platform;
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public virtual void BeforeEachTest()
|
||||
{
|
||||
AppManager.StartApp();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (TestContext.CurrentContext.Result.Outcome.Status != TestStatus.Failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (App == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (TestEnvironment.Platform != TestPlatform.Local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE uncomment to help debug failing tests
|
||||
//App.Repl();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/UiTests/Setup/Csharp9Support.cs
Normal file
9
src/UiTests/Setup/Csharp9Support.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// NOTE C# 9.0 support
|
||||
// https://stackoverflow.com/a/64749403/859738
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
public class IsExternalInit { }
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
||||
}
|
||||
39
src/UiTests/Setup/PlatformQuery.cs
Executable file
39
src/UiTests/Setup/PlatformQuery.cs
Executable file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
namespace Bit.UITests.Setup
|
||||
{
|
||||
public class PlatformQuery
|
||||
{
|
||||
Func<AppQuery, AppQuery> _current;
|
||||
public Func<AppQuery, AppQuery> Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_current == null)
|
||||
throw new NullReferenceException("Trait not set for current platform");
|
||||
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public Func<AppQuery, AppQuery> Android
|
||||
{
|
||||
set
|
||||
{
|
||||
if (AppManager.Platform == Platform.Android)
|
||||
_current = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Func<AppQuery, AppQuery> iOS
|
||||
{
|
||||
set
|
||||
{
|
||||
if (AppManager.Platform == Platform.iOS)
|
||||
_current = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/UiTests/Setup/SimulatorManager/InstrumentsRunner.cs
Executable file
47
src/UiTests/Setup/SimulatorManager/InstrumentsRunner.cs
Executable file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Bit.UITests.Setup.SimulatorManager
|
||||
{
|
||||
class InstrumentsRunner
|
||||
{
|
||||
static string[] GetInstrumentsOutput()
|
||||
{
|
||||
const string cmd = "/usr/bin/xcrun";
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = cmd,
|
||||
Arguments = "instruments -s devices",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
var proc = new Process();
|
||||
proc.StartInfo = startInfo;
|
||||
proc.Start();
|
||||
var result = proc.StandardOutput.ReadToEnd();
|
||||
proc.WaitForExit();
|
||||
|
||||
var lines = result.Split('\n');
|
||||
return lines;
|
||||
}
|
||||
|
||||
public Simulator[] GetListOfSimulators()
|
||||
{
|
||||
var simulators = new List<Simulator>();
|
||||
var lines = GetInstrumentsOutput();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var sim = new Simulator(line);
|
||||
if (sim.IsValid())
|
||||
{
|
||||
simulators.Add(sim);
|
||||
}
|
||||
}
|
||||
|
||||
return simulators.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/UiTests/Setup/SimulatorManager/IosSimulatorsManager.cs
Executable file
38
src/UiTests/Setup/SimulatorManager/IosSimulatorsManager.cs
Executable file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Configuration;
|
||||
|
||||
namespace Bit.UITests.Setup.SimulatorManager
|
||||
{
|
||||
internal static class IosSimulatorsManager
|
||||
{
|
||||
|
||||
public static iOSAppConfigurator SetDeviceByName(this iOSAppConfigurator configurator, string simulatorName)
|
||||
{
|
||||
var deviceId = GetDeviceId(simulatorName);
|
||||
return configurator.DeviceIdentifier(deviceId);
|
||||
}
|
||||
|
||||
public static string GetDeviceId(string simulatorName)
|
||||
{
|
||||
if (!TestEnvironment.Platform.Equals(TestPlatform.Local))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// See below for the InstrumentsRunner class.
|
||||
IEnumerable<Simulator> simulators = new InstrumentsRunner().GetListOfSimulators();
|
||||
|
||||
var simulator = simulators.FirstOrDefault(x => x.Name.Contains(simulatorName));
|
||||
|
||||
if (simulator == null)
|
||||
{
|
||||
throw new ArgumentException("Could not find a device identifier for '" + simulatorName + "'.", "simulatorName");
|
||||
}
|
||||
|
||||
return simulator.GUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/UiTests/Setup/SimulatorManager/Simulator.cs
Executable file
49
src/UiTests/Setup/SimulatorManager/Simulator.cs
Executable file
@@ -0,0 +1,49 @@
|
||||
namespace Bit.UITests.Setup.SimulatorManager
|
||||
{
|
||||
internal class Simulator
|
||||
{
|
||||
public Simulator(string line)
|
||||
{
|
||||
ParseLine(line);
|
||||
}
|
||||
|
||||
public string Line { get; private set; }
|
||||
|
||||
public string GUID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(GUID) && !(string.IsNullOrWhiteSpace(Name));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Line;
|
||||
}
|
||||
|
||||
void ParseLine(string line)
|
||||
{
|
||||
GUID = string.Empty;
|
||||
Name = string.Empty;
|
||||
Line = string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Line = line.Trim();
|
||||
var idx1 = line.IndexOf(" [");
|
||||
|
||||
if (idx1 < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Name = Line.Substring(0, idx1).Trim();
|
||||
GUID = Line.Substring(idx1 + 2, 36).Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/UiTests/Tests/LoginTests.cs
Normal file
60
src/UiTests/Tests/LoginTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Bit.UITests.Categories;
|
||||
using Bit.UITests.Pages;
|
||||
using Bit.UITests.Pages.Accounts;
|
||||
using Bit.UITests.Setup;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Bit.UiTests.Tests
|
||||
{
|
||||
public class LoginTests : BaseTestFixture
|
||||
{
|
||||
private const string _testServerUrl = "zamboni";
|
||||
private const string _email = "zamboni";
|
||||
private const string _password = "zamboni";
|
||||
|
||||
public LoginTests(Platform platform)
|
||||
: base(platform)
|
||||
{
|
||||
}
|
||||
|
||||
//[Test]
|
||||
public void OpenREPL()
|
||||
{
|
||||
App.Repl();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[SmokeTest]
|
||||
public void WaitForAppToLoad()
|
||||
{
|
||||
new HomePage();
|
||||
|
||||
App.Screenshot("App loaded with success!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoginWithSuccess()
|
||||
{
|
||||
|
||||
new HomePage()
|
||||
.TapEnvironmentAndNavigate();
|
||||
|
||||
new EnvironmentPage()
|
||||
.InputServerUrl(_testServerUrl)
|
||||
.TapSaveAndNavigate();
|
||||
|
||||
new HomePage()
|
||||
.TapLoginAndNavigate();
|
||||
|
||||
new LoginPage()
|
||||
.InputEmail(_email)
|
||||
.InputPassword(_password)
|
||||
.TapLoginAndNavigate();
|
||||
|
||||
new TabsPage();
|
||||
|
||||
App.Screenshot("After logging in with success, I can see the vault");
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/UiTests/UiTests.csproj
Normal file
59
src/UiTests/UiTests.csproj
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{23FB637B-1705-485F-9464-078FCAF361A8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Bit.UiTests</RootNamespace>
|
||||
<AssemblyName>UiTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="Xamarin.UITest" Version="3.2.6" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Categories\SmokeTest.cs" />
|
||||
<Compile Include="Extensions\IAppExtension.cs" />
|
||||
<Compile Include="Setup\SimulatorManager\InstrumentsRunner.cs" />
|
||||
<Compile Include="Setup\SimulatorManager\IosSimulatorsManager.cs" />
|
||||
<Compile Include="Setup\SimulatorManager\Simulator.cs" />
|
||||
<Compile Include="Setup\AppManager.cs" />
|
||||
<Compile Include="Setup\BasePage.cs" />
|
||||
<Compile Include="Setup\BaseTestFixture.cs" />
|
||||
<Compile Include="Setup\Csharp9Support.cs" />
|
||||
<Compile Include="Setup\PlatformQuery.cs" />
|
||||
<Compile Include="Helpers\CustomWaitTimes.cs" />
|
||||
<Compile Include="Helpers\AppState.cs" />
|
||||
<Compile Include="Tests\LoginTests.cs" />
|
||||
<Compile Include="Pages\Accounts\EnvironmentPage.cs" />
|
||||
<Compile Include="Pages\Accounts\HomePage.cs" />
|
||||
<Compile Include="Pages\Accounts\LoginPage.cs" />
|
||||
<Compile Include="Pages\TabsPage.cs" />
|
||||
<Compile Include="Pages\ExamplePage.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
Reference in New Issue
Block a user