New toast action to show toast messages from XAML

Code cleanup for Sonar
Tests corrections
This commit is contained in:
BONNEVILLE Geoffroy
2018-06-21 11:13:40 +02:00
parent 803dab0fb5
commit 559fbe65c3
15 changed files with 110 additions and 54 deletions

View File

@@ -0,0 +1,33 @@
using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity;
using ModernKeePass.Common;
namespace ModernKeePass.Actions
{
public class ToastAction : DependencyObject, IAction
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ToastAction), new PropertyMetadata(string.Empty));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(ToastAction), new PropertyMetadata(string.Empty));
public object Execute(object sender, object parameter)
{
ToastNotificationHelper.ShowGenericToast(Title, Message);
return null;
}
}
}

View File

@@ -114,7 +114,8 @@ namespace ModernKeePass
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{ {
//TODO: Load state from previously terminated application // Load state from previously terminated application
await SuspensionManager.RestoreAsync();
#if DEBUG #if DEBUG
await MessageDialogHelper.ShowNotificationDialog("App terminated", "Windows or an error made the app terminate"); await MessageDialogHelper.ShowNotificationDialog("App terminated", "Windows or an error made the app terminate");
#endif #endif
@@ -164,7 +165,7 @@ namespace ModernKeePass
/// <param name="e">Details about the navigation failure</param> /// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e) void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{ {
throw new Exception("Failed to load Page " + e.SourcePageType.FullName); throw new NavigationException(e.SourcePageType);
} }
/// <summary> /// <summary>
@@ -174,7 +175,7 @@ namespace ModernKeePass
/// </summary> /// </summary>
/// <param name="sender">The source of the suspend request.</param> /// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param> /// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e) private async void OnSuspending(object sender, SuspendingEventArgs e)
{ {
var deferral = e.SuspendingOperation.GetDeferral(); var deferral = e.SuspendingOperation.GetDeferral();
var database = DatabaseService.Instance; var database = DatabaseService.Instance;
@@ -187,6 +188,7 @@ namespace ModernKeePass
{ {
ToastNotificationHelper.ShowErrorToast(exception); ToastNotificationHelper.ShowErrorToast(exception);
} }
await SuspensionManager.SaveAsync();
deferral.Complete(); deferral.Complete();
} }

View File

@@ -17,7 +17,7 @@ namespace ModernKeePass.Common
/// carry across sessions, but that should be discarded when an application crashes or is /// carry across sessions, but that should be discarded when an application crashes or is
/// upgraded. /// upgraded.
/// </summary> /// </summary>
internal sealed class SuspensionManager internal static class SuspensionManager
{ {
private static Dictionary<string, object> _sessionState = new Dictionary<string, object>(); private static Dictionary<string, object> _sessionState = new Dictionary<string, object>();
private static readonly List<Type> _knownTypes = new List<Type>(); private static readonly List<Type> _knownTypes = new List<Type>();

View File

@@ -1,9 +0,0 @@
using System;
namespace ModernKeePass.Exceptions
{
public class DatabaseOpenedException: Exception
{
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace ModernKeePass.Exceptions
{
public class NavigationException: Exception
{
public NavigationException(Type pageType) : base($"Failed to load Page {pageType.FullName}")
{
}
}
}

View File

@@ -111,11 +111,12 @@
<Compile Include="Actions\ClipboardAction.cs" /> <Compile Include="Actions\ClipboardAction.cs" />
<Compile Include="Actions\NavigateToUrlAction.cs" /> <Compile Include="Actions\NavigateToUrlAction.cs" />
<Compile Include="Actions\SetupFocusAction.cs" /> <Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="Actions\ToastAction.cs" />
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Converters\IntToSymbolConverter.cs" /> <Compile Include="Converters\IntToSymbolConverter.cs" />
<Compile Include="Exceptions\DatabaseOpenedException.cs" /> <Compile Include="Exceptions\NavigationException.cs" />
<Compile Include="Interfaces\IProxyInvocationHandler.cs" /> <Compile Include="Interfaces\IProxyInvocationHandler.cs" />
<Compile Include="Interfaces\IRecentService.cs" /> <Compile Include="Interfaces\IRecentService.cs" />
<Compile Include="Interfaces\IRecentItem.cs" /> <Compile Include="Interfaces\IRecentItem.cs" />

View File

@@ -294,4 +294,7 @@
<data name="MessageDialogSaveErrorTitle" xml:space="preserve"> <data name="MessageDialogSaveErrorTitle" xml:space="preserve">
<value>Save error</value> <value>Save error</value>
</data> </data>
<data name="ToastSavedMessage" xml:space="preserve">
<value>Database successfully saved!</value>
</data>
</root> </root>

View File

@@ -387,4 +387,13 @@
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>History</value> <value>History</value>
</data> </data>
<data name="ToastCopyLogin.Message" xml:space="preserve">
<value>Login successfully copied!</value>
</data>
<data name="ToastCopyPassword.Message" xml:space="preserve">
<value>Password successfully copied!</value>
</data>
<data name="ToastCopyUrl.Message" xml:space="preserve">
<value>URL successfully copied!</value>
</data>
</root> </root>

View File

@@ -187,7 +187,7 @@
<value>Attention</value> <value>Attention</value>
</data> </data>
<data name="EntityDeleting" xml:space="preserve"> <data name="EntityDeleting" xml:space="preserve">
<value>Suprression</value> <value>Suppression</value>
</data> </data>
<data name="EntityRestoredTitle" xml:space="preserve"> <data name="EntityRestoredTitle" xml:space="preserve">
<value>Restauré</value> <value>Restauré</value>
@@ -208,10 +208,10 @@
<value>Entrée replacée dans son groupe d'origine</value> <value>Entrée replacée dans son groupe d'origine</value>
</data> </data>
<data name="GroupDeleted" xml:space="preserve"> <data name="GroupDeleted" xml:space="preserve">
<value>Groupe supprimé défnitivement</value> <value>Groupe supprimé définitivement</value>
</data> </data>
<data name="GroupDeletingConfirmation" xml:space="preserve"> <data name="GroupDeletingConfirmation" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer ce group et toutes ses entrées ?</value> <value>Êtes-vous sûr de vouloir supprimer ce groupe et toutes ses entrées ?</value>
</data> </data>
<data name="GroupRecycled" xml:space="preserve"> <data name="GroupRecycled" xml:space="preserve">
<value>Groupe placé dans la Corbeille</value> <value>Groupe placé dans la Corbeille</value>
@@ -294,4 +294,7 @@
<data name="MessageDialogSaveErrorTitle" xml:space="preserve"> <data name="MessageDialogSaveErrorTitle" xml:space="preserve">
<value>Erreur de sauvegarde</value> <value>Erreur de sauvegarde</value>
</data> </data>
<data name="ToastSavedMessage" xml:space="preserve">
<value>Base de données sauvegardée avec succès !</value>
</data>
</root> </root>

View File

@@ -387,4 +387,13 @@
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>Historique</value> <value>Historique</value>
</data> </data>
<data name="ToastCopyLogin.Message" xml:space="preserve">
<value>Login copié avec succès !</value>
</data>
<data name="ToastCopyPassword.Message" xml:space="preserve">
<value>Mot de passe copié avec succès !</value>
</data>
<data name="ToastCopyUrl.Message" xml:space="preserve">
<value>URL copié avec succès !</value>
</data>
</root> </root>

View File

@@ -153,6 +153,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding UserName}" /> <actions:ClipboardAction Text="{Binding UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Name}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
@@ -160,6 +161,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding Password}" /> <actions:ClipboardAction Text="{Binding Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Name}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
@@ -167,6 +169,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{Binding Url}" /> <actions:NavigateToUrlAction Url="{Binding Url}" />
<actions:ToastAction x:Uid="ToastCopyUrl" Title="{Binding Name}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>

View File

@@ -103,6 +103,9 @@ namespace ModernKeePass.Views.UserControls
async command => async command =>
{ {
database.Save(); database.Save();
ToastNotificationHelper.ShowGenericToast(
database.Name,
resource.GetResourceValue("ToastSavedMessage"));
database.Close(false); database.Close(false);
await OpenDatabase(resource); await OpenDatabase(resource);
}, },

View File

@@ -17,9 +17,8 @@ namespace ModernKeePassApp.Test
public void TestCreate() public void TestCreate()
{ {
Assert.IsTrue(_database.IsClosed); Assert.IsTrue(_database.IsClosed);
_database.DatabaseFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync("NewDatabase.kdbx").GetAwaiter().GetResult(); var databaseFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync("NewDatabase.kdbx").GetAwaiter().GetResult();
Assert.IsTrue(_database.IsFileOpen); OpenOrCreateDatabase(databaseFile, true);
OpenOrCreateDatabase(true);
_database.Close(); _database.Close();
Assert.IsTrue(_database.IsClosed); Assert.IsTrue(_database.IsClosed);
} }
@@ -28,9 +27,8 @@ namespace ModernKeePassApp.Test
public void TestOpen() public void TestOpen()
{ {
Assert.IsTrue(_database.IsClosed); Assert.IsTrue(_database.IsClosed);
_database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx").GetAwaiter().GetResult(); var databaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx").GetAwaiter().GetResult();
Assert.IsTrue(_database.IsFileOpen); OpenOrCreateDatabase(databaseFile, false);
OpenOrCreateDatabase(false);
} }
[TestMethod] [TestMethod]
@@ -44,16 +42,16 @@ namespace ModernKeePassApp.Test
TestOpen(); TestOpen();
} }
private void OpenOrCreateDatabase(bool createNew) private void OpenOrCreateDatabase(StorageFile databaseFile, bool createNew)
{ {
Assert.ThrowsException<ArgumentNullException>( Assert.ThrowsException<ArgumentNullException>(
() => _database.Open(null, createNew)); () => _database.Open(databaseFile, null, createNew));
var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock()) var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock())
{ {
HasPassword = true, HasPassword = true,
Password = "test" Password = "test"
}; };
compositeKey.OpenDatabase(createNew).GetAwaiter().GetResult(); compositeKey.OpenDatabase(databaseFile, createNew).GetAwaiter().GetResult();
Assert.IsTrue(_database.IsOpen); Assert.IsTrue(_database.IsOpen);
} }
} }

View File

@@ -11,13 +11,11 @@ namespace ModernKeePassApp.Test.Mock
public class DatabaseServiceMock : IDatabaseService public class DatabaseServiceMock : IDatabaseService
{ {
private bool _isOpen; private bool _isOpen;
private bool _isClosed;
private CompositeKey _compositeKey; private CompositeKey _compositeKey;
private StorageFile _databaseFile;
public PwCompressionAlgorithm CompressionAlgorithm { get; set; } public PwCompressionAlgorithm CompressionAlgorithm { get; set; }
public StorageFile DatabaseFile { get; set; }
public CompositeKey CompositeKey public CompositeKey CompositeKey
{ {
get { return _compositeKey; } get { return _compositeKey; }
@@ -30,10 +28,6 @@ namespace ModernKeePassApp.Test.Mock
public bool IsOpen => _isOpen; public bool IsOpen => _isOpen;
public bool IsFileOpen => DatabaseFile != null;
public bool IsClosed => _isClosed;
public bool HasChanged { get; set; } public bool HasChanged { get; set; }
public string Name => "MockDatabase"; public string Name => "MockDatabase";
@@ -51,7 +45,6 @@ namespace ModernKeePassApp.Test.Mock
public void Close(bool releaseFile = true) public void Close(bool releaseFile = true)
{ {
_isClosed = true;
_isOpen = false; _isOpen = false;
} }
@@ -60,16 +53,16 @@ namespace ModernKeePassApp.Test.Mock
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Open(CompositeKey key, bool createNew = false) public void Open(StorageFile databaseFile, CompositeKey key, bool createNew = false)
{ {
_databaseFile = databaseFile;
_compositeKey = key; _compositeKey = key;
_isOpen = true; _isOpen = true;
_isClosed = false;
} }
public void ReOpen() public void ReOpen()
{ {
Open(_compositeKey); Open(_databaseFile, _compositeKey);
} }
public void Save() public void Save()

View File

@@ -31,15 +31,15 @@ namespace ModernKeePassApp.Test
var mainVm = new MainVm(null, null, database, _resource, _recent); var mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.AreEqual(1, mainVm.MainMenuItems.Count()); Assert.AreEqual(1, mainVm.MainMenuItems.Count());
var firstGroup = mainVm.MainMenuItems.FirstOrDefault(); var firstGroup = mainVm.MainMenuItems.FirstOrDefault();
Assert.AreEqual(7, firstGroup.Count()); Assert.AreEqual(7, firstGroup?.Count());
database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx") var databaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
.GetAwaiter().GetResult(); .GetAwaiter().GetResult();
mainVm = new MainVm(null, null, database, _resource, _recent); mainVm = new MainVm(null, null, database, _resource, _recent, databaseFile);
Assert.IsNotNull(mainVm.SelectedItem); Assert.IsNotNull(mainVm.SelectedItem);
Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType); Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType);
database.Open(null); database.Open(databaseFile, null);
mainVm = new MainVm(null, null, database, _resource, _recent); mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.IsNotNull(mainVm.SelectedItem); Assert.IsNotNull(mainVm.SelectedItem);
Assert.AreEqual(2, mainVm.MainMenuItems.Count()); Assert.AreEqual(2, mainVm.MainMenuItems.Count());
@@ -51,7 +51,7 @@ namespace ModernKeePassApp.Test
{ {
var database = new DatabaseServiceMock(); var database = new DatabaseServiceMock();
var compositeKeyVm = new CompositeKeyVm(database, _resource); var compositeKeyVm = new CompositeKeyVm(database, _resource);
Assert.IsTrue(compositeKeyVm.OpenDatabase(false).GetAwaiter().GetResult()); Assert.IsTrue(compositeKeyVm.OpenDatabase(null, false).GetAwaiter().GetResult());
compositeKeyVm.StatusType = 1; compositeKeyVm.StatusType = 1;
compositeKeyVm.Password = "test"; compositeKeyVm.Password = "test";
Assert.AreEqual(0, compositeKeyVm.StatusType); Assert.AreEqual(0, compositeKeyVm.StatusType);
@@ -61,13 +61,10 @@ namespace ModernKeePassApp.Test
[TestMethod] [TestMethod]
public void TestOpenVm() public void TestOpenVm()
{ {
var database = new DatabaseServiceMock var databaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
{ .GetAwaiter().GetResult();
DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx") var openVm = new OpenVm(databaseFile);
.GetAwaiter().GetResult() Assert.IsTrue(openVm.IsFileSelected);
};
var openVm = new OpenVm(database);
Assert.IsTrue(openVm.ShowPasswordBox);
Assert.AreEqual("MockDatabase", openVm.Name); Assert.AreEqual("MockDatabase", openVm.Name);
} }
@@ -94,7 +91,7 @@ namespace ModernKeePassApp.Test
{ {
var database = new DatabaseServiceMock(); var database = new DatabaseServiceMock();
var saveVm = new SaveVm(database); var saveVm = new SaveVm(database);
database.Open(null); database.Open(null, null);
saveVm.Save(false); saveVm.Save(false);
Assert.IsTrue(database.IsOpen); Assert.IsTrue(database.IsOpen);
saveVm.Save(); saveVm.Save();
@@ -108,7 +105,7 @@ namespace ModernKeePassApp.Test
Assert.AreEqual(1, settingsVm.MenuItems.Count()); Assert.AreEqual(1, settingsVm.MenuItems.Count());
var firstGroup = settingsVm.MenuItems.FirstOrDefault(); var firstGroup = settingsVm.MenuItems.FirstOrDefault();
// All groups have an empty title, so all settings are put inside the empty group // All groups have an empty title, so all settings are put inside the empty group
Assert.AreEqual(4, firstGroup.Count()); Assert.AreEqual(4, firstGroup?.Count());
Assert.IsNotNull(settingsVm.SelectedItem); Assert.IsNotNull(settingsVm.SelectedItem);
var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem; var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem;
Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType); Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType);