mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
6 Commits
mobiletf/p
...
uitests
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cc1a501a5 | ||
|
|
b0a8694801 | ||
|
|
b9c1ab7c1d | ||
|
|
4d96b091f7 | ||
|
|
88fee155db | ||
|
|
f930028920 |
@@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UiTests", "src\UiTests\UiTests.csproj", "{23FB637B-1705-485F-9464-078FCAF361A8}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
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|iPhone.Build.0 = Release|iPhone
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -464,6 +496,7 @@ Global
|
|||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ namespace Bit.Droid
|
|||||||
Intent?.Validate();
|
Intent?.Validate();
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
//if (!CoreHelpers.InDebugMode())
|
||||||
{
|
//{
|
||||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
// Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||||
}
|
//}
|
||||||
|
|
||||||
#if !DEBUG && !FDROID
|
#if !DEBUG && !FDROID
|
||||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" AutomationId="save_button"/>
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
@@ -34,7 +34,8 @@
|
|||||||
Placeholder="ex. https://bitwarden.company.com"
|
Placeholder="ex. https://bitwarden.company.com"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}" />
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
|
AutomationId="server_input"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n SelfHostedEnvironmentFooter}"
|
Text="{u:I18n SelfHostedEnvironmentFooter}"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
Priority="-1"
|
Priority="-1"
|
||||||
UseOriginalImage="True"
|
UseOriginalImage="True"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Account}" />
|
AutomationProperties.Name="{u:I18n Account}"/>
|
||||||
<ToolbarItem
|
<ToolbarItem
|
||||||
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
@@ -37,15 +37,18 @@
|
|||||||
<Image
|
<Image
|
||||||
x:Name="_logo"
|
x:Name="_logo"
|
||||||
Source="logo.png"
|
Source="logo.png"
|
||||||
VerticalOptions="Center" />
|
VerticalOptions="Center"
|
||||||
|
AutomationId="logo_image"
|
||||||
|
/>
|
||||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||||
StyleClass="text-lg"
|
StyleClass="text-lg"
|
||||||
HorizontalTextAlignment="Center">
|
HorizontalTextAlignment="Center"/>
|
||||||
</Label>
|
|
||||||
<StackLayout Spacing="5">
|
<StackLayout Spacing="5">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
Clicked="LogIn_Clicked" />
|
Clicked="LogIn_Clicked"
|
||||||
|
AutomationId="homepage_login_button"/>
|
||||||
<Button Text="{u:I18n CreateAccount}"
|
<Button Text="{u:I18n CreateAccount}"
|
||||||
Clicked="Register_Clicked" />
|
Clicked="Register_Clicked" />
|
||||||
<Button Text="{u:I18n LogInSso}"
|
<Button Text="{u:I18n LogInSso}"
|
||||||
|
|||||||
@@ -56,7 +56,8 @@
|
|||||||
x:Name="_email"
|
x:Name="_email"
|
||||||
Text="{Binding Email}"
|
Text="{Binding Email}"
|
||||||
Keyboard="Email"
|
Keyboard="Email"
|
||||||
StyleClass="box-value">
|
StyleClass="box-value"
|
||||||
|
AutomationId="email_input">
|
||||||
<VisualStateManager.VisualStateGroups>
|
<VisualStateManager.VisualStateGroups>
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
<VisualState x:Name="Disabled">
|
<VisualState x:Name="Disabled">
|
||||||
@@ -92,7 +93,8 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding LogInCommand}" />
|
ReturnCommand="{Binding LogInCommand}"
|
||||||
|
AutomationId="password_input"/>
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text="{Binding ShowPasswordIcon}"
|
Text="{Binding ShowPasswordIcon}"
|
||||||
@@ -107,10 +109,12 @@
|
|||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
Clicked="LogIn_Clicked" />
|
Clicked="LogIn_Clicked"
|
||||||
|
AutomationId="loginpage_login_button"/>
|
||||||
<Button Text="{u:I18n Cancel}"
|
<Button Text="{u:I18n Cancel}"
|
||||||
IsVisible="{Binding ShowCancelButton}"
|
IsVisible="{Binding ShowCancelButton}"
|
||||||
Clicked="Cancel_Clicked" />
|
Clicked="Cancel_Clicked"
|
||||||
|
AutomationId="cancel_button"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ namespace Bit.App.Pages
|
|||||||
VerticalOptions = LayoutOptions.CenterAndExpand,
|
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||||
HorizontalOptions = LayoutOptions.Center,
|
HorizontalOptions = LayoutOptions.Center,
|
||||||
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
||||||
|
AutomationId = "activity_indicator"
|
||||||
};
|
};
|
||||||
if (targetView != null)
|
if (targetView != null)
|
||||||
{
|
{
|
||||||
|
|||||||
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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/UiTests/Extensions/IAppExtension.cs
Normal file
34
src/UiTests/Extensions/IAppExtension.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WaitAndScreenshot(this IApp app, string screenshotTitle)
|
||||||
|
{
|
||||||
|
app.Wait(1); //screenshots tend to be too fast and not capture the previous actions
|
||||||
|
app.Screenshot(screenshotTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/UiTests/Helpers/AppState.cs
Normal file
14
src/UiTests/Helpers/AppState.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Xamarin.UITest;
|
||||||
|
|
||||||
|
namespace Bit.UITests.Helpers
|
||||||
|
{
|
||||||
|
public static class AppState
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void EnableScreenshots(this IApp app)
|
||||||
|
{
|
||||||
|
//TODO placeholder, mobile app needs the service / setting to enable Android screenshots first
|
||||||
|
app.Invoke("Zamboni");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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(10);
|
||||||
|
|
||||||
|
public CustomWaitTimes()
|
||||||
|
{
|
||||||
|
_timeout = DefaultCustomTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomWaitTimes(TimeSpan timeoutTimeSpan)
|
||||||
|
{
|
||||||
|
_timeout = timeoutTimeSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan GestureCompletionTimeout => _timeout;
|
||||||
|
|
||||||
|
public TimeSpan GestureWaitTimeout => _timeout;
|
||||||
|
|
||||||
|
public TimeSpan WaitForTimeout => _timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/UiTests/Pages/Accounts/EnvironmentPage.cs
Normal file
52
src/UiTests/Pages/Accounts/EnvironmentPage.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Bit.UITests.Extensions;
|
||||||
|
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.ClearText(_serverUrlInput);
|
||||||
|
App.EnterText(_serverUrlInput, serverUrl);
|
||||||
|
App.DismissKeyboard();
|
||||||
|
App.WaitAndScreenshot("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.Extensions;
|
||||||
|
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.ClearText(_emailInput);
|
||||||
|
App.EnterText(_emailInput, email);
|
||||||
|
App.DismissKeyboard();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginPage InputPassword(string password)
|
||||||
|
{
|
||||||
|
App.Tap(_passwordInput);
|
||||||
|
App.EnterText(password);
|
||||||
|
App.DismissKeyboard();
|
||||||
|
App.WaitAndScreenshot("After I input the email and password fields, I can see both fields filled");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/UiTests/Pages/ExamplePage.cs
Normal file
55
src/UiTests/Pages/ExamplePage.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.UITests.Extensions;
|
||||||
|
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.WaitAndScreenshot("After I input the email and password fields, I can see both fields filled");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/UiTests/Pages/TabsPage.cs
Normal file
70
src/UiTests/Pages/TabsPage.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.UITests.Extensions;
|
||||||
|
using Bit.UITests.Helpers;
|
||||||
|
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;
|
||||||
|
private readonly Query _accountSwitchingAvatar;
|
||||||
|
private readonly Query _accountSwitchingAddAccount;
|
||||||
|
|
||||||
|
|
||||||
|
public TabsPage()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
_vaultTab = x => x.Marked("My Vault");
|
||||||
|
_sendTab = x => x.Marked("Send");
|
||||||
|
_accountSwitchingAvatar = x => x.Marked("Account");
|
||||||
|
_accountSwitchingAddAccount = x => x.Marked("Add Account");
|
||||||
|
|
||||||
|
WaitForNoLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override PlatformQuery Trait => new PlatformQuery
|
||||||
|
{
|
||||||
|
Android = x => x.Marked("Send"),
|
||||||
|
iOS = x => x.Marked("Send"),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TabsPage WaitForNoLoader()
|
||||||
|
{
|
||||||
|
App.WaitForNoElement(LoadingIndicator, timeout: CustomWaitTimes.DefaultCustomTimeout);
|
||||||
|
App.WaitAndScreenshot("Page finished loading");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabsPage TapAccountSwitchingAvatar()
|
||||||
|
{
|
||||||
|
App.WaitForElement(_accountSwitchingAvatar);
|
||||||
|
App.Tap(_accountSwitchingAvatar);
|
||||||
|
App.WaitAndScreenshot("Tapping the avatar, I can see the account switching panel");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabsPage TapAccountSwitchingAddAccount()
|
||||||
|
{
|
||||||
|
App.Tap(_accountSwitchingAddAccount);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabsPage TapVaultTab()
|
||||||
|
{
|
||||||
|
App.Tap(_vaultTab);
|
||||||
|
App.WaitAndScreenshot("Tapping the Vault tab, I can see the Vault view");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabsPage TapSTab()
|
||||||
|
{
|
||||||
|
App.Tap(_sendTab);
|
||||||
|
App.WaitAndScreenshot("Tapping the Send tab, I can see the Send view");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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", "release", $"{AndroidPackageName}-Signed.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/UiTests/Setup/BasePage.cs
Normal file
73
src/UiTests/Setup/BasePage.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.UITests.Extensions;
|
||||||
|
using Bit.UITests.Helpers;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Xamarin.UITest;
|
||||||
|
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
|
||||||
|
|
||||||
|
namespace Bit.UITests.Setup
|
||||||
|
{
|
||||||
|
public abstract class BasePage
|
||||||
|
{
|
||||||
|
|
||||||
|
protected BasePage()
|
||||||
|
{
|
||||||
|
AssertOnPage(CustomWaitTimes.DefaultCustomTimeout);
|
||||||
|
App.Screenshot("On " + GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly Query LoadingIndicator = x => x.Marked("activity_indicator");
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/UiTests/Tests/LoginTests.cs
Normal file
84
src/UiTests/Tests/LoginTests.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
record AccountCredentials(string ServerUrl, string Email, string Password);
|
||||||
|
|
||||||
|
private AccountCredentials[] _accounts =
|
||||||
|
{
|
||||||
|
new ("", "", ""),
|
||||||
|
new ("", "", ""),
|
||||||
|
new ("", "", ""),
|
||||||
|
};
|
||||||
|
|
||||||
|
public LoginTests(Platform platform)
|
||||||
|
: base(platform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[SmokeTest]
|
||||||
|
public void WaitForAppToLoad()
|
||||||
|
{
|
||||||
|
new HomePage();
|
||||||
|
|
||||||
|
App.Screenshot("App loaded with success!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//[Test]
|
||||||
|
public void LoginWithSuccess()
|
||||||
|
{
|
||||||
|
Login(_accounts[0]);
|
||||||
|
new TabsPage();
|
||||||
|
App.Repl();
|
||||||
|
|
||||||
|
App.Screenshot("After logging in with success, I can see the vault");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AccountSwitchWithSuccess()
|
||||||
|
{
|
||||||
|
Login(_accounts[0], true);
|
||||||
|
new TabsPage()
|
||||||
|
.TapAccountSwitchingAvatar()
|
||||||
|
.TapAccountSwitchingAddAccount();
|
||||||
|
|
||||||
|
Login(_accounts[1]);
|
||||||
|
new TabsPage()
|
||||||
|
.TapAccountSwitchingAvatar()
|
||||||
|
.TapAccountSwitchingAddAccount();
|
||||||
|
|
||||||
|
Login(_accounts[2]);
|
||||||
|
|
||||||
|
new TabsPage()
|
||||||
|
.TapAccountSwitchingAvatar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Login(AccountCredentials account, bool changeEnvironment = false)
|
||||||
|
{
|
||||||
|
if (changeEnvironment)
|
||||||
|
{
|
||||||
|
new HomePage()
|
||||||
|
.TapEnvironmentAndNavigate();
|
||||||
|
new EnvironmentPage()
|
||||||
|
.InputServerUrl(account.ServerUrl)
|
||||||
|
.TapSaveAndNavigate();
|
||||||
|
}
|
||||||
|
|
||||||
|
new HomePage()
|
||||||
|
.TapLoginAndNavigate();
|
||||||
|
|
||||||
|
new LoginPage()
|
||||||
|
.InputEmail(account.Email)
|
||||||
|
.InputPassword(account.Password)
|
||||||
|
.TapLoginAndNavigate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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