mirror of
https://github.com/mgroves/MonodroidStockPortfolio.git
synced 2024-11-14 19:29:26 +00:00
mvp refactor of the edit-portfolio activity, also half-way refactoring the Validation framework stuff
This commit is contained in:
parent
f6d7c71188
commit
9a0d711294
13 changed files with 353 additions and 55 deletions
|
@ -31,6 +31,8 @@
|
|||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
|
|
28
MonoStockPortfolio.Tests/Framework/ValidationTests.cs
Normal file
28
MonoStockPortfolio.Tests/Framework/ValidationTests.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Machine.Specifications;
|
||||
using MonoStockPortfolio.Activites.EditPortfolioScreen;
|
||||
using MonoStockPortfolio.Framework;
|
||||
|
||||
namespace MonoStockPortfolio.Tests.Framework
|
||||
{
|
||||
public class ValidationTests
|
||||
{
|
||||
static NewFormValidator _validator;
|
||||
static IEnumerable<string> _errors;
|
||||
|
||||
Establish context = () =>
|
||||
{
|
||||
_validator = new NewFormValidator();
|
||||
};
|
||||
|
||||
Because of = () =>
|
||||
{
|
||||
_validator.AddRequired(() => "", "This is required");
|
||||
_errors = _validator.Apply();
|
||||
};
|
||||
|
||||
It should_return_1_error_message = () =>
|
||||
_errors.Count().ShouldEqual(1);
|
||||
}
|
||||
}
|
|
@ -62,6 +62,8 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Framework\ValidationTests.cs" />
|
||||
<Compile Include="Presenters\EditPortfolioTests.cs" />
|
||||
<Compile Include="Presenters\MainPresenterTests.cs" />
|
||||
<Compile Include="Presenters\PortfolioPresenterTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
112
MonoStockPortfolio.Tests/Presenters/EditPortfolioTests.cs
Normal file
112
MonoStockPortfolio.Tests/Presenters/EditPortfolioTests.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Machine.Specifications;
|
||||
using MonoStockPortfolio.Activites.EditPortfolioScreen;
|
||||
using MonoStockPortfolio.Core.PortfolioRepositories;
|
||||
using MonoStockPortfolio.Entities;
|
||||
using Telerik.JustMock;
|
||||
|
||||
namespace MonoStockPortfolio.Tests.Presenters
|
||||
{
|
||||
public class EditPortfolioTests
|
||||
{
|
||||
protected static EditPortfolioPresenter _presenter;
|
||||
protected static IPortfolioRepository _mockPortfolioRepository;
|
||||
protected static IEditPortfolioView _mockEditPortfolioView;
|
||||
|
||||
Establish context = () =>
|
||||
{
|
||||
_mockPortfolioRepository = Mock.Create<IPortfolioRepository>();
|
||||
Mock.Arrange(() => _mockPortfolioRepository.GetPortfolioById(999)).Returns(
|
||||
new Portfolio(999) { Name = "Testing Portfolio!" });
|
||||
|
||||
_mockEditPortfolioView = Mock.Create<IEditPortfolioView>();
|
||||
|
||||
_presenter = new EditPortfolioPresenter(_mockPortfolioRepository);
|
||||
};
|
||||
}
|
||||
|
||||
public class When_initializing_the_edit_portfolio_presenter_with_no_id : EditPortfolioTests
|
||||
{
|
||||
Because of = () =>
|
||||
{
|
||||
_presenter.Initialize(_mockEditPortfolioView, null);
|
||||
};
|
||||
|
||||
It should_set_the_title_to_Add_New_Portfolio = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.SetTitle("Add New Portfolio"), Occurs.Exactly(1));
|
||||
It shouldnt_prepopulate_the_form_with_anything = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.PopulateForm(Arg.IsAny<Portfolio>()), Occurs.Never());
|
||||
}
|
||||
|
||||
public class When_initializing_the_edit_portfolio_presenter_with_an_id : EditPortfolioTests
|
||||
{
|
||||
Because of = () =>
|
||||
{
|
||||
_presenter.Initialize(_mockEditPortfolioView, 999);
|
||||
};
|
||||
|
||||
It should_set_the_title_to_Edit_Portfolio = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.SetTitle("Edit Portfolio"), Occurs.Exactly(1));
|
||||
It should_prepopulate_the_form_with_a_portfolio_name = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.PopulateForm(Arg.Matches<Portfolio>(x => x.Name == "Testing Portfolio!")), Occurs.Exactly(1));
|
||||
}
|
||||
|
||||
public class When_the_user_wants_to_save_a_valid_portfolio : EditPortfolioTests
|
||||
{
|
||||
Establish context = () =>
|
||||
{
|
||||
_presenter.Initialize(_mockEditPortfolioView, null);
|
||||
};
|
||||
|
||||
Because of = () =>
|
||||
{
|
||||
_presenter.SavePortfolio(new Portfolio(999) { Name = "Whatever Portfolio" });
|
||||
};
|
||||
|
||||
It should_use_the_repository_to_save_the_portfolio = () =>
|
||||
Mock.Assert(() => _mockPortfolioRepository.SavePortfolio(Arg.Matches<Portfolio>(x => x.ID == 999 && x.Name == "Whatever Portfolio")), Occurs.Exactly(1));
|
||||
It should_tell_the_view_to_show_a_nice_saved_message = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.ShowSaveSuccessMessage("You saved: Whatever Portfolio"), Occurs.Exactly(1));
|
||||
It should_tell_the_view_to_go_back_to_the_main_activity = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.GoBackToMainActivity(), Occurs.Exactly(1));
|
||||
}
|
||||
|
||||
public class When_the_user_tries_to_save_a_new_portfolio_with_a_blank_name : EditPortfolioTests
|
||||
{
|
||||
Establish context = () =>
|
||||
{
|
||||
_presenter.Initialize(_mockEditPortfolioView);
|
||||
};
|
||||
|
||||
Because of = () =>
|
||||
{
|
||||
_presenter.SavePortfolio(new Portfolio { Name = "" });
|
||||
};
|
||||
|
||||
It should_return_1_validation_error = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.ShowValidationErrors(Arg.Matches<IEnumerable<string>>(x => x.Count() == 1)), Occurs.Exactly(1));
|
||||
It should_return_a_nice_required_validation_error_message = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.ShowValidationErrors(Arg.Matches<IEnumerable<string>>(x => x.Single() == "Please enter a portfolio name")), Occurs.Exactly(1));
|
||||
}
|
||||
|
||||
public class When_the_user_tries_to_save_a_portfolio_with_a_duplicated_name : EditPortfolioTests
|
||||
{
|
||||
Establish context = () =>
|
||||
{
|
||||
_presenter.Initialize(_mockEditPortfolioView);
|
||||
};
|
||||
|
||||
Because of = () =>
|
||||
{
|
||||
Mock.Arrange(() => _mockPortfolioRepository.GetPortfolioByName(Arg.AnyString)).Returns(
|
||||
new Portfolio(998) {Name = "Some Name"});
|
||||
_presenter.SavePortfolio(new Portfolio { Name = "Some Name" });
|
||||
};
|
||||
|
||||
It should_return_1_validation_error = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.ShowValidationErrors(Arg.Matches<IEnumerable<string>>(x => x.Count() == 1)), Occurs.Exactly(1));
|
||||
It should_return_a_nice_duplication_error_message = () =>
|
||||
Mock.Assert(() => _mockEditPortfolioView.ShowValidationErrors(Arg.Matches<IEnumerable<string>>(x => x.Single() == "Portfolio name is already taken")), Occurs.Exactly(1));
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using MonoStockPortfolio.Core.PortfolioRepositories;
|
||||
using MonoStockPortfolio.Entities;
|
||||
using MonoStockPortfolio.Framework;
|
||||
|
||||
namespace MonoStockPortfolio.Activites
|
||||
namespace MonoStockPortfolio.Activites.EditPortfolioScreen
|
||||
{
|
||||
[Activity(Label = "Add Portfolio", MainLauncher = false, Name = "monostockportfolio.activites.EditPortfolioActivity")]
|
||||
public class EditPortfolioActivity : Activity
|
||||
public class EditPortfolioActivity : Activity, IEditPortfolioView
|
||||
{
|
||||
[IoC] private IPortfolioRepository _repo;
|
||||
[IoC] private IEditPortfolioPresenter _presenter;
|
||||
|
||||
[LazyView(Resource.Id.btnSave)] protected Button SaveButton;
|
||||
[LazyView(Resource.Id.portfolioName)] protected EditText PortfolioName;
|
||||
|
@ -32,7 +32,41 @@ namespace MonoStockPortfolio.Activites
|
|||
intent.PutExtra(PORTFOLIOIDEXTRA, portfolioId);
|
||||
return intent;
|
||||
}
|
||||
public long ThisPortfolioId { get { return Intent.GetLongExtra(PORTFOLIOIDEXTRA, -1); } }
|
||||
|
||||
#region IEditPortfolioView members
|
||||
|
||||
public void SetTitle(string title)
|
||||
{
|
||||
this.Title = title;
|
||||
}
|
||||
|
||||
public void PopulateForm(Portfolio portfolio)
|
||||
{
|
||||
PortfolioName.Text = portfolio.Name;
|
||||
}
|
||||
|
||||
public void ShowSaveSuccessMessage(string message)
|
||||
{
|
||||
this.LongToast(message);
|
||||
}
|
||||
|
||||
public void GoBackToMainActivity()
|
||||
{
|
||||
this.EndActivity();
|
||||
}
|
||||
|
||||
public void ShowValidationErrors(IEnumerable<string> errors)
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
foreach (var error in errors)
|
||||
{
|
||||
errorMessage += error + "\n";
|
||||
}
|
||||
errorMessage = errorMessage.Trim('\n');
|
||||
this.LongToast(errorMessage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
|
@ -40,20 +74,17 @@ namespace MonoStockPortfolio.Activites
|
|||
|
||||
SetContentView(Resource.Layout.addportfolio);
|
||||
|
||||
WireUpEvents();
|
||||
|
||||
var portfolioId = ThisPortfolioId;
|
||||
if(portfolioId != -1)
|
||||
var portfolioId = Intent.GetLongExtra(PORTFOLIOIDEXTRA, -1);
|
||||
if (portfolioId != -1)
|
||||
{
|
||||
this.Title = "Edit Portfolio";
|
||||
PopulateForm(portfolioId);
|
||||
_presenter.Initialize(this, portfolioId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_presenter.Initialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateForm(long portfolioId)
|
||||
{
|
||||
var portfolio = _repo.GetPortfolioById(portfolioId);
|
||||
PortfolioName.Text = portfolio.Name;
|
||||
WireUpEvents();
|
||||
}
|
||||
|
||||
private void WireUpEvents()
|
||||
|
@ -63,47 +94,13 @@ namespace MonoStockPortfolio.Activites
|
|||
|
||||
private void saveButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Portfolio portfolioToSave = GetPortfolioToSave();
|
||||
|
||||
if (Validate(portfolioToSave))
|
||||
{
|
||||
_repo.SavePortfolio(portfolioToSave);
|
||||
|
||||
this.LongToast("You saved: " + PortfolioName.Text);
|
||||
|
||||
this.EndActivity();
|
||||
}
|
||||
}
|
||||
|
||||
private bool Validate(Portfolio portfolioToSave)
|
||||
{
|
||||
var validator = new FormValidator();
|
||||
validator.AddRequired(PortfolioName, "Please enter a portfolio name");
|
||||
validator.AddValidation(PortfolioName, () => IsDuplicateName(portfolioToSave));
|
||||
|
||||
var result = validator.Apply();
|
||||
if(result != string.Empty)
|
||||
{
|
||||
this.LongToast(result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private string IsDuplicateName(Portfolio portfolioToSave)
|
||||
{
|
||||
var portfolio = _repo.GetPortfolioByName(portfolioToSave.Name);
|
||||
if(portfolio != null && portfolio.ID != portfolioToSave.ID)
|
||||
{
|
||||
return "Portfolio name is already taken";
|
||||
}
|
||||
return string.Empty;
|
||||
_presenter.SavePortfolio(GetPortfolioToSave());
|
||||
}
|
||||
|
||||
private Portfolio GetPortfolioToSave()
|
||||
{
|
||||
Portfolio portfolioToSave;
|
||||
var portfolioId = ThisPortfolioId;
|
||||
var portfolioId = Intent.GetLongExtra(PORTFOLIOIDEXTRA, -1);
|
||||
if (portfolioId != -1)
|
||||
{
|
||||
portfolioToSave = new Portfolio(portfolioId);
|
||||
|
@ -112,7 +109,7 @@ namespace MonoStockPortfolio.Activites
|
|||
{
|
||||
portfolioToSave = new Portfolio();
|
||||
}
|
||||
portfolioToSave.Name = PortfolioName.Text.ToString();
|
||||
portfolioToSave.Name = PortfolioName.Text;
|
||||
return portfolioToSave;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MonoStockPortfolio.Core.PortfolioRepositories;
|
||||
using MonoStockPortfolio.Entities;
|
||||
using MonoStockPortfolio.Framework;
|
||||
|
||||
namespace MonoStockPortfolio.Activites.EditPortfolioScreen
|
||||
{
|
||||
public class EditPortfolioPresenter : IEditPortfolioPresenter
|
||||
{
|
||||
private IEditPortfolioView _currentView;
|
||||
private long? _portfolioId;
|
||||
private readonly IPortfolioRepository _porfolioRepository;
|
||||
|
||||
public EditPortfolioPresenter(IPortfolioRepository portfolioRepository)
|
||||
{
|
||||
_porfolioRepository = portfolioRepository;
|
||||
}
|
||||
|
||||
public void Initialize(IEditPortfolioView editPortfolioView, long? portfolioId = null)
|
||||
{
|
||||
_portfolioId = portfolioId;
|
||||
_currentView = editPortfolioView;
|
||||
|
||||
SetTitle();
|
||||
|
||||
PrepopulateForm();
|
||||
}
|
||||
|
||||
public void SavePortfolio(Portfolio portfolioToSave)
|
||||
{
|
||||
var errors = Validate((portfolioToSave));
|
||||
if (!errors.Any())
|
||||
{
|
||||
_porfolioRepository.SavePortfolio(portfolioToSave);
|
||||
|
||||
_currentView.ShowSaveSuccessMessage("You saved: " + portfolioToSave.Name);
|
||||
|
||||
_currentView.GoBackToMainActivity();
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentView.ShowValidationErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> Validate(Portfolio portfolioToSave)
|
||||
{
|
||||
var validator = new NewFormValidator();
|
||||
validator.AddRequired(() => portfolioToSave.Name, "Please enter a portfolio name");
|
||||
validator.AddValidation(() => portfolioToSave.Name, () => IsDuplicateName(portfolioToSave));
|
||||
|
||||
return validator.Apply().ToList();
|
||||
}
|
||||
|
||||
private string IsDuplicateName(Portfolio portfolioToSave)
|
||||
{
|
||||
var portfolio = _porfolioRepository.GetPortfolioByName(portfolioToSave.Name);
|
||||
if (portfolio != null && portfolio.ID != portfolioToSave.ID)
|
||||
{
|
||||
return "Portfolio name is already taken";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void PrepopulateForm()
|
||||
{
|
||||
if (_portfolioId != null)
|
||||
{
|
||||
var portfolio = _porfolioRepository.GetPortfolioById(_portfolioId ?? -1);
|
||||
_currentView.PopulateForm(portfolio);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTitle()
|
||||
{
|
||||
_currentView.SetTitle(_portfolioId == null ? "Add New Portfolio" : "Edit Portfolio");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using MonoStockPortfolio.Entities;
|
||||
|
||||
namespace MonoStockPortfolio.Activites.EditPortfolioScreen
|
||||
{
|
||||
public interface IEditPortfolioPresenter
|
||||
{
|
||||
void Initialize(IEditPortfolioView editPortfolioView, long? portfolioId = null);
|
||||
void SavePortfolio(Portfolio portfolioToSave);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using MonoStockPortfolio.Entities;
|
||||
|
||||
namespace MonoStockPortfolio.Activites.EditPortfolioScreen
|
||||
{
|
||||
public interface IEditPortfolioView
|
||||
{
|
||||
void SetTitle(string title);
|
||||
void PopulateForm(Portfolio portfolio);
|
||||
void ShowSaveSuccessMessage(string message);
|
||||
void GoBackToMainActivity();
|
||||
void ShowValidationErrors(IEnumerable<string> errors);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using Android.Content;
|
|||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using MonoStockPortfolio.Activites.EditPortfolioScreen;
|
||||
using MonoStockPortfolio.Activites.PortfolioScreen;
|
||||
using MonoStockPortfolio.Framework;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Reflection;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Widget;
|
||||
|
|
45
MonoStockPortfolio/Framework/NewFormValidator.cs
Normal file
45
MonoStockPortfolio/Framework/NewFormValidator.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MonoStockPortfolio.Framework
|
||||
{
|
||||
public class NewFormValidator
|
||||
{
|
||||
private readonly IList<Func<string>> _list;
|
||||
|
||||
public NewFormValidator()
|
||||
{
|
||||
_list = new List<Func<string>>();
|
||||
}
|
||||
|
||||
public void AddValidation(Func<string> getValue, Func<string> validationFunction)
|
||||
{
|
||||
_list.Add(validationFunction);
|
||||
}
|
||||
|
||||
public void AddRequired(Func<string> getValue, string errorMessage)
|
||||
{
|
||||
AddValidation(getValue, () => Required(getValue(), errorMessage));
|
||||
}
|
||||
|
||||
public IEnumerable<string> Apply()
|
||||
{
|
||||
return _list.Select(validation => validation())
|
||||
.Where(result => !string.IsNullOrEmpty(result));
|
||||
}
|
||||
|
||||
#region validation functions
|
||||
|
||||
private static string Required(string getValue, string errorMessage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(getValue))
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Util;
|
||||
using MonoStockPortfolio.Activites.EditPortfolioScreen;
|
||||
using MonoStockPortfolio.Activites.MainScreen;
|
||||
using MonoStockPortfolio.Activites.PortfolioScreen;
|
||||
using MonoStockPortfolio.Core.Config;
|
||||
|
@ -26,6 +27,7 @@ namespace MonoStockPortfolio.Framework
|
|||
// presenters
|
||||
IttyBittyIoC.Register<IMainPresenter, MainPresenter>();
|
||||
IttyBittyIoC.Register<IPortfolioPresenter, PortfolioPresenter>();
|
||||
IttyBittyIoC.Register<IEditPortfolioPresenter, EditPortfolioPresenter>();
|
||||
}
|
||||
|
||||
public static object Get(Type serviceType)
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
|
@ -53,7 +55,11 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activites\ConfigActivity.cs" />
|
||||
<Compile Include="Activites\EditPortfolioActivity.cs" />
|
||||
<Compile Include="Activites\EditPortfolioScreen\EditPortfolioActivity.cs" />
|
||||
<Compile Include="Activites\EditPortfolioScreen\EditPortfolioPresenter.cs" />
|
||||
<Compile Include="Activites\EditPortfolioScreen\IEditPortfolioPresenter.cs" />
|
||||
<Compile Include="Activites\EditPortfolioScreen\IEditPortfolioView.cs" />
|
||||
<Compile Include="Framework\NewFormValidator.cs" />
|
||||
<Compile Include="Activites\MainScreen\IMainPresenter.cs" />
|
||||
<Compile Include="Activites\MainScreen\IMainView.cs" />
|
||||
<Compile Include="Activites\MainScreen\MainPresenter.cs" />
|
||||
|
|
Loading…
Reference in a new issue