Created a Settings Service

Created a Recent Service
Created a Resources Service
Code refactor
Updated tests
This commit is contained in:
BONNEVILLE Geoffroy
2017-12-01 17:59:38 +01:00
parent f172e31250
commit 744858df81
35 changed files with 552 additions and 141 deletions

View File

@@ -1,16 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using Windows.ApplicationModel; using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Activation;
using Windows.Data.Json;
using Windows.Storage; using Windows.Storage;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Exceptions; using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
using ModernKeePass.Services; using ModernKeePass.Services;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227 // The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
@@ -22,7 +19,7 @@ namespace ModernKeePass
/// </summary> /// </summary>
sealed partial class App sealed partial class App
{ {
public DatabaseService Database { get; set; } = new DatabaseService(); public DatabaseService Database { get; private set; }
/// <summary> /// <summary>
/// Initializes the singleton application object. This is the first line of authored code /// Initializes the singleton application object. This is the first line of authored code
@@ -49,7 +46,7 @@ namespace ModernKeePass
if (!(realException is SaveException)) return; if (!(realException is SaveException)) return;
unhandledExceptionEventArgs.Handled = true; unhandledExceptionEventArgs.Handled = true;
MessageDialogService.SaveErrorDialog(realException as SaveException, Database); MessageDialogHelper.SaveErrorDialog(realException as SaveException, Database);
} }
/// <summary> /// <summary>
@@ -124,6 +121,7 @@ namespace ModernKeePass
}*/ }*/
// Ensure the current window is active // Ensure the current window is active
Window.Current.Activate(); Window.Current.Activate();
Database = new DatabaseService();
} }
/// <summary> /// <summary>

View File

@@ -5,9 +5,9 @@ using Windows.UI.Popups;
using ModernKeePass.Exceptions; using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
namespace ModernKeePass.Services namespace ModernKeePass.Common
{ {
public static class MessageDialogService public static class MessageDialogHelper
{ {
public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action) public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action)
{ {

View File

@@ -5,9 +5,9 @@ using Windows.UI.Notifications;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
namespace ModernKeePass.Services namespace ModernKeePass.Common
{ {
public static class ToastNotificationService public static class ToastNotificationHelper
{ {
public static void ShowMovedToast(IPwEntity entity, string action, string text) public static void ShowMovedToast(IPwEntity entity, string action, string text)
{ {

View File

@@ -0,0 +1,14 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.Storage;
namespace ModernKeePass.Interfaces
{
public interface IRecent
{
int EntryCount { get; }
Task<IStorageItem> GetFileAsync(string token);
ObservableCollection<IRecentItem> GetAllFiles(bool removeIfNonExistant = true);
void Add(IStorageItem file, string metadata);
}
}

View File

@@ -0,0 +1,11 @@
using Windows.Storage;
namespace ModernKeePass.Interfaces
{
public interface IRecentItem
{
StorageFile DatabaseFile { get; }
string Token { get; }
string Name { get; }
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Interfaces
{
public interface IResource
{
string GetResourceValue(string key);
}
}

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Interfaces
{
public interface ISettings
{
T GetSetting<T>(string property);
void PutSetting<T>(string property, T value);
}
}

View File

@@ -113,18 +113,24 @@
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Interfaces\IRecent.cs" />
<Compile Include="Interfaces\IRecentItem.cs" />
<Compile Include="Interfaces\IResource.cs" />
<Compile Include="Pages\MainPageFrames\DonatePage.xaml.cs"> <Compile Include="Pages\MainPageFrames\DonatePage.xaml.cs">
<DependentUpon>DonatePage.xaml</DependentUpon> <DependentUpon>DonatePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Services\DatabaseService.cs" /> <Compile Include="Services\DatabaseService.cs" />
<Compile Include="Services\MessageDialogService.cs" /> <Compile Include="Interfaces\ISettings.cs" />
<Compile Include="Common\MessageDialogHelper.cs" />
<Compile Include="Common\NavigationHelper.cs" /> <Compile Include="Common\NavigationHelper.cs" />
<Compile Include="Common\NotifyPropertyChangedBase.cs" /> <Compile Include="Common\NotifyPropertyChangedBase.cs" />
<Compile Include="Common\ObservableDictionary.cs" /> <Compile Include="Common\ObservableDictionary.cs" />
<Compile Include="Common\RelayCommand.cs" /> <Compile Include="Common\RelayCommand.cs" />
<Compile Include="Common\SuspensionManager.cs" /> <Compile Include="Common\SuspensionManager.cs" />
<Compile Include="Services\RecentService.cs" />
<Compile Include="Services\ResourcesService.cs" />
<Compile Include="Services\SettingsService.cs" /> <Compile Include="Services\SettingsService.cs" />
<Compile Include="Services\ToastNotificationService.cs" /> <Compile Include="Common\ToastNotificationHelper.cs" />
<Compile Include="Converters\DiscreteIntToSolidColorBrushConverter.cs" /> <Compile Include="Converters\DiscreteIntToSolidColorBrushConverter.cs" />
<Compile Include="Converters\EmptyStringToVisibilityConverter.cs" /> <Compile Include="Converters\EmptyStringToVisibilityConverter.cs" />
<Compile Include="Converters\NullToBooleanConverter.cs" /> <Compile Include="Converters\NullToBooleanConverter.cs" />
@@ -218,6 +224,7 @@
<None Include="packages.config"> <None Include="packages.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>
<PRIResource Include="Strings\en-US\CodeBehind.resw" />
<PRIResource Include="Strings\en-US\Resources.resw" /> <PRIResource Include="Strings\en-US\Resources.resw" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -394,11 +394,11 @@
</AppBarButton> </AppBarButton>
</CommandBar.SecondaryCommands> </CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}"> <AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<!--<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" /> <core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>--> </interactivity:Interaction.Behaviors>
</AppBarToggleButton> </AppBarToggleButton>
<AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click"> <AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>

View File

@@ -77,9 +77,9 @@ namespace ModernKeePass.Pages
? "Are you sure you want to send this entry to the recycle bin?" ? "Are you sure you want to send this entry to the recycle bin?"
: "Are you sure you want to delete this entry?"; : "Are you sure you want to delete this entry?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed"; var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogService.ShowActionDialog("Warning", message, "Delete", "Cancel", a => MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a =>
{ {
ToastNotificationService.ShowMovedToast(Model, "Deleting", text); ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
Model.MarkForDelete(); Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
}); });
@@ -87,7 +87,7 @@ namespace ModernKeePass.Pages
private void RestoreButton_Click(object sender, RoutedEventArgs e) private void RestoreButton_Click(object sender, RoutedEventArgs e)
{ {
ToastNotificationService.ShowMovedToast(Model, "Restored", "Item returned to its original group"); ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group");
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
} }
@@ -100,7 +100,7 @@ namespace ModernKeePass.Pages
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageDialogService.ShowErrorDialog(ex); MessageDialogHelper.ShowErrorDialog(ex);
} }
} }
} }

View File

@@ -64,11 +64,11 @@
</Button.Flyout> </Button.Flyout>
</AppBarButton> </AppBarButton>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}"> <AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<!--<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" /> <core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>--> </interactivity:Interaction.Behaviors>
</AppBarToggleButton> </AppBarToggleButton>
<AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click"> <AppBarButton Icon="Undo" Label="Restore" Visibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>

View File

@@ -115,9 +115,9 @@ namespace ModernKeePass.Pages
? "Are you sure you want to send the whole group and all its entries to the recycle bin?" ? "Are you sure you want to send the whole group and all its entries to the recycle bin?"
: "Are you sure you want to delete the whole group and all its entries?"; : "Are you sure you want to delete the whole group and all its entries?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed"; var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogService.ShowActionDialog("Warning", message, "Delete", "Cancel", a => MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a =>
{ {
ToastNotificationService.ShowMovedToast(Model, "Deleting", text); ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
Model.MarkForDelete(); Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
}); });
@@ -125,7 +125,7 @@ namespace ModernKeePass.Pages
private void RestoreButton_Click(object sender, RoutedEventArgs e) private void RestoreButton_Click(object sender, RoutedEventArgs e)
{ {
ToastNotificationService.ShowMovedToast(Model, "Restored", "Item returned to its original group"); ToastNotificationHelper.ShowMovedToast(Model, "Restored", "Item returned to its original group");
if (Frame.CanGoBack) Frame.GoBack(); if (Frame.CanGoBack) Frame.GoBack();
} }

View File

@@ -18,7 +18,7 @@ namespace ModernKeePass.Pages
private void CompositeKeyUserControl_OnValidationChecked(object sender, PasswordEventArgs e) private void CompositeKeyUserControl_OnValidationChecked(object sender, PasswordEventArgs e)
{ {
ToastNotificationService.ShowGenericToast("Composite key", "Database successfully updated."); ToastNotificationHelper.ShowGenericToast("Composite key", "Database successfully updated.");
} }
} }
} }

View File

@@ -26,6 +26,8 @@ namespace ModernKeePass.Services
Opened = 2 Opened = 2
} }
private readonly PwDatabase _pwDatabase = new PwDatabase(); private readonly PwDatabase _pwDatabase = new PwDatabase();
private readonly ISettings _settings;
private readonly IResource _resource;
private StorageFile _databaseFile; private StorageFile _databaseFile;
private GroupVm _recycleBin; private GroupVm _recycleBin;
@@ -78,6 +80,14 @@ namespace ModernKeePass.Services
set { _pwDatabase.KdfParameters = value; } set { _pwDatabase.KdfParameters = value; }
} }
public DatabaseService() : this(new SettingsService())
{ }
public DatabaseService(ISettings settings)
{
_settings = settings;
}
/// <summary> /// <summary>
/// Open a KeePass database /// Open a KeePass database
/// </summary> /// </summary>
@@ -99,8 +109,8 @@ namespace ModernKeePass.Services
_pwDatabase.New(ioConnection, key); _pwDatabase.New(ioConnection, key);
//Get settings default values //Get settings default values
if (SettingsService.GetSetting<bool>("Sample")) CreateSampleData(); if (_settings.GetSetting<bool>("Sample")) CreateSampleData();
var fileFormat = SettingsService.GetSetting<string>("DefaultFileFormat"); var fileFormat = _settings.GetSetting<string>("DefaultFileFormat");
switch (fileFormat) switch (fileFormat)
{ {
case "4": case "4":

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using ModernKeePass.Interfaces;
using Windows.Storage;
using Windows.Storage.AccessCache;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Services
{
public class RecentService : IRecent
{
private readonly StorageItemMostRecentlyUsedList _mru = StorageApplicationPermissions.MostRecentlyUsedList;
public int EntryCount => _mru.Entries.Count;
public ObservableCollection<IRecentItem> GetAllFiles(bool removeIfNonExistant = true)
{
var result = new ObservableCollection<IRecentItem>();
foreach (var entry in _mru.Entries)
{
try
{
var file = _mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult();
result.Add(new RecentItemVm(entry.Token, entry.Metadata, file));
}
catch (Exception)
{
if (removeIfNonExistant) _mru.Remove(entry.Token);
}
}
return result;
}
public void Add(IStorageItem file, string metadata)
{
_mru.Add(file, metadata);
}
public async Task<IStorageItem> GetFileAsync(string token)
{
return await _mru.GetFileAsync(token);
}
}
}

View File

@@ -0,0 +1,17 @@
using Windows.ApplicationModel.Resources;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Services
{
public class ResourcesService: IResource
{
private const string ResourceFileName = "CodeBehind";
private readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView();
public string GetResourceValue(string key)
{
var resource = _resourceLoader.GetString($"/{ResourceFileName}/{key}");
return resource;
}
}
}

View File

@@ -1,15 +1,19 @@
using System; using System;
using Windows.Foundation.Collections;
using Windows.Storage; using Windows.Storage;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Services namespace ModernKeePass.Services
{ {
public class SettingsService public class SettingsService : ISettings
{ {
public static T GetSetting<T>(string property) private readonly IPropertySet _values = ApplicationData.Current.LocalSettings.Values;
public T GetSetting<T>(string property)
{ {
try try
{ {
return (T)Convert.ChangeType(ApplicationData.Current.LocalSettings.Values[property], typeof(T)); return (T)Convert.ChangeType(_values[property], typeof(T));
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
@@ -17,12 +21,11 @@ namespace ModernKeePass.Services
} }
} }
public static void PutSetting<T>(string property, T value) public void PutSetting<T>(string property, T value)
{ {
var localSettings = ApplicationData.Current.LocalSettings; if (_values.ContainsKey(property))
if (localSettings.Values.ContainsKey(property)) _values[property] = value;
localSettings.Values[property] = value; else _values.Add(property, value);
else localSettings.Values.Add(property, value);
} }
} }
} }

View File

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CompositeKeyDefaultKeyFile" xml:space="preserve">
<value>Select key file from disk...</value>
</data>
<data name="CompositeKeyErrorOpen" xml:space="preserve">
<value>Error</value>
</data>
<data name="CompositeKeyErrorUserKeyFile" xml:space="preserve">
<value>key file</value>
</data>
<data name="CompositeKeyErrorUserOr" xml:space="preserve">
<value> or </value>
</data>
<data name="CompositeKeyErrorUserPassword" xml:space="preserve">
<value>password</value>
</data>
<data name="CompositeKeyErrorUserStart" xml:space="preserve">
<value>Error: wrong </value>
</data>
<data name="CompositeKeyUpdated" xml:space="preserve">
<value>Database composite key updated.</value>
</data>
<data name="EntryNew" xml:space="preserve">
<value>&lt; New entry &gt;</value>
</data>
<data name="GroupNew" xml:space="preserve">
<value>&lt; New group &gt;</value>
</data>
<data name="MainMenuItemAbout" xml:space="preserve">
<value>About</value>
</data>
<data name="MainMenuItemDonate" xml:space="preserve">
<value>Donate</value>
</data>
<data name="MainMenuItemNew" xml:space="preserve">
<value>New</value>
</data>
<data name="MainMenuItemOpen" xml:space="preserve">
<value>Open</value>
</data>
<data name="MainMenuItemRecent" xml:space="preserve">
<value>Recent</value>
</data>
<data name="MainMenuItemSave" xml:space="preserve">
<value>Save</value>
</data>
<data name="MainMenuItemSettings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="SettingsMenuGroupApplication" xml:space="preserve">
<value>Application</value>
</data>
<data name="SettingsMenuGroupDatabase" xml:space="preserve">
<value>Database</value>
</data>
<data name="SettingsMenuItemGeneral" xml:space="preserve">
<value>General</value>
</data>
<data name="SettingsMenuItemNew" xml:space="preserve">
<value>New</value>
</data>
<data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Security</value>
</data>
</root>

View File

@@ -29,7 +29,8 @@ namespace ModernKeePass.ViewModels
private string _status; private string _status;
private StatusTypes _statusType; private StatusTypes _statusType;
private StorageFile _keyFile; private StorageFile _keyFile;
private string _keyFileText = "Select key file from disk..."; private string _keyFileText;
private readonly IResource _resource;
public IDatabase Database { get; set; } public IDatabase Database { get; set; }
@@ -100,10 +101,12 @@ namespace ModernKeePass.ViewModels
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray()); public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray());
public CompositeKeyVm() : this((Application.Current as App)?.Database) { } public CompositeKeyVm() : this((Application.Current as App)?.Database, new ResourcesService()) { }
public CompositeKeyVm(IDatabase database) public CompositeKeyVm(IDatabase database, IResource resource)
{ {
_resource = resource;
_keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile");
Database = database; Database = database;
} }
@@ -116,7 +119,7 @@ namespace ModernKeePass.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
error = $"Error: {e.Message}"; error = $"{_resource.GetResourceValue("CompositeKeyErrorOpen")}: {e.Message}";
} }
switch ((DatabaseService.DatabaseStatus)Database.Status) switch ((DatabaseService.DatabaseStatus)Database.Status)
{ {
@@ -124,10 +127,10 @@ namespace ModernKeePass.ViewModels
await Task.Run(() => RootGroup = Database.RootGroup); await Task.Run(() => RootGroup = Database.RootGroup);
return true; return true;
case DatabaseService.DatabaseStatus.CompositeKeyError: case DatabaseService.DatabaseStatus.CompositeKeyError:
var errorMessage = new StringBuilder("Error: wrong "); var errorMessage = new StringBuilder(_resource.GetResourceValue("CompositeKeyErrorUserStart"));
if (HasPassword) errorMessage.Append("password"); if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword"));
if (HasPassword && HasKeyFile) errorMessage.Append(" or "); if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr"));
if (HasKeyFile) errorMessage.Append("key file"); if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile"));
UpdateStatus(errorMessage.ToString(), StatusTypes.Error); UpdateStatus(errorMessage.ToString(), StatusTypes.Error);
break; break;
case DatabaseService.DatabaseStatus.Error: case DatabaseService.DatabaseStatus.Error:
@@ -140,7 +143,7 @@ namespace ModernKeePass.ViewModels
public void UpdateKey() public void UpdateKey()
{ {
Database.UpdateCompositeKey(CreateCompositeKey()); Database.UpdateCompositeKey(CreateCompositeKey());
UpdateStatus("Database composite key updated.", StatusTypes.Success); UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success);
} }
public void CreateKeyFile(StorageFile file) public void CreateKeyFile(StorageFile file)

View File

@@ -5,6 +5,7 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Mappings; using ModernKeePass.Mappings;
using ModernKeePass.Services;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Cryptography.PasswordGenerator; using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
@@ -48,8 +49,9 @@ namespace ModernKeePass.ViewModels
{ {
get get
{ {
var title = GetEntryValue(PwDefs.TitleField); /*var title = GetEntryValue(PwDefs.TitleField);
return title == null ? "< New entry >" : title; return title == null ? _resource.GetResourceValue("EntryNew") : title;*/
return GetEntryValue(PwDefs.TitleField);
} }
set { SetEntryValue(PwDefs.TitleField, value); } set { SetEntryValue(PwDefs.TitleField, value); }
} }

View File

@@ -19,10 +19,10 @@ namespace ModernKeePass.ViewModels
public GroupVm ParentGroup { get; private set; } public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; } public GroupVm PreviousGroup { get; private set; }
public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>(); public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>();
public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>(); public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>();
public int EntryCount => Entries.Count() - 1; public int EntryCount => Entries.Count;
// TODO: put in a converter
public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal; public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal;
public int GroupCount => Groups.Count - 1; public int GroupCount => Groups.Count - 1;
public PwUuid IdUuid => _pwGroup?.Uuid; public PwUuid IdUuid => _pwGroup?.Uuid;
@@ -50,7 +50,7 @@ namespace ModernKeePass.ViewModels
public string Name public string Name
{ {
get { return _pwGroup == null ? "< New group >" : _pwGroup.Name; } get { return _pwGroup == null ? string.Empty : _pwGroup.Name; }
set { _pwGroup.Name = value; } set { _pwGroup.Name = value; }
} }
@@ -86,7 +86,6 @@ namespace ModernKeePass.ViewModels
private readonly IDatabase _database; private readonly IDatabase _database;
private bool _isEditMode; private bool _isEditMode;
private PwEntry _reorderedEntry; private PwEntry _reorderedEntry;
//private int _reorderedEntryIndex;
public GroupVm() {} public GroupVm() {}
@@ -148,7 +147,6 @@ namespace ModernKeePass.ViewModels
Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null); Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null);
} }
public void UndoDelete() public void UndoDelete()
{ {
Move(PreviousGroup); Move(PreviousGroup);
@@ -192,7 +190,7 @@ namespace ModernKeePass.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
MessageDialogService.ShowErrorDialog(e); MessageDialogHelper.ShowErrorDialog(e);
} }
} }
@@ -206,7 +204,7 @@ namespace ModernKeePass.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
MessageDialogService.ShowErrorDialog(e); MessageDialogHelper.ShowErrorDialog(e);
} }
} }

View File

@@ -1,24 +1,15 @@
using System; using Windows.Storage;
using Windows.Storage;
using ModernKeePass.Common; using ModernKeePass.Common;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel public class RecentItemVm: NotifyPropertyChangedBase, ISelectableModel, IRecentItem
{ {
private bool _isSelected; private bool _isSelected;
public RecentItemVm() {}
public RecentItemVm(AccessListEntry entry, StorageFile file)
{
Token = entry.Token;
Name = entry.Metadata;
DatabaseFile = file;
}
public StorageFile DatabaseFile { get; } public StorageFile DatabaseFile { get; }
public string Token { get; } public string Token { get; }
public string Name { get; } public string Name { get; }
@@ -30,6 +21,14 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isSelected, value); } set { SetProperty(ref _isSelected, value); }
} }
public RecentItemVm() {}
public RecentItemVm(string token, string metadata, IStorageItem file)
{
Token = token;
Name = metadata;
DatabaseFile = file as StorageFile;
}
public void OpenDatabaseFile() public void OpenDatabaseFile()
{ {
OpenDatabaseFile((Application.Current as App)?.Database); OpenDatabaseFile((Application.Current as App)?.Database);
@@ -40,10 +39,14 @@ namespace ModernKeePass.ViewModels
database.DatabaseFile = DatabaseFile; database.DatabaseFile = DatabaseFile;
} }
public async void UpdateAccessTime() public void UpdateAccessTime()
{ {
var mru = StorageApplicationPermissions.MostRecentlyUsedList; UpdateAccessTime(new RecentService());
await mru.GetFileAsync(Token); }
public async void UpdateAccessTime(IRecent recent)
{
await recent.GetFileAsync(Token);
} }
} }
} }

View File

@@ -11,6 +11,7 @@ using ModernKeePassLib.Cryptography.KeyDerivation;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
// TODO: implement Kdf settings
public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject
{ {
private readonly IDatabase _database; private readonly IDatabase _database;

View File

@@ -1,23 +1,33 @@
using System.Collections.Generic; using System.Collections.Generic;
using ModernKeePass.Interfaces;
using ModernKeePass.Services; using ModernKeePass.Services;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class SettingsNewVm public class SettingsNewVm
{ {
private ISettings _settings;
public SettingsNewVm() : this(new SettingsService())
{ }
public SettingsNewVm(ISettings settings)
{
_settings = settings;
}
public bool IsCreateSample public bool IsCreateSample
{ {
get { return SettingsService.GetSetting<bool>("Sample"); } get { return _settings.GetSetting<bool>("Sample"); }
set { SettingsService.PutSetting("Sample", value); } set { _settings.PutSetting("Sample", value); }
} }
public IEnumerable<string> FileFormats => new []{"2", "4"}; public IEnumerable<string> FileFormats => new []{"2", "4"};
public string FileFormatVersion public string FileFormatVersion
{ {
get { return SettingsService.GetSetting<string>("DefaultFileFormat"); } get { return _settings.GetSetting<string>("DefaultFileFormat"); }
set { SettingsService.PutSetting("DefaultFileFormat", value); } set { _settings.PutSetting("DefaultFileFormat", value); }
} }
} }
} }

View File

@@ -1,7 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Windows.ApplicationModel; using Windows.ApplicationModel;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using ModernKeePass.Common; using ModernKeePass.Common;
@@ -46,40 +45,68 @@ namespace ModernKeePass.ViewModels
public MainVm() {} public MainVm() {}
internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame, (Application.Current as App)?.Database) { } internal MainVm(Frame referenceFrame, Frame destinationFrame) : this(referenceFrame, destinationFrame,
(Application.Current as App)?.Database, new ResourcesService(), new RecentService())
{ }
public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database) public MainVm(Frame referenceFrame, Frame destinationFrame, IDatabase database, IResource resource, IRecent recent)
{ {
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
var isDatabaseOpen = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened; var isDatabaseOpen = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opened;
var mainMenuItems = new ObservableCollection<MainMenuItemVm> var mainMenuItems = new ObservableCollection<MainMenuItemVm>
{ {
new MainMenuItemVm new MainMenuItemVm
{ {
Title = "Open", PageType = typeof(OpenDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Page2, Title = resource.GetResourceValue("MainMenuItemOpen"),
PageType = typeof(OpenDatabasePage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Page2,
IsSelected = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opening IsSelected = database != null && database.Status == (int) DatabaseService.DatabaseStatus.Opening
}, },
new MainMenuItemVm new MainMenuItemVm
{ {
Title = "New" , PageType = typeof(NewDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Add Title = resource.GetResourceValue("MainMenuItemNew"),
PageType = typeof(NewDatabasePage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Add
}, },
new MainMenuItemVm new MainMenuItemVm
{ {
Title = "Save" , PageType = typeof(SaveDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Save, Title = resource.GetResourceValue("MainMenuItemSave"),
IsSelected = isDatabaseOpen, IsEnabled = isDatabaseOpen PageType = typeof(SaveDatabasePage),
}, Destination = destinationFrame,
new MainMenuItemVm { Parameter = referenceFrame,
Title = "Recent" , PageType = typeof(RecentDatabasesPage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Copy, SymbolIcon = Symbol.Save,
IsSelected = (database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) && mru.Entries.Count > 0, IsEnabled = mru.Entries.Count > 0 IsSelected = isDatabaseOpen,
IsEnabled = isDatabaseOpen
}, },
new MainMenuItemVm new MainMenuItemVm
{ {
Title = "Settings" , PageType = typeof(SettingsPage), Destination = referenceFrame, SymbolIcon = Symbol.Setting Title = resource.GetResourceValue("MainMenuItemRecent"),
PageType = typeof(RecentDatabasesPage),
Destination = destinationFrame,
Parameter = referenceFrame,
SymbolIcon = Symbol.Copy,
IsSelected =
(database == null || database.Status == (int) DatabaseService.DatabaseStatus.Closed) &&
recent.EntryCount > 0,
IsEnabled = recent.EntryCount > 0
}, },
new MainMenuItemVm new MainMenuItemVm
{ {
Title = "About" , PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help Title = resource.GetResourceValue("MainMenuItemSettings"),
PageType = typeof(SettingsPage),
Destination = referenceFrame,
SymbolIcon = Symbol.Setting
},
new MainMenuItemVm
{
Title = resource.GetResourceValue("MainMenuItemAbout"),
PageType = typeof(AboutPage),
Destination = destinationFrame,
SymbolIcon = Symbol.Help
} }
}; };
// Auto-select the Recent Items menu item if the conditions are met // Auto-select the Recent Items menu item if the conditions are met

View File

@@ -1,5 +1,4 @@
using Windows.Storage; using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
@@ -25,17 +24,21 @@ namespace ModernKeePass.ViewModels
} }
public void OpenFile(StorageFile file) public void OpenFile(StorageFile file)
{
OpenFile(file, new RecentService());
}
public void OpenFile(StorageFile file, IRecent recent)
{ {
_database.DatabaseFile = file; _database.DatabaseFile = file;
OnPropertyChanged("Name"); OnPropertyChanged("Name");
OnPropertyChanged("ShowPasswordBox"); OnPropertyChanged("ShowPasswordBox");
AddToRecentList(file); AddToRecentList(file, recent);
} }
private void AddToRecentList(StorageFile file) private void AddToRecentList(StorageFile file, IRecent recent)
{ {
var mru = StorageApplicationPermissions.MostRecentlyUsedList; recent.Add(file, file.DisplayName);
mru.Add(file, file.DisplayName);
} }
} }
} }

View File

@@ -1,17 +1,16 @@
using System; using System.Collections.ObjectModel;
using System.Collections.ObjectModel;
using Windows.Storage.AccessCache;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject public class RecentVm : NotifyPropertyChangedBase, IHasSelectableObject
{ {
private ISelectableModel _selectedItem; private ISelectableModel _selectedItem;
private ObservableCollection<RecentItemVm> _recentItems = new ObservableCollection<RecentItemVm>(); private ObservableCollection<IRecentItem> _recentItems = new ObservableCollection<IRecentItem>();
public ObservableCollection<RecentItemVm> RecentItems public ObservableCollection<IRecentItem> RecentItems
{ {
get { return _recentItems; } get { return _recentItems; }
set { SetProperty(ref _recentItems, value); } set { SetProperty(ref _recentItems, value); }
@@ -35,23 +34,14 @@ namespace ModernKeePass.ViewModels
} }
} }
public RecentVm() public RecentVm() : this (new RecentService())
{ }
public RecentVm(IRecent recent)
{ {
var mru = StorageApplicationPermissions.MostRecentlyUsedList; RecentItems = recent.GetAllFiles();
foreach (var entry in mru.Entries)
{
try
{
var file = mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult();
RecentItems.Add(new RecentItemVm(entry, file));
}
catch (Exception)
{
mru.Remove(entry.Token);
}
}
if (RecentItems.Count > 0) if (RecentItems.Count > 0)
SelectedItem = RecentItems[0]; SelectedItem = RecentItems[0] as RecentItemVm;
} }
} }

View File

@@ -6,6 +6,7 @@ using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Pages; using ModernKeePass.Pages;
using ModernKeePass.Pages.SettingsPageFrames; using ModernKeePass.Pages.SettingsPageFrames;
using ModernKeePass.Services;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
@@ -41,32 +42,32 @@ namespace ModernKeePass.ViewModels
} }
} }
public SettingsVm() : this((Application.Current as App)?.Database) { } public SettingsVm() : this((Application.Current as App)?.Database, new ResourcesService()) { }
public SettingsVm(IDatabase database) public SettingsVm(IDatabase database, IResource resource)
{ {
var menuItems = new ObservableCollection<ListMenuItemVm> var menuItems = new ObservableCollection<ListMenuItemVm>
{ {
new ListMenuItemVm new ListMenuItemVm
{ {
Title = "New", Title = resource.GetResourceValue("SettingsMenuItemNew"),
Group = "Application", Group = resource.GetResourceValue("SettingsMenuGroupApplication"),
SymbolIcon = Symbol.Add, SymbolIcon = Symbol.Add,
PageType = typeof(SettingsNewDatabasePage), PageType = typeof(SettingsNewDatabasePage),
IsSelected = true IsSelected = true
}, },
new ListMenuItemVm new ListMenuItemVm
{ {
Title = "General", Title = resource.GetResourceValue("SettingsMenuItemGeneral"),
Group = "Database", Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Setting, SymbolIcon = Symbol.Setting,
PageType = typeof(SettingsDatabasePage), PageType = typeof(SettingsDatabasePage),
IsEnabled = database?.Status == 2 IsEnabled = database?.Status == 2
}, },
new ListMenuItemVm new ListMenuItemVm
{ {
Title = "Security", Title = resource.GetResourceValue("SettingsMenuItemSecurity"),
Group = "Database", Group = resource.GetResourceValue("SettingsMenuGroupDatabase"),
SymbolIcon = Symbol.Permissions, SymbolIcon = Symbol.Permissions,
PageType = typeof(SettingsSecurityPage), PageType = typeof(SettingsSecurityPage),
IsEnabled = database?.Status == 2 IsEnabled = database?.Status == 2

View File

@@ -4,13 +4,14 @@ using Windows.Storage;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using ModernKeePass.Services; using ModernKeePass.Services;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using ModernKeePassApp.Test.Mock;
namespace ModernKeePassApp.Test namespace ModernKeePassApp.Test
{ {
[TestClass] [TestClass]
public class DatabaseTests public class DatabaseTests
{ {
private readonly DatabaseService _database = new DatabaseService(); private readonly DatabaseService _database = new DatabaseService(new SettingsServiceMock());
[TestMethod] [TestMethod]
public void TestCreate() public void TestCreate()
@@ -47,12 +48,12 @@ namespace ModernKeePassApp.Test
{ {
_database.Open(null, createNew); _database.Open(null, createNew);
Assert.AreEqual((int)DatabaseService.DatabaseStatus.NoCompositeKey, _database.Status); Assert.AreEqual((int)DatabaseService.DatabaseStatus.NoCompositeKey, _database.Status);
var compositeKey = new CompositeKeyVm(_database) var compositeKey = new CompositeKeyVm(_database, new ResourceServiceMock())
{ {
HasPassword = true, HasPassword = true,
Password = "test" Password = "test"
}; };
compositeKey.OpenDatabase(createNew); compositeKey.OpenDatabase(createNew).GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opened, _database.Status); Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opened, _database.Status);
} }
} }

View File

@@ -8,7 +8,7 @@ using Windows.Storage;
namespace ModernKeePassApp.Test.Mock namespace ModernKeePassApp.Test.Mock
{ {
public class DatabaseHelperMock : IDatabase public class DatabaseServiceMock : IDatabase
{ {
public PwCompressionAlgorithm CompressionAlgorithm { get; set; } public PwCompressionAlgorithm CompressionAlgorithm { get; set; }

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using ModernKeePass.Interfaces;
using Windows.Storage;
namespace ModernKeePassApp.Test.Mock
{
class RecentServiceMock : IRecent
{
public int EntryCount => 0;
public void Add(IStorageItem file, string metadata)
{
throw new NotImplementedException();
}
public ObservableCollection<IRecentItem> GetAllFiles(bool removeIfNonExistant = true)
{
throw new NotImplementedException();
}
public Task<IStorageItem> GetFileAsync(string token)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock
{
class ResourceServiceMock : IResource
{
public string GetResourceValue(string key)
{
return string.Empty;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock
{
public class SettingsServiceMock : ISettings
{
public T GetSetting<T>(string property)
{
return default(T);
}
public void PutSetting<T>(string property, T value)
{
throw new NotImplementedException();
}
}
}

View File

@@ -120,7 +120,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DatabaseTests.cs" /> <Compile Include="DatabaseTests.cs" />
<Compile Include="Mock\DatabaseHelperMock.cs" /> <Compile Include="Mock\DatabaseServiceMock.cs" />
<Compile Include="Mock\RecentServiceMock.cs" />
<Compile Include="Mock\ResourceServiceMock.cs" />
<Compile Include="Mock\SettingsServiceMock.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModelsTests.cs" /> <Compile Include="ViewModelsTests.cs" />
</ItemGroup> </ItemGroup>

View File

@@ -3,6 +3,8 @@ using System.Linq;
using Windows.ApplicationModel; using Windows.ApplicationModel;
using Windows.Storage.AccessCache; using Windows.Storage.AccessCache;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using ModernKeePass.Pages;
using ModernKeePass.Pages.SettingsPageFrames;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using ModernKeePassApp.Test.Mock; using ModernKeePassApp.Test.Mock;
@@ -11,6 +13,9 @@ namespace ModernKeePassApp.Test
[TestClass] [TestClass]
public class ViewModelsTests public class ViewModelsTests
{ {
private RecentServiceMock _recent = new RecentServiceMock();
private ResourceServiceMock _resource = new ResourceServiceMock();
[TestMethod] [TestMethod]
public void TestAboutVm() public void TestAboutVm()
{ {
@@ -21,29 +26,30 @@ namespace ModernKeePassApp.Test
[TestMethod] [TestMethod]
public void TestMainVm() public void TestMainVm()
{ {
var database = new DatabaseHelperMock(); var database = new DatabaseServiceMock();
var mainVm = new MainVm(null, null, database);
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(6, firstGroup.Count()); Assert.AreEqual(6, firstGroup.Count());
database.Status = 1; database.Status = 1;
mainVm = new MainVm(null, null, database); mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.IsNotNull(mainVm.SelectedItem); Assert.IsNotNull(mainVm.SelectedItem);
Assert.AreEqual("Open", ((MainMenuItemVm) mainVm.SelectedItem).Title); Assert.AreEqual(typeof(OpenDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType);
database.Status = 2; database.Status = 2;
mainVm = new MainVm(null, null, database); 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());
Assert.AreEqual("Save", ((MainMenuItemVm) mainVm.SelectedItem).Title); Assert.AreEqual(typeof(SaveDatabasePage), ((MainMenuItemVm) mainVm.SelectedItem).PageType);
} }
[TestMethod] [TestMethod]
public void TestCompositeKeyVm() public void TestCompositeKeyVm()
{ {
var database = new DatabaseHelperMock(); var database = new DatabaseServiceMock();
var compositeKeyVm = new CompositeKeyVm(database); var compositeKeyVm = new CompositeKeyVm(database, _resource);
Assert.IsTrue(compositeKeyVm.OpenDatabase(false).GetAwaiter().GetResult()); Assert.IsTrue(compositeKeyVm.OpenDatabase(false).GetAwaiter().GetResult());
compositeKeyVm.StatusType = 1; compositeKeyVm.StatusType = 1;
compositeKeyVm.Password = "test"; compositeKeyVm.Password = "test";
@@ -54,7 +60,7 @@ namespace ModernKeePassApp.Test
[TestMethod] [TestMethod]
public void TestOpenVm() public void TestOpenVm()
{ {
var database = new DatabaseHelperMock var database = new DatabaseServiceMock
{ {
Status = 1, Status = 1,
DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx") DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx")
@@ -78,7 +84,7 @@ namespace ModernKeePassApp.Test
.GetAwaiter().GetResult(), "MockDatabase"); .GetAwaiter().GetResult(), "MockDatabase");
var recentVm = new RecentVm(); var recentVm = new RecentVm();
Assert.IsTrue(recentVm.RecentItems.Count == 1); Assert.IsTrue(recentVm.RecentItems.Count == 1);
recentVm.SelectedItem = recentVm.RecentItems.FirstOrDefault(); recentVm.SelectedItem = recentVm.RecentItems.FirstOrDefault() as RecentItemVm;
Assert.IsTrue(recentVm.SelectedItem.IsSelected); Assert.IsTrue(recentVm.SelectedItem.IsSelected);
mru.Clear(); mru.Clear();
} }
@@ -91,13 +97,14 @@ namespace ModernKeePassApp.Test
[TestMethod] [TestMethod]
public void TestSettingsVm() public void TestSettingsVm()
{ {
var settingsVm = new SettingsVm(); var settingsVm = new SettingsVm(new DatabaseServiceMock(), _resource);
Assert.AreEqual(1, settingsVm.MenuItems.Count()); Assert.AreEqual(1, settingsVm.MenuItems.Count());
var firstGroup = settingsVm.MenuItems.FirstOrDefault(); var firstGroup = settingsVm.MenuItems.FirstOrDefault();
Assert.AreEqual(1, firstGroup.Count()); // All groups have an empty title, so all settings are put inside the empty group
Assert.AreEqual(3, firstGroup.Count());
Assert.IsNotNull(settingsVm.SelectedItem); Assert.IsNotNull(settingsVm.SelectedItem);
var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem; var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem;
Assert.AreEqual("New", selectedItem.Title); Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType);
} }
} }
} }