diff options
Diffstat (limited to 'src/Notepad')
95 files changed, 3675 insertions, 0 deletions
diff --git a/src/Notepad/DataAccess/Repositories/DefaultRepository.cs b/src/Notepad/DataAccess/Repositories/DefaultRepository.cs new file mode 100644 index 0000000..c429f19 --- /dev/null +++ b/src/Notepad/DataAccess/Repositories/DefaultRepository.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Container;
+
+namespace Notepad.DataAccess.Repositories {
+ public class DefaultRepository<T> : IRepository<T> {
+ private IDependencyRegistry registry;
+
+ public DefaultRepository(IDependencyRegistry registry) {
+ this.registry = registry;
+ }
+
+ public IEnumerable<T> All() {
+ return registry.AllImplementationsOf<T>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/DataAccess/Repositories/DefaultRepositorySpecs.cs b/src/Notepad/DataAccess/Repositories/DefaultRepositorySpecs.cs new file mode 100644 index 0000000..712a584 --- /dev/null +++ b/src/Notepad/DataAccess/Repositories/DefaultRepositorySpecs.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Container;
+using Rhino.Mocks;
+
+namespace Notepad.DataAccess.Repositories {
+ public class DefaultRepositorySpecs {}
+
+ [TestFixture]
+ public class when_retrieving_all_the_items_from_the_default_repository {
+ private MockRepository mockery;
+ private IDependencyRegistry registry;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ registry = mockery.DynamicMock<IDependencyRegistry>();
+ }
+
+ [Test]
+ public void should_leverage_the_resolver_to_retrieve_all_the_implementations() {
+ var intsToReturn = new List<int>();
+
+ using (mockery.Record()) {
+ Expect
+ .Call(registry.AllImplementationsOf<int>())
+ .Return(intsToReturn)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().All();
+ }
+ }
+
+ private IRepository<int> CreateSUT() {
+ return new DefaultRepository<int>(registry);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Domain/FileSystem/AbsoluteFilePath.cs b/src/Notepad/Domain/FileSystem/AbsoluteFilePath.cs new file mode 100644 index 0000000..31ef07b --- /dev/null +++ b/src/Notepad/Domain/FileSystem/AbsoluteFilePath.cs @@ -0,0 +1,34 @@ +namespace Notepad.Domain.FileSystem {
+ public class AbsoluteFilePath : IFilePath {
+ private readonly string rawFilePath;
+
+ public AbsoluteFilePath(string rawFilePath) {
+ this.rawFilePath = rawFilePath;
+ }
+
+ public string RawPathToFile() {
+ return rawFilePath;
+ }
+
+ public bool Equals(IFilePath other) {
+ if (ReferenceEquals(null, other)) {
+ return false;
+ }
+ return ReferenceEquals(this, other) || Equals(other.RawPathToFile(), rawFilePath);
+ }
+
+ public override bool Equals(object other) {
+ if (ReferenceEquals(null, other)) {
+ return false;
+ }
+ if (ReferenceEquals(this, other)) {
+ return true;
+ }
+ return other.GetType() == typeof (AbsoluteFilePath) && Equals((AbsoluteFilePath) other);
+ }
+
+ public override int GetHashCode() {
+ return (rawFilePath != null ? rawFilePath.GetHashCode() : 0);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Domain/FileSystem/IFilePath.cs b/src/Notepad/Domain/FileSystem/IFilePath.cs new file mode 100644 index 0000000..094907a --- /dev/null +++ b/src/Notepad/Domain/FileSystem/IFilePath.cs @@ -0,0 +1,7 @@ +using System;
+
+namespace Notepad.Domain.FileSystem {
+ public interface IFilePath : IEquatable<IFilePath> {
+ string RawPathToFile();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Domain/Repositories/IRepository.cs b/src/Notepad/Domain/Repositories/IRepository.cs new file mode 100644 index 0000000..fd25072 --- /dev/null +++ b/src/Notepad/Domain/Repositories/IRepository.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic;
+
+namespace Notepad.Domain.Repositories {
+ public interface IRepository<T> {
+ IEnumerable<T> All();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/DependencyResolutionException.cs b/src/Notepad/Infrastructure/Container/DependencyResolutionException.cs new file mode 100644 index 0000000..fe36112 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/DependencyResolutionException.cs @@ -0,0 +1,9 @@ +using System;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Infrastructure.Container {
+ public class DependencyResolutionException<T> : Exception {
+ public DependencyResolutionException(Exception innerException)
+ : base("Could not resolve {0}".FormatWith(typeof (T).FullName), innerException) {}
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/IDependencyRegistry.cs b/src/Notepad/Infrastructure/Container/IDependencyRegistry.cs new file mode 100644 index 0000000..fbcd911 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/IDependencyRegistry.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic;
+
+namespace Notepad.Infrastructure.Container {
+ public interface IDependencyRegistry {
+ Interface FindAnImplementationOf<Interface>();
+ IEnumerable<Interface> AllImplementationsOf<Interface>();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/Resolve.cs b/src/Notepad/Infrastructure/Container/Resolve.cs new file mode 100644 index 0000000..507ccb4 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/Resolve.cs @@ -0,0 +1,20 @@ +using System;
+
+namespace Notepad.Infrastructure.Container {
+ public static class Resolve {
+ private static IDependencyRegistry underlyingRegistry;
+
+ public static void InitializeWith(IDependencyRegistry registry) {
+ underlyingRegistry = registry;
+ }
+
+ public static DependencyToResolve DependencyFor<DependencyToResolve>() {
+ try {
+ return underlyingRegistry.FindAnImplementationOf<DependencyToResolve>();
+ }
+ catch (Exception e) {
+ throw new DependencyResolutionException<DependencyToResolve>(e);
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/ResolveSpecs.cs b/src/Notepad/Infrastructure/Container/ResolveSpecs.cs new file mode 100644 index 0000000..e16d137 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/ResolveSpecs.cs @@ -0,0 +1,83 @@ +using System;
+using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Infrastructure.Container {
+ public class ResolveSpecs {}
+
+ [TestFixture]
+ public class when_resolving_a_dependency_using_the_resolve_gateway_ {
+ private MockRepository mockery;
+ private IDependencyRegistry registry;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ registry = mockery.DynamicMock<IDependencyRegistry>();
+ Resolve.InitializeWith(registry);
+ }
+
+ [Test]
+ public void should_leverage_the_underlying_container_it_was_initialized_with() {
+ var presenter = mockery.DynamicMock<IPresenter>();
+
+ using (mockery.Record()) {
+ Expect
+ .Call(registry.FindAnImplementationOf<IPresenter>())
+ .Return(presenter)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ Resolve.DependencyFor<IPresenter>();
+ }
+ }
+
+ [Test]
+ public void should_return_the_resolved_dependency() {
+ var presenter = mockery.DynamicMock<IPresenter>();
+
+ using (mockery.Record()) {
+ Expect
+ .Call(registry.FindAnImplementationOf<IPresenter>())
+ .Return(presenter)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ Resolve.DependencyFor<IPresenter>().ShouldBeEqualTo(presenter);
+ }
+ }
+ }
+
+ [TestFixture]
+ public class when_resolving_a_dependency_that_is_not_registered_ {
+ private MockRepository mockery;
+ private IDependencyRegistry registry;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ registry = mockery.DynamicMock<IDependencyRegistry>();
+ Resolve.InitializeWith(registry);
+ }
+
+ [Test]
+ [ExpectedException(typeof (DependencyResolutionException<IPresenter>))]
+ public void should_throw_a_dependency_resolution_exception() {
+ using (mockery.Record()) {
+ SetupResult
+ .For(registry.FindAnImplementationOf<IPresenter>())
+ .Throw(new Exception());
+ }
+
+ using (mockery.Playback()) {
+ Resolve.DependencyFor<IPresenter>();
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/Windsor/IComponentExclusionSpecification.cs b/src/Notepad/Infrastructure/Container/Windsor/IComponentExclusionSpecification.cs new file mode 100644 index 0000000..b611859 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/Windsor/IComponentExclusionSpecification.cs @@ -0,0 +1,15 @@ +using System;
+using System.Windows.Forms;
+using Notepad.Infrastructure.Core;
+
+namespace Notepad.Infrastructure.Container.Windsor {
+ public interface IComponentExclusionSpecification : ISpecification<Type> {}
+
+ public class ComponentExclusionSpecification : IComponentExclusionSpecification {
+ public bool IsSatisfiedBy(Type type) {
+ return type.GetInterfaces().Length == 0
+ || type.IsSubclassOf(typeof (Form))
+ || type.IsAssignableFrom(typeof (IDependencyRegistry));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/Windsor/IWindsorContainerFactory.cs b/src/Notepad/Infrastructure/Container/Windsor/IWindsorContainerFactory.cs new file mode 100644 index 0000000..4087a5e --- /dev/null +++ b/src/Notepad/Infrastructure/Container/Windsor/IWindsorContainerFactory.cs @@ -0,0 +1,42 @@ +using Castle.MicroKernel.Registration;
+using Castle.Windsor;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Infrastructure.Container.Windsor {
+ public interface IWindsorContainerFactory {
+ IWindsorContainer Create();
+ }
+
+ public class WindsorContainerFactory : IWindsorContainerFactory {
+ private static IWindsorContainer container;
+ private IComponentExclusionSpecification criteriaToSatisfy;
+
+ public WindsorContainerFactory() : this(new ComponentExclusionSpecification()) {}
+
+ public WindsorContainerFactory(IComponentExclusionSpecification criteriaToSatisfy) {
+ this.criteriaToSatisfy = criteriaToSatisfy;
+ }
+
+ public IWindsorContainer Create() {
+ if (null == container) {
+ container = new WindsorContainer();
+ container.Register(
+ AllTypes
+ .Pick()
+ .FromAssembly(GetType().Assembly)
+ .WithService
+ .FirstInterface()
+ .Unless(criteriaToSatisfy.IsSatisfiedBy)
+ .Configure(
+ delegate(ComponentRegistration registration) {
+ this.LogInformational("{1}-{0}", registration.Implementation, registration.ServiceType.Name);
+ if (registration.Implementation.GetInterfaces().Length == 0) {
+ registration.For(registration.Implementation);
+ }
+ })
+ );
+ }
+ return container;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyRegistry.cs b/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyRegistry.cs new file mode 100644 index 0000000..75cc206 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyRegistry.cs @@ -0,0 +1,40 @@ +using System;
+using System.Collections.Generic;
+using Castle.Windsor;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Infrastructure.Container.Windsor {
+ public class WindsorDependencyRegistry : IDependencyRegistry {
+ private IWindsorContainer underlyingContainer;
+
+ public WindsorDependencyRegistry() : this(new WindsorContainerFactory()) {}
+
+ public WindsorDependencyRegistry(IWindsorContainerFactory factory) {
+ underlyingContainer = factory.Create();
+ }
+
+ public Interface FindAnImplementationOf<Interface>() {
+ return underlyingContainer.Kernel.Resolve<Interface>();
+ }
+
+ public void Register(Type typeOfInterface, Type typeOfImplementation) {
+ underlyingContainer
+ .Kernel
+ .AddComponent("{0}-{1}".FormatWith(typeOfInterface.FullName, typeOfImplementation.FullName),
+ typeOfInterface,
+ typeOfImplementation);
+ }
+
+ public void Register<Interface, Implementation>() {
+ Register(typeof (Interface), typeof (Implementation));
+ }
+
+ public void RegisterInstanceOf<Interface>(Interface instanceOfTheInterface) {
+ underlyingContainer.Kernel.AddComponentInstance<Interface>(instanceOfTheInterface);
+ }
+
+ public IEnumerable<Interface> AllImplementationsOf<Interface>() {
+ return underlyingContainer.Kernel.ResolveAll<Interface>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyResolverSpecs.cs b/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyResolverSpecs.cs new file mode 100644 index 0000000..8235643 --- /dev/null +++ b/src/Notepad/Infrastructure/Container/Windsor/WindsorDependencyResolverSpecs.cs @@ -0,0 +1,73 @@ +using Castle.Windsor;
+using MbUnit.Framework;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Infrastructure.Container.Windsor {
+ public class WindsorDependencyResolverSpecs {}
+
+
+ [TestFixture]
+ public class when_registering_a_singleton_component_with_the_windsor_container_ {
+ private WindsorDependencyRegistry sut;
+
+ [SetUp]
+ public void SetUp() {
+ sut = CreateSUT();
+ }
+
+ [Test]
+ public void should_return_the_same_instance_each_time_its_resolved() {
+ sut
+ .FindAnImplementationOf<IBird>()
+ .ShouldBeSameInstanceAs(sut.FindAnImplementationOf<IBird>());
+ }
+
+ [Test]
+ public void should_not_return_null() {
+ sut.FindAnImplementationOf<IBird>().ShouldNotBeNull();
+ }
+
+ private WindsorDependencyRegistry CreateSUT() {
+ return new WindsorDependencyRegistry();
+ }
+ }
+
+ [TestFixture]
+ public class when_creating_the_windsor_resolver_ {
+ private MockRepository mockery;
+ private IWindsorContainerFactory factory;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ factory = mockery.DynamicMock<IWindsorContainerFactory>();
+ }
+
+ [Test]
+ public void should_leverage_the_factory_to_create_the_underlying_container() {
+ var container = new WindsorContainer();
+ using (mockery.Record()) {
+ Expect
+ .Call(factory.Create())
+ .Return(container)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT();
+ }
+ }
+
+ private IDependencyRegistry CreateSUT() {
+ return new WindsorDependencyRegistry(factory);
+ }
+ }
+
+ public class BlueBird : IBird {
+ public void Initialize() {}
+ }
+
+ public interface IBird {}
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Core/ICommand.cs b/src/Notepad/Infrastructure/Core/ICommand.cs new file mode 100644 index 0000000..1fadec3 --- /dev/null +++ b/src/Notepad/Infrastructure/Core/ICommand.cs @@ -0,0 +1,5 @@ +namespace Notepad.Infrastructure.Core {
+ public interface ICommand {
+ void Execute();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Core/IMapper.cs b/src/Notepad/Infrastructure/Core/IMapper.cs new file mode 100644 index 0000000..39bad69 --- /dev/null +++ b/src/Notepad/Infrastructure/Core/IMapper.cs @@ -0,0 +1,5 @@ +namespace Notepad.Infrastructure.Core {
+ public interface IMapper<Input, Output> {
+ Output MapFrom(Input item);
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Core/ISpecification.cs b/src/Notepad/Infrastructure/Core/ISpecification.cs new file mode 100644 index 0000000..c61d8b9 --- /dev/null +++ b/src/Notepad/Infrastructure/Core/ISpecification.cs @@ -0,0 +1,5 @@ +namespace Notepad.Infrastructure.Core {
+ public interface ISpecification<T> {
+ bool IsSatisfiedBy(T item);
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Extensions/ConversionExtensions.cs b/src/Notepad/Infrastructure/Extensions/ConversionExtensions.cs new file mode 100644 index 0000000..0266e58 --- /dev/null +++ b/src/Notepad/Infrastructure/Extensions/ConversionExtensions.cs @@ -0,0 +1,7 @@ +namespace Notepad.Infrastructure.Extensions {
+ public static class ConversionExtensions {
+ public static T DowncastTo<T>(this object objectToCast) {
+ return (T) objectToCast;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Extensions/EnumerableExtensions.cs b/src/Notepad/Infrastructure/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..d57c0e1 --- /dev/null +++ b/src/Notepad/Infrastructure/Extensions/EnumerableExtensions.cs @@ -0,0 +1,24 @@ +using System;
+using System.Collections.Generic;
+
+namespace Notepad.Infrastructure.Extensions {
+ public static class EnumerableExtensions {
+ public static void Walk<T>(this IEnumerable<T> itemsToWalk) {
+ foreach (var item in itemsToWalk) {}
+ }
+
+ public static IEnumerable<T> ThatSatisfy<T>(this IEnumerable<T> itemsToPeekInto, Predicate<T> criteriaToSatisfy) {
+ foreach (var item in itemsToPeekInto) {
+ if (item.Satisfies(criteriaToSatisfy)) {
+ yield return item;
+ }
+ }
+ }
+
+ public static IEnumerable<T> SortedUsing<T>(this IEnumerable<T> itemsToSort, IComparer<T> sortingAlgorithm) {
+ var sortedItems = new List<T>(itemsToSort);
+ sortedItems.Sort(sortingAlgorithm);
+ return sortedItems;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Extensions/LoggingExtensions.cs b/src/Notepad/Infrastructure/Extensions/LoggingExtensions.cs new file mode 100644 index 0000000..a05e2fc --- /dev/null +++ b/src/Notepad/Infrastructure/Extensions/LoggingExtensions.cs @@ -0,0 +1,17 @@ +using System;
+using Notepad.Infrastructure.Logging;
+
+namespace Notepad.Infrastructure.Extensions {
+ public static class LoggingExtensions {
+ public static void LogError(this Exception errorToLog) {
+ Log.For(errorToLog).Error(errorToLog);
+ }
+
+ public static void LogInformational<T>(
+ this T typeToCreateLoggerFor,
+ string formattedMessage,
+ params object[] arguments) {
+ Log.For(typeToCreateLoggerFor).Informational(formattedMessage, arguments);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Extensions/SpecificationExtensions.cs b/src/Notepad/Infrastructure/Extensions/SpecificationExtensions.cs new file mode 100644 index 0000000..c7ed7dc --- /dev/null +++ b/src/Notepad/Infrastructure/Extensions/SpecificationExtensions.cs @@ -0,0 +1,14 @@ +using System;
+using Notepad.Infrastructure.Core;
+
+namespace Notepad.Infrastructure.Extensions {
+ public static class SpecificationExtensions {
+ public static bool Satisfies<T>(this T itemToValidate, Predicate<T> criteriaToSatisfy) {
+ return criteriaToSatisfy(itemToValidate);
+ }
+
+ public static bool Satisfies<T>(this T itemToValidate, ISpecification<T> criteriaToSatisfy) {
+ return itemToValidate.Satisfies(criteriaToSatisfy.IsSatisfiedBy);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Extensions/StringExtensions.cs b/src/Notepad/Infrastructure/Extensions/StringExtensions.cs new file mode 100644 index 0000000..9c19f97 --- /dev/null +++ b/src/Notepad/Infrastructure/Extensions/StringExtensions.cs @@ -0,0 +1,13 @@ +using Notepad.Domain.FileSystem;
+
+namespace Notepad.Infrastructure.Extensions {
+ public static class StringExtensions {
+ public static string FormatWith(this string formattedString, params object[] arguments) {
+ return string.Format(formattedString, arguments);
+ }
+
+ public static IFilePath AsAnAbsoluteFilePath(this string rawFilePath) {
+ return new AbsoluteFilePath(rawFilePath);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/ILogFactory.cs b/src/Notepad/Infrastructure/Logging/ILogFactory.cs new file mode 100644 index 0000000..ce1d2e1 --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/ILogFactory.cs @@ -0,0 +1,7 @@ +using System;
+
+namespace Notepad.Infrastructure.Logging {
+ public interface ILogFactory {
+ ILogger CreateFor(Type typeToCreateLoggerFor);
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/ILogger.cs b/src/Notepad/Infrastructure/Logging/ILogger.cs new file mode 100644 index 0000000..ac7dd6f --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/ILogger.cs @@ -0,0 +1,8 @@ +using System;
+
+namespace Notepad.Infrastructure.Logging {
+ public interface ILogger {
+ void Informational(string formattedString, params object[] arguments);
+ void Error(Exception e);
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/Log.cs b/src/Notepad/Infrastructure/Logging/Log.cs new file mode 100644 index 0000000..e8a3b4c --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/Log.cs @@ -0,0 +1,15 @@ +using Notepad.Infrastructure.Container;
+using Notepad.Infrastructure.Logging.Log4NetLogging;
+
+namespace Notepad.Infrastructure.Logging {
+ public static class Log {
+ public static ILogger For<T>(T typeToCreateLoggerFor) {
+ try {
+ return Resolve.DependencyFor<ILogFactory>().CreateFor(typeof (T));
+ }
+ catch {
+ return new Log4NetLogFactory().CreateFor(typeof (T));
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogFactory.cs b/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogFactory.cs new file mode 100644 index 0000000..0784a13 --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogFactory.cs @@ -0,0 +1,20 @@ +using System;
+using System.IO;
+using log4net;
+using log4net.Config;
+
+namespace Notepad.Infrastructure.Logging.Log4NetLogging {
+ public class Log4NetLogFactory : ILogFactory {
+ public Log4NetLogFactory() {
+ XmlConfigurator.Configure(PathToConfigFile());
+ }
+
+ public ILogger CreateFor(Type typeToCreateLoggerFor) {
+ return new Log4NetLogger(LogManager.GetLogger(typeToCreateLoggerFor));
+ }
+
+ private FileInfo PathToConfigFile() {
+ return new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config.xml"));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogger.cs b/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogger.cs new file mode 100644 index 0000000..5111622 --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/Log4NetLogging/Log4NetLogger.cs @@ -0,0 +1,20 @@ +using System;
+using log4net;
+
+namespace Notepad.Infrastructure.Logging.Log4NetLogging {
+ public class Log4NetLogger : ILogger {
+ private readonly ILog log;
+
+ public Log4NetLogger(ILog log) {
+ this.log = log;
+ }
+
+ public void Informational(string formattedString, params object[] arguments) {
+ log.InfoFormat(formattedString, arguments);
+ }
+
+ public void Error(Exception e) {
+ log.Error(e.ToString());
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/Logging/LogSpecs.cs b/src/Notepad/Infrastructure/Logging/LogSpecs.cs new file mode 100644 index 0000000..3e6aead --- /dev/null +++ b/src/Notepad/Infrastructure/Logging/LogSpecs.cs @@ -0,0 +1,40 @@ +using MbUnit.Framework;
+using Notepad.Infrastructure.Container;
+using Rhino.Mocks;
+
+namespace Notepad.Infrastructure.Logging {
+ public class LogSpecs {}
+
+ [TestFixture]
+ public class when_creating_a_logger_for_a_particular_type_ {
+ private MockRepository mockery;
+ private ILogFactory factory;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ factory = mockery.DynamicMock<ILogFactory>();
+
+ var resolver = mockery.DynamicMock<IDependencyRegistry>();
+ SetupResult.For(resolver.FindAnImplementationOf<ILogFactory>()).Return(factory);
+
+ Resolve.InitializeWith(resolver);
+ }
+
+ [Test]
+ public void should_leverage_the_log_factory_to_create_a_logger_for_the_given_type() {
+ var logger = mockery.DynamicMock<ILogger>();
+ using (mockery.Record()) {
+ Expect
+ .Call(factory.CreateFor(GetType()))
+ .Return(logger)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ Log.For(this);
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Infrastructure/System/IApplicationEnvironment.cs b/src/Notepad/Infrastructure/System/IApplicationEnvironment.cs new file mode 100644 index 0000000..64cc737 --- /dev/null +++ b/src/Notepad/Infrastructure/System/IApplicationEnvironment.cs @@ -0,0 +1,13 @@ +using System;
+
+namespace Notepad.Infrastructure.System {
+ public interface IApplicationEnvironment {
+ void ShutDown();
+ }
+
+ public class ApplicationEnvironment : IApplicationEnvironment {
+ public void ShutDown() {
+ Environment.Exit(Environment.ExitCode);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Notepad.csproj b/src/Notepad/Notepad.csproj new file mode 100644 index 0000000..fe58fd4 --- /dev/null +++ b/src/Notepad/Notepad.csproj @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{2DB82691-BF15-4538-8C5E-6BF8F4F875A9}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Notepad</RootNamespace>
+ <AssemblyName>Notepad</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Castle.Core, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\app\castle\Castle.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Castle.DynamicProxy2, Version=2.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\app\castle\Castle.DynamicProxy2.dll</HintPath>
+ </Reference>
+ <Reference Include="Castle.MicroKernel, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\app\castle\Castle.MicroKernel.dll</HintPath>
+ </Reference>
+ <Reference Include="Castle.Windsor, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\app\castle\Castle.Windsor.dll</HintPath>
+ </Reference>
+ <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\app\log4net\log4net.dll</HintPath>
+ </Reference>
+ <Reference Include="MbUnit.Framework, Version=2.4.2.130, Culture=neutral, PublicKeyToken=5e72ecd30bc408d5">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\tools\mbunit\MbUnit.Framework.dll</HintPath>
+ </Reference>
+ <Reference Include="Rhino.Mocks, Version=3.5.0.1, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\build\lib\test\rhino.mocks\Rhino.Mocks.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Deployment" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="DataAccess\Repositories\DefaultRepository.cs" />
+ <Compile Include="DataAccess\Repositories\DefaultRepositorySpecs.cs" />
+ <Compile Include="Domain\FileSystem\AbsoluteFilePath.cs" />
+ <Compile Include="Domain\FileSystem\IFilePath.cs" />
+ <Compile Include="Infrastructure\Container\Windsor\IComponentExclusionSpecification.cs" />
+ <Compile Include="Infrastructure\Extensions\LoggingExtensions.cs" />
+ <Compile Include="Presentation\Presenters\Commands\IRunPresenterCommand.cs" />
+ <Compile Include="Presentation\Presenters\Commands\RunPresenterCommandSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\Commands\SaveCommandSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\ExitMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\File\ExitMenuItemSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\FileMenu.cs" />
+ <Compile Include="Presentation\Model\Menu\File\FileMenuSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\NewMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\File\NewMenuItemSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\SaveAsMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\File\SaveAsMenuItemSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\SaveMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\File\SaveMenuItemSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\Help\AboutMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\Help\AboutMenuItemSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\Help\HelpMenu.cs" />
+ <Compile Include="Presentation\Model\Menu\Help\HelpMenuSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\IMenuItem.cs" />
+ <Compile Include="Presentation\Model\Menu\IMenuItemComparer.cs" />
+ <Compile Include="Presentation\Model\Menu\ISubMenuItemComparer.cs" />
+ <Compile Include="Presentation\Model\Menu\MenuNames.cs" />
+ <Compile Include="Infrastructure\Container\Windsor\IWindsorContainerFactory.cs" />
+ <Compile Include="Infrastructure\Core\ISpecification.cs" />
+ <Compile Include="Infrastructure\Extensions\ConversionExtensions.cs" />
+ <Compile Include="Infrastructure\Extensions\EnumerableExtensions.cs" />
+ <Compile Include="Infrastructure\Extensions\SpecificationExtensions.cs" />
+ <Compile Include="Infrastructure\Logging\ILogFactory.cs" />
+ <Compile Include="Infrastructure\Logging\ILogger.cs" />
+ <Compile Include="Infrastructure\Logging\Log.cs" />
+ <Compile Include="Infrastructure\Logging\Log4NetLogging\Log4NetLogger.cs" />
+ <Compile Include="Infrastructure\Logging\Log4NetLogging\Log4NetLogFactory.cs" />
+ <Compile Include="Infrastructure\Logging\LogSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\Commands\ISaveCommand.cs" />
+ <Compile Include="Presentation\Core\IPresenterRegistry.cs" />
+ <Compile Include="Presentation\Presenters\Menu\File\ISaveAsPresenter.cs" />
+ <Compile Include="Presentation\Presenters\Menu\File\SaveAsPresenterSpecs.cs" />
+ <Compile Include="Presentation\Presenters\Menu\Help\AboutApplicationPresenterSpecs.cs" />
+ <Compile Include="Presentation\Presenters\Menu\Help\IAboutApplicationPresenter.cs" />
+ <Compile Include="Presentation\Views\Menu\Help\AboutApplicationView.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ <Compile Include="Presentation\Views\Menu\Help\AboutApplicationView.Designer.cs">
+ <DependentUpon>AboutApplicationView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Presentation\Views\Menu\Help\IAboutApplicationView.cs" />
+ <Compile Include="Presentation\Views\Menu\Mappers\IMenuItemToToolStripMenuItemMapper.cs" />
+ <Compile Include="Presentation\Views\Menu\Mappers\ISubMenuToToolStripMenuItemMapper.cs" />
+ <Compile Include="Presentation\Views\Menu\File\ISaveAsView.cs" />
+ <Compile Include="Presentation\Views\Shell\WindowShell.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ <Compile Include="Presentation\Views\Shell\WindowShell.Designer.cs">
+ <DependentUpon>WindowShell.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Presentation\Views\Menu\Mappers\MenuItemToToolStripMenuItemMapperSpecs.cs" />
+ <Compile Include="Presentation\Views\Menu\Mappers\SubMenuToToolStripMenuItemMapper.cs" />
+ <Compile Include="Presentation\Views\Menu\Mappers\SubMenuToToolStripMenuItemMapperSpecs.cs" />
+ <Compile Include="Presentation\Context\NotepadApplicationContextSpecs.cs" />
+ <Compile Include="Presentation\Context\NotepadApplicationContext.cs" />
+ <Compile Include="Start.cs" />
+ <Compile Include="Presentation\Model\Menu\ISubMenu.cs" />
+ <Compile Include="Domain\Repositories\IRepository.cs" />
+ <Compile Include="Infrastructure\Container\DependencyResolutionException.cs" />
+ <Compile Include="Infrastructure\Container\Resolve.cs" />
+ <Compile Include="Infrastructure\Container\ResolveSpecs.cs" />
+ <Compile Include="Infrastructure\Container\Windsor\WindsorDependencyRegistry.cs" />
+ <Compile Include="Infrastructure\Container\Windsor\WindsorDependencyResolverSpecs.cs" />
+ <Compile Include="Infrastructure\Core\ICommand.cs" />
+ <Compile Include="Infrastructure\Core\IMapper.cs" />
+ <Compile Include="Infrastructure\Extensions\StringExtensions.cs" />
+ <Compile Include="Infrastructure\System\IApplicationEnvironment.cs" />
+ <Compile Include="Presentation\Model\Menu\File\Commands\ExitCommandSpecs.cs" />
+ <Compile Include="Presentation\Model\Menu\File\Commands\IExitCommand.cs" />
+ <Compile Include="Presentation\Core\ApplicationControllerSpecs.cs" />
+ <Compile Include="Presentation\Core\ApplicationController.cs" />
+ <Compile Include="Presentation\Core\IApplicationController.cs" />
+ <Compile Include="Presentation\Core\IPresenter.cs" />
+ <Compile Include="Infrastructure\Container\IDependencyRegistry.cs" />
+ <Compile Include="Presentation\Presenters\Menu\MainMenuPresenterSpecs.cs" />
+ <Compile Include="Presentation\Presenters\Shell\MainShellPresenterSpecs.cs" />
+ <Compile Include="Presentation\Presenters\Menu\IMainMenuPresenter.cs" />
+ <Compile Include="Presentation\Presenters\Shell\IMainShellPresenter.cs" />
+ <Compile Include="Presentation\Presenters\Shell\MainShellPresenter.cs" />
+ <Compile Include="Presentation\Views\Menu\IMainMenuView.cs" />
+ <Compile Include="Presentation\Views\Menu\MainMenuViewSpecs.cs" />
+ <Compile Include="Program.cs" />
+ <EmbeddedResource Include="Presentation\Views\Menu\Help\AboutApplicationView.resx">
+ <DependentUpon>AboutApplicationView.cs</DependentUpon>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Presentation\Views\Shell\WindowShell.resx">
+ <DependentUpon>WindowShell.cs</DependentUpon>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Properties\Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <Compile Include="Properties\Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ <Compile Include="Properties\Settings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Settings.settings</DependentUpon>
+ <DesignTimeSharedInput>True</DesignTimeSharedInput>
+ </Compile>
+ <Compile Include="Tasks\IDocumentTasks.cs" />
+ <Compile Include="Tasks\Stubs\StubDocumentTasks.cs" />
+ <Compile Include="Test\Call.cs" />
+ <Compile Include="Test\Extensions\AssertionExtensions.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/src/Notepad/Presentation/Context/NotepadApplicationContext.cs b/src/Notepad/Presentation/Context/NotepadApplicationContext.cs new file mode 100644 index 0000000..8fda742 --- /dev/null +++ b/src/Notepad/Presentation/Context/NotepadApplicationContext.cs @@ -0,0 +1,18 @@ +using System.Windows.Forms;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Shell;
+using Notepad.Presentation.Views.Shell;
+
+namespace Notepad.Presentation.Context {
+ public class NotepadApplicationContext : ApplicationContext {
+ public NotepadApplicationContext(
+ WindowShell shellView,
+ IExitCommand exitCommand,
+ IApplicationController applicationController) {
+ shellView.Closed += delegate { exitCommand.Execute(); };
+ applicationController.Run<IMainShellPresenter>();
+ MainForm = shellView;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs b/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs new file mode 100644 index 0000000..f8b9765 --- /dev/null +++ b/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs @@ -0,0 +1,80 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Context;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Shell;
+using Notepad.Presentation.Views.Shell;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+using Rhino.Mocks.Constraints;
+using Rhino.Mocks.Interfaces;
+
+namespace Notepad.Presentation.Context {
+ public class NotepadApplicationContextSpecs {}
+
+ [TestFixture]
+ public class when_creating_the_application_context_ {
+ private MockRepository mockery;
+ private WindowShell shellView;
+ private IExitCommand exitCommand;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ shellView = mockery.DynamicMock<WindowShell>();
+ exitCommand = mockery.DynamicMock<IExitCommand>();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_register_for_the_main_shell_views_closing_event() {
+ using (mockery.Record()) {
+ shellView.Closed += null;
+ LastCall.Constraints(Is.NotNull());
+ }
+ using (mockery.Playback()) {
+ CreateSUT();
+ }
+ }
+
+ [Test]
+ public void should_execute_the_exit_application_command() {
+ IEventRaiser raiser;
+ using (mockery.Record()) {
+ shellView.Closed += null;
+ raiser = LastCall.Constraints(Is.NotNull()).GetEventRaiser();
+
+ exitCommand.Execute();
+ }
+ using (mockery.Playback()) {
+ CreateSUT();
+ raiser.Raise(null, null);
+ }
+ }
+
+ [Test]
+ public void should_specify_the_main_shell_view_as_the_main_form() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MainForm.ShouldBeEqualTo(shellView);
+ }
+ }
+
+ [Test]
+ public void should_run_the_main_shell_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IMainShellPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT();
+ }
+ }
+
+ private NotepadApplicationContext CreateSUT() {
+ return new NotepadApplicationContext(shellView, exitCommand, applicationController);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Core/ApplicationController.cs b/src/Notepad/Presentation/Core/ApplicationController.cs new file mode 100644 index 0000000..4970593 --- /dev/null +++ b/src/Notepad/Presentation/Core/ApplicationController.cs @@ -0,0 +1,13 @@ +namespace Notepad.Presentation.Core {
+ public class ApplicationController : IApplicationController {
+ private readonly IPresenterRegistry registeredPresenters;
+
+ public ApplicationController(IPresenterRegistry presenterRegistry) {
+ registeredPresenters = presenterRegistry;
+ }
+
+ public void Run<Presenter>() where Presenter : IPresenter {
+ registeredPresenters.FindAnImplementationOf<Presenter>().Initialize();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs b/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs new file mode 100644 index 0000000..834ec7c --- /dev/null +++ b/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs @@ -0,0 +1,54 @@ +using MbUnit.Framework;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Core {
+ public class ApplicationControllerSpecs {}
+
+ [TestFixture]
+ public class when_the_application_controller_is_asked_to_run_a_presenter_ {
+ private MockRepository mockery;
+ private IPresenterRegistry presenterRegistry;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ presenterRegistry = mockery.DynamicMock<IPresenterRegistry>();
+ }
+
+ [Test]
+ public void should_ask_the_registered_presenters_for_an_instance_of_the_presenter_to_run() {
+ var implementationOfThePresenter = mockery.DynamicMock<IPresenter>();
+ using (mockery.Record()) {
+ Expect
+ .Call(presenterRegistry.FindAnImplementationOf<IPresenter>())
+ .Return(implementationOfThePresenter)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Run<IPresenter>();
+ }
+ }
+
+ [Test]
+ public void should_initialize_the_presenter_to_run() {
+ var presenterToInitialize = mockery.DynamicMock<IPresenter>();
+ using (mockery.Record()) {
+ SetupResult
+ .For(presenterRegistry.FindAnImplementationOf<IPresenter>())
+ .Return(presenterToInitialize);
+
+ presenterToInitialize.Initialize();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Run<IPresenter>();
+ }
+ }
+
+ private IApplicationController CreateSUT() {
+ return new ApplicationController(presenterRegistry);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Core/IApplicationController.cs b/src/Notepad/Presentation/Core/IApplicationController.cs new file mode 100644 index 0000000..735ef04 --- /dev/null +++ b/src/Notepad/Presentation/Core/IApplicationController.cs @@ -0,0 +1,5 @@ +namespace Notepad.Presentation.Core {
+ public interface IApplicationController {
+ void Run<Presenter>() where Presenter : IPresenter;
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Core/IPresenter.cs b/src/Notepad/Presentation/Core/IPresenter.cs new file mode 100644 index 0000000..45d40b2 --- /dev/null +++ b/src/Notepad/Presentation/Core/IPresenter.cs @@ -0,0 +1,5 @@ +namespace Notepad.Presentation.Core {
+ public interface IPresenter {
+ void Initialize();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Core/IPresenterRegistry.cs b/src/Notepad/Presentation/Core/IPresenterRegistry.cs new file mode 100644 index 0000000..9117489 --- /dev/null +++ b/src/Notepad/Presentation/Core/IPresenterRegistry.cs @@ -0,0 +1,13 @@ +using Notepad.Infrastructure.Container;
+
+namespace Notepad.Presentation.Core {
+ public interface IPresenterRegistry {
+ Presenter FindAnImplementationOf<Presenter>() where Presenter : IPresenter;
+ }
+
+ public class RegisteredPresenter : IPresenterRegistry {
+ public Presenter FindAnImplementationOf<Presenter>() where Presenter : IPresenter {
+ return Resolve.DependencyFor<Presenter>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs new file mode 100644 index 0000000..00855b6 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs @@ -0,0 +1,34 @@ +using MbUnit.Framework;
+using Notepad.Infrastructure.System;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public class ExitCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_exit_command_specs_ {
+ private MockRepository mockery;
+ private IApplicationEnvironment application;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ application = mockery.DynamicMock<IApplicationEnvironment>();
+ }
+
+ [Test]
+ public void should_ask_the_application_environment_to_shut_down() {
+ using (mockery.Record()) {
+ application.ShutDown();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private IExitCommand CreateSUT() {
+ return new ExitCommand(application);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs new file mode 100644 index 0000000..a78c32b --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs @@ -0,0 +1,18 @@ +using Notepad.Infrastructure.Core;
+using Notepad.Infrastructure.System;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public interface IExitCommand : ICommand {}
+
+ public class ExitCommand : IExitCommand {
+ private readonly IApplicationEnvironment application;
+
+ public ExitCommand(IApplicationEnvironment application) {
+ this.application = application;
+ }
+
+ public void Execute() {
+ application.ShutDown();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs new file mode 100644 index 0000000..ea7c396 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs @@ -0,0 +1,27 @@ +using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Tasks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public interface ISaveCommand : ICommand {}
+
+ public class SaveCommand : ISaveCommand {
+ private readonly IApplicationController controller;
+ private readonly IDocumentTasks tasks;
+
+ public SaveCommand(IApplicationController controller, IDocumentTasks tasks) {
+ this.controller = controller;
+ this.tasks = tasks;
+ }
+
+ public void Execute() {
+ if (!tasks.HasAPathToSaveToBeenSpecified()) {
+ controller.Run<ISaveAsPresenter>();
+ }
+ else {
+ tasks.SaveTheActiveDocument();
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs new file mode 100644 index 0000000..7e4d827 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs @@ -0,0 +1,95 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Tasks;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public class SaveCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_save_command_and_a_file_path_to_save_to_has_not_been_specified_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+
+ SetupResult.For(tasks.HasAPathToSaveToBeenSpecified()).Return(false);
+ }
+
+ [Test]
+ public void should_run_the_save_as_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<ISaveAsPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ [Test]
+ public void should_not_save_the_active_document() {
+ using (mockery.Record()) {
+ tasks.SaveTheActiveDocument();
+ LastCall.Repeat.Never();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private ISaveCommand CreateSUT() {
+ return new SaveCommand(applicationController, tasks);
+ }
+ }
+
+ [TestFixture]
+ public class when_executing_the_save_command_and_a_path_to_save_to_has_already_been_specified_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+
+ SetupResult.For(tasks.HasAPathToSaveToBeenSpecified()).Return(true);
+ }
+
+ [Test]
+ public void should_not_run_the_save_as_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<ISaveAsPresenter>();
+ LastCall.Repeat.Never();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ [Test]
+ public void should_save_the_active_document() {
+ using (mockery.Record()) {
+ tasks.SaveTheActiveDocument();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private ISaveCommand CreateSUT() {
+ return new SaveCommand(applicationController, tasks);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs new file mode 100644 index 0000000..c926dac --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs @@ -0,0 +1,23 @@ +using Notepad.Presentation.Model.Menu.File.Commands;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class ExitMenuItem : IMenuItem {
+ private readonly IExitCommand exitCommand;
+
+ public ExitMenuItem(IExitCommand exitCommand) {
+ this.exitCommand = exitCommand;
+ }
+
+ public string Name() {
+ return "E&xit";
+ }
+
+ public void Click() {
+ exitCommand.Execute();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs new file mode 100644 index 0000000..3220ae5 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs @@ -0,0 +1,72 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class ExitMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_exit_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("E&xit");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_exit_menu_item_ {
+ private MockRepository mockery;
+ private IExitCommand exitCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ exitCommand = mockery.DynamicMock<IExitCommand>();
+ }
+
+ [Test]
+ public void should_execute_the_exit_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(exitCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_exit_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(null);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs b/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs new file mode 100644 index 0000000..98b5a47 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class FileMenu : ISubMenu {
+ private readonly IRepository<IMenuItem> repository;
+ private readonly IMenuItemComparer menuItemComparer;
+
+ public FileMenu(IRepository<IMenuItem> repository, IMenuItemComparer menuItemComparer) {
+ this.repository = repository;
+ this.menuItemComparer = menuItemComparer;
+ }
+
+ public IEnumerable<IMenuItem> AllMenuItems() {
+ return repository
+ .All()
+ .ThatSatisfy(menuItem => menuItem.BelongsTo(this))
+ .SortedUsing(menuItemComparer);
+ }
+
+ public string Name() {
+ return MenuNames.File;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs new file mode 100644 index 0000000..8630ad3 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class FileMenuSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_file_menu_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuNames.File);
+ }
+
+ private ISubMenu CreateSUT() {
+ return new FileMenu(null, null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_file_menu_for_its_menu_items_ {
+ private MockRepository mockery;
+ private IRepository<IMenuItem> repository;
+ private ISubMenu sut;
+ private IMenuItemComparer menuItemComparer;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<IMenuItem>>();
+ menuItemComparer = mockery.DynamicMock<IMenuItemComparer>();
+
+ sut = CreateSUT();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_menu_items() {
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(new List<IMenuItem>())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ [Test]
+ public void should_return_the_menu_items_that_belong_to_the_file_menu() {
+ var saveMenuItem = mockery.DynamicMock<IMenuItem>();
+ var helpMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {saveMenuItem, helpMenuItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(saveMenuItem.BelongsTo(sut)).Return(true);
+ SetupResult.For(helpMenuItem.BelongsTo(sut)).Return(false);
+ }
+
+ using (mockery.Playback()) {
+ var returnedItems = sut.AllMenuItems();
+ returnedItems.ShouldContain(saveMenuItem);
+ returnedItems.ShouldNotContain(helpMenuItem);
+ }
+ }
+
+ [Test]
+ public void should_sort_the_items_in_the_file_menu() {
+ var firstItem = mockery.DynamicMock<IMenuItem>();
+ var secondItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {firstItem, secondItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(firstItem.BelongsTo(null))
+ .IgnoreArguments()
+ .Return(true);
+ SetupResult.For(secondItem.BelongsTo(null))
+ .IgnoreArguments()
+ .Return(true);
+ Expect
+ .Call(menuItemComparer.Compare(firstItem, secondItem))
+ .Return(1)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ private ISubMenu CreateSUT() {
+ return new FileMenu(repository, menuItemComparer);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs new file mode 100644 index 0000000..ab527d8 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs @@ -0,0 +1,17 @@ +using System;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class NewMenuItem : IMenuItem {
+ public void Click() {
+ throw new NotImplementedException();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+
+ public string Name() {
+ return "&New";
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs new file mode 100644 index 0000000..54a7b20 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs @@ -0,0 +1,46 @@ +using MbUnit.Framework;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class NewMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_new_file_menu_if_it_belongs_to_the_file_menu_ {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return("&File");
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new NewMenuItem();
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_new_file_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("&New");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new NewMenuItem();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs new file mode 100644 index 0000000..859ad32 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs @@ -0,0 +1,24 @@ +using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.File;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveAsMenuItem : IMenuItem {
+ private readonly IRunPresenterCommand<ISaveAsPresenter> saveAsCommand;
+
+ public SaveAsMenuItem(IRunPresenterCommand<ISaveAsPresenter> saveAsCommand) {
+ this.saveAsCommand = saveAsCommand;
+ }
+
+ public string Name() {
+ return "Save &As...";
+ }
+
+ public void Click() {
+ saveAsCommand.Execute();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs new file mode 100644 index 0000000..a7c7fd0 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs @@ -0,0 +1,102 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveAsMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("Save &As...");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_save_as_menu_item_ {
+ private MockRepository mockery;
+ private IRunPresenterCommand<ISaveAsPresenter> saveAsCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ saveAsCommand = mockery.DynamicMock<IRunPresenterCommand<ISaveAsPresenter>>();
+ }
+
+ [Test]
+ public void should_execute_the_save_as_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(saveAsCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs new file mode 100644 index 0000000..f99c179 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs @@ -0,0 +1,23 @@ +using Notepad.Presentation.Model.Menu.File.Commands;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveMenuItem : IMenuItem {
+ private readonly ISaveCommand saveCommand;
+
+ public SaveMenuItem(ISaveCommand saveCommand) {
+ this.saveCommand = saveCommand;
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+
+ public void Click() {
+ saveCommand.Execute();
+ }
+
+ public string Name() {
+ return "&Save";
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs new file mode 100644 index 0000000..071adf8 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs @@ -0,0 +1,99 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("&Save");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_save_menu_item_ {
+ private MockRepository mockery;
+ private ISaveCommand saveCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ saveCommand = mockery.DynamicMock<ISaveCommand>();
+ }
+
+ [Test]
+ public void should_execute_the_save_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(saveCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs new file mode 100644 index 0000000..82a2839 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs @@ -0,0 +1,24 @@ +using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.Help;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class AboutMenuItem : IMenuItem {
+ private readonly IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand;
+
+ public AboutMenuItem(IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand) {
+ this.displayAboutCommand = displayAboutCommand;
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.Help);
+ }
+
+ public void Click() {
+ displayAboutCommand.Execute();
+ }
+
+ public string Name() {
+ return MenuItemNames.About;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs new file mode 100644 index 0000000..74d7663 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs @@ -0,0 +1,101 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.Help;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class AboutMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuItemNames.About);
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_about_menu_item_ {
+ private MockRepository mockery;
+ private IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ displayAboutCommand = mockery.DynamicMock<IRunPresenterCommand<IAboutApplicationPresenter>>();
+ }
+
+ [Test]
+ public void should_execute_the_display_about_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(displayAboutCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu menuThatThisItemBelongsTo;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ menuThatThisItemBelongsTo = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(menuThatThisItemBelongsTo.Name()).Return(MenuNames.Help);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(menuThatThisItemBelongsTo).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs b/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs new file mode 100644 index 0000000..ace9696 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class HelpMenu : ISubMenu {
+ private readonly IRepository<IMenuItem> menuItems;
+
+ public HelpMenu(IRepository<IMenuItem> repository) {
+ menuItems = repository;
+ }
+
+ public IEnumerable<IMenuItem> AllMenuItems() {
+ return menuItems.All().ThatSatisfy(m => m.BelongsTo(this));
+ }
+
+ public string Name() {
+ return MenuNames.Help;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs b/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs new file mode 100644 index 0000000..c716376 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Model.Menu.Help;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class HelpMenuSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_help_menu_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuNames.Help);
+ }
+
+ private ISubMenu CreateSUT() {
+ return new HelpMenu(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_help_menu_for_its_menu_items_ {
+ private MockRepository mockery;
+ private IRepository<IMenuItem> repository;
+ private ISubMenu sut;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<IMenuItem>>();
+ sut = CreateSUT();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_menu_items() {
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(new List<IMenuItem>())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ [Test]
+ public void should_return_the_menu_items_that_belong_to_the_help_menu() {
+ var saveMenuItem = mockery.DynamicMock<IMenuItem>();
+ var helpMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {saveMenuItem, helpMenuItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(saveMenuItem.BelongsTo(sut)).Return(false);
+ SetupResult.For(helpMenuItem.BelongsTo(sut)).Return(true);
+ }
+
+ using (mockery.Playback()) {
+ var returnedItems = sut.AllMenuItems();
+ returnedItems.ShouldNotContain(saveMenuItem);
+ returnedItems.ShouldContain(helpMenuItem);
+ }
+ }
+
+ private ISubMenu CreateSUT() {
+ return new HelpMenu(repository);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/IMenuItem.cs b/src/Notepad/Presentation/Model/Menu/IMenuItem.cs new file mode 100644 index 0000000..26483a0 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/IMenuItem.cs @@ -0,0 +1,7 @@ +namespace Notepad.Presentation.Model.Menu {
+ public interface IMenuItem {
+ string Name();
+ void Click();
+ bool BelongsTo(ISubMenu menu);
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs b/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs new file mode 100644 index 0000000..2fd15c7 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface IMenuItemComparer : IComparer<IMenuItem> {}
+
+ public class MenuItemComparer : IMenuItemComparer {
+ private IList<string> rankedMenuItems;
+
+ public MenuItemComparer() {
+ rankedMenuItems = new List<string> {
+ MenuItemNames.New,
+ MenuItemNames.Save,
+ MenuItemNames.SaveAs,
+ MenuItemNames.Exit,
+ MenuItemNames.About
+ };
+ }
+
+ public int Compare(IMenuItem x, IMenuItem y) {
+ return rankedMenuItems.IndexOf(x.Name()).CompareTo(rankedMenuItems.IndexOf(y.Name()));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/ISubMenu.cs b/src/Notepad/Presentation/Model/Menu/ISubMenu.cs new file mode 100644 index 0000000..f06a0b9 --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/ISubMenu.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface ISubMenu {
+ string Name();
+ IEnumerable<IMenuItem> AllMenuItems();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs b/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs new file mode 100644 index 0000000..74f30db --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface ISubMenuItemComparer : IComparer<ISubMenu> {}
+
+ public class SubMenuItemComparer : ISubMenuItemComparer {
+ private List<string> rankings;
+
+ public SubMenuItemComparer() {
+ rankings = new List<string> {
+ MenuNames.File,
+ MenuNames.Help
+ };
+ }
+
+ public int Compare(ISubMenu x, ISubMenu y) {
+ return rankings.IndexOf(x.Name()).CompareTo(rankings.IndexOf(y.Name()));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Model/Menu/MenuNames.cs b/src/Notepad/Presentation/Model/Menu/MenuNames.cs new file mode 100644 index 0000000..f5ef21f --- /dev/null +++ b/src/Notepad/Presentation/Model/Menu/MenuNames.cs @@ -0,0 +1,14 @@ +namespace Notepad.Presentation.Model.Menu {
+ public static class MenuNames {
+ public static readonly string File = "&File";
+ public static readonly string Help = "&Help";
+ }
+
+ public static class MenuItemNames {
+ public static readonly string New = "&New";
+ public static readonly string Save = "&Save";
+ public static readonly string SaveAs = "Save &As...";
+ public static readonly string Exit = "E&xit";
+ public static readonly string About = "&About";
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs b/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs new file mode 100644 index 0000000..9d7458e --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs @@ -0,0 +1,18 @@ +using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Core;
+
+namespace Notepad.Presentation.Presenters.Commands {
+ public interface IRunPresenterCommand<Presenter> : ICommand where Presenter : IPresenter {}
+
+ public class RunPresenterCommand<Presenter> : IRunPresenterCommand<Presenter> where Presenter : IPresenter {
+ private readonly IApplicationController applicationController;
+
+ public RunPresenterCommand(IApplicationController applicationController) {
+ this.applicationController = applicationController;
+ }
+
+ public void Execute() {
+ applicationController.Run<Presenter>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs b/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs new file mode 100644 index 0000000..5ec7002 --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs @@ -0,0 +1,34 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Commands {
+ public class RunPresenterCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_run_presenter_command_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_run_the_display_about_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private IRunPresenterCommand<IPresenter> CreateSUT() {
+ return new RunPresenterCommand<IPresenter>(applicationController);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs new file mode 100644 index 0000000..509d21c --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs @@ -0,0 +1,28 @@ +using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Views.Menu.File;
+using Notepad.Tasks;
+
+namespace Notepad.Presentation.Presenters.Menu.File {
+ public interface ISaveAsPresenter : IPresenter {
+ void SaveToFileAt(string pathToSaveDocumentTo);
+ }
+
+ public class SaveAsPresenter : ISaveAsPresenter {
+ private readonly ISaveAsView view;
+ private readonly IDocumentTasks tasks;
+
+ public SaveAsPresenter(ISaveAsView view, IDocumentTasks tasks) {
+ this.view = view;
+ this.tasks = tasks;
+ }
+
+ public void Initialize() {
+ view.AttachTo(this);
+ }
+
+ public void SaveToFileAt(string pathToSaveDocumentTo) {
+ tasks.SaveActiveDocumentTo(pathToSaveDocumentTo.AsAnAbsoluteFilePath());
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs new file mode 100644 index 0000000..da5032f --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs @@ -0,0 +1,69 @@ +using MbUnit.Framework;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Views.Menu.File;
+using Notepad.Tasks;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu.File {
+ public class SaveAsPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_save_as_presenter_ {
+ private MockRepository mockery;
+ private ISaveAsView view;
+ private ISaveAsPresenter sut;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ view = mockery.DynamicMock<ISaveAsView>();
+
+ sut = CreateSUT();
+ }
+
+ private ISaveAsPresenter CreateSUT() {
+ return new SaveAsPresenter(view, null);
+ }
+
+ [Test]
+ public void should_initialize_the_view_with_itself() {
+ using (mockery.Record()) {
+ view.AttachTo(sut);
+ }
+
+ using (mockery.Playback()) {
+ sut.Initialize();
+ }
+ }
+ }
+
+ [TestFixture]
+ public class when_selecting_a_file_path_to_save_to_ {
+ private MockRepository mockery;
+ private ISaveCommand saveCommand;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+ }
+
+ [Test]
+ public void should_ask_the_active_document_to_save_the_path_specified() {
+ var pathToSaveFileTo = "some path";
+ using (mockery.Record()) {
+ tasks.SaveActiveDocumentTo(pathToSaveFileTo.AsAnAbsoluteFilePath());
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().SaveToFileAt(pathToSaveFileTo);
+ }
+ }
+
+ private ISaveAsPresenter CreateSUT() {
+ return new SaveAsPresenter(null, tasks);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs new file mode 100644 index 0000000..26d7148 --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs @@ -0,0 +1,34 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Views.Menu.Help;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu.Help {
+ public class AboutApplicationPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_application_information_presenter_ {
+ private MockRepository mockery;
+ private IAboutApplicationView view;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ view = mockery.DynamicMock<IAboutApplicationView>();
+ }
+
+ [Test]
+ public void should_display_the_view() {
+ using (mockery.Record()) {
+ view.Display();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IAboutApplicationPresenter CreateSUT() {
+ return new AboutApplicationPresenter(view);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs new file mode 100644 index 0000000..f4c593f --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs @@ -0,0 +1,18 @@ +using Notepad.Presentation.Core;
+using Notepad.Presentation.Views.Menu.Help;
+
+namespace Notepad.Presentation.Presenters.Menu.Help {
+ public interface IAboutApplicationPresenter : IPresenter {}
+
+ public class AboutApplicationPresenter : IAboutApplicationPresenter {
+ private readonly IAboutApplicationView view;
+
+ public AboutApplicationPresenter(IAboutApplicationView view) {
+ this.view = view;
+ }
+
+ public void Initialize() {
+ view.Display();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs new file mode 100644 index 0000000..b40507d --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs @@ -0,0 +1,29 @@ +using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu;
+
+namespace Notepad.Presentation.Presenters.Menu {
+ public interface IMainMenuPresenter : IPresenter {}
+
+ public class MainMenuPresenter : IMainMenuPresenter {
+ private readonly IMainMenuView mainMenu;
+ private readonly IRepository<ISubMenu> repository;
+ private readonly ISubMenuItemComparer comparer;
+
+ public MainMenuPresenter(IMainMenuView mainMenu,
+ IRepository<ISubMenu> repository,
+ ISubMenuItemComparer comparer) {
+ this.mainMenu = mainMenu;
+ this.repository = repository;
+ this.comparer = comparer;
+ }
+
+ public void Initialize() {
+ foreach (var subMenuToAddToMainMenu in repository.All().SortedUsing(comparer)) {
+ mainMenu.Add(subMenuToAddToMainMenu);
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs new file mode 100644 index 0000000..a1b708b --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu {
+ public class MainMenuPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_main_menu_presenter_ {
+ private MockRepository mockery;
+ private IRepository<ISubMenu> repository;
+ private IMainMenuView mainMenu;
+ private ISubMenuItemComparer comparer;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<ISubMenu>>();
+ mainMenu = mockery.DynamicMock<IMainMenuView>();
+ comparer = mockery.DynamicMock<ISubMenuItemComparer>();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_sub_menus() {
+ var subMenus = new List<ISubMenu>();
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(subMenus)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ [Test]
+ public void should_add_each_of_the_sub_menus_to_the_main_menu() {
+ var fileMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenus = new List<ISubMenu> {fileMenu};
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(repository.All())
+ .Return(subMenus);
+ mainMenu.Add(fileMenu);
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ [Test]
+ public void should_sort_the_sub_menus_using_the_comparer() {
+ var firstMenu = mockery.DynamicMock<ISubMenu>();
+ var secondMenu = mockery.DynamicMock<ISubMenu>();
+
+ var subMenus = new List<ISubMenu> {firstMenu, secondMenu};
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(repository.All())
+ .Return(subMenus);
+ Expect
+ .Call(comparer.Compare(firstMenu, secondMenu))
+ .Return(0)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IMainMenuPresenter CreateSUT() {
+ return new MainMenuPresenter(mainMenu, repository, comparer);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs b/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs new file mode 100644 index 0000000..c0ed3e9 --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs @@ -0,0 +1,5 @@ +using Notepad.Presentation.Core;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public interface IMainShellPresenter : IPresenter {}
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs new file mode 100644 index 0000000..1406be0 --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs @@ -0,0 +1,17 @@ +using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu;
+using Notepad.Presentation.Presenters.Shell;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public class MainShellPresenter : IMainShellPresenter {
+ private readonly IApplicationController applicationController;
+
+ public MainShellPresenter(IApplicationController applicationController) {
+ this.applicationController = applicationController;
+ }
+
+ public void Initialize() {
+ applicationController.Run<IMainMenuPresenter>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs new file mode 100644 index 0000000..681ce58 --- /dev/null +++ b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs @@ -0,0 +1,36 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu;
+using Notepad.Presentation.Presenters.Shell;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public class MainShellPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_main_shell_presenter_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_initialize_the_main_menu_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IMainMenuPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IMainShellPresenter CreateSUT() {
+ return new MainShellPresenter(applicationController);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs b/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs new file mode 100644 index 0000000..e66de04 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs @@ -0,0 +1,17 @@ +using System.Windows.Forms;
+using Notepad.Presentation.Presenters.Menu.File;
+
+namespace Notepad.Presentation.Views.Menu.File {
+ public interface ISaveAsView {
+ void AttachTo(ISaveAsPresenter presenterToDelegateWorkToo);
+ }
+
+ public class SaveAsView : ISaveAsView {
+ public void AttachTo(ISaveAsPresenter presenterToDelegateWorkToo) {
+ var saveFileDialog = new SaveFileDialog();
+ if (saveFileDialog.ShowDialog() == DialogResult.OK) {
+ presenterToDelegateWorkToo.SaveToFileAt(saveFileDialog.FileName);
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs new file mode 100644 index 0000000..6059d18 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs @@ -0,0 +1,184 @@ +namespace Notepad.Presentation.Views.Menu.Help
+{
+ public partial class AboutApplicationView
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
+ this.logoPictureBox = new System.Windows.Forms.PictureBox();
+ this.labelProductName = new System.Windows.Forms.Label();
+ this.labelVersion = new System.Windows.Forms.Label();
+ this.uxCopyright = new System.Windows.Forms.Label();
+ this.uxCompanyName = new System.Windows.Forms.Label();
+ this.uxDescription = new System.Windows.Forms.TextBox();
+ this.okButton = new System.Windows.Forms.Button();
+ this.tableLayoutPanel.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
+ this.SuspendLayout();
+ //
+ // tableLayoutPanel
+ //
+ this.tableLayoutPanel.ColumnCount = 2;
+ this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F));
+ this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F));
+ this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0);
+ this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 0);
+ this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1);
+ this.tableLayoutPanel.Controls.Add(this.uxCopyright, 1, 2);
+ this.tableLayoutPanel.Controls.Add(this.uxCompanyName, 1, 3);
+ this.tableLayoutPanel.Controls.Add(this.uxDescription, 1, 4);
+ this.tableLayoutPanel.Controls.Add(this.okButton, 1, 5);
+ this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tableLayoutPanel.Location = new System.Drawing.Point(9, 9);
+ this.tableLayoutPanel.Name = "tableLayoutPanel";
+ this.tableLayoutPanel.RowCount = 6;
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.Size = new System.Drawing.Size(417, 265);
+ this.tableLayoutPanel.TabIndex = 0;
+ //
+ // logoPictureBox
+ //
+ this.logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.logoPictureBox.Location = new System.Drawing.Point(3, 3);
+ this.logoPictureBox.Name = "logoPictureBox";
+ this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 6);
+ this.logoPictureBox.Size = new System.Drawing.Size(131, 259);
+ this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+ this.logoPictureBox.TabIndex = 12;
+ this.logoPictureBox.TabStop = false;
+ //
+ // labelProductName
+ //
+ this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.labelProductName.Location = new System.Drawing.Point(143, 0);
+ this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17);
+ this.labelProductName.Name = "labelProductName";
+ this.labelProductName.Size = new System.Drawing.Size(271, 17);
+ this.labelProductName.TabIndex = 19;
+ this.labelProductName.Text = "Product Name";
+ this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // labelVersion
+ //
+ this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.labelVersion.Location = new System.Drawing.Point(143, 26);
+ this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17);
+ this.labelVersion.Name = "labelVersion";
+ this.labelVersion.Size = new System.Drawing.Size(271, 17);
+ this.labelVersion.TabIndex = 0;
+ this.labelVersion.Text = "Version";
+ this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxCopyright
+ //
+ this.uxCopyright.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxCopyright.Location = new System.Drawing.Point(143, 52);
+ this.uxCopyright.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.uxCopyright.MaximumSize = new System.Drawing.Size(0, 17);
+ this.uxCopyright.Name = "uxCopyright";
+ this.uxCopyright.Size = new System.Drawing.Size(271, 17);
+ this.uxCopyright.TabIndex = 21;
+ this.uxCopyright.Text = "Copyright";
+ this.uxCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxCompanyName
+ //
+ this.uxCompanyName.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxCompanyName.Location = new System.Drawing.Point(143, 78);
+ this.uxCompanyName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.uxCompanyName.MaximumSize = new System.Drawing.Size(0, 17);
+ this.uxCompanyName.Name = "uxCompanyName";
+ this.uxCompanyName.Size = new System.Drawing.Size(271, 17);
+ this.uxCompanyName.TabIndex = 22;
+ this.uxCompanyName.Text = "Company Name";
+ this.uxCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxDescription
+ //
+ this.uxDescription.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxDescription.Location = new System.Drawing.Point(143, 107);
+ this.uxDescription.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
+ this.uxDescription.Multiline = true;
+ this.uxDescription.Name = "uxDescription";
+ this.uxDescription.ReadOnly = true;
+ this.uxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.uxDescription.Size = new System.Drawing.Size(271, 126);
+ this.uxDescription.TabIndex = 23;
+ this.uxDescription.TabStop = false;
+ this.uxDescription.Text = "Description";
+ //
+ // okButton
+ //
+ this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.okButton.Location = new System.Drawing.Point(339, 239);
+ this.okButton.Name = "okButton";
+ this.okButton.Size = new System.Drawing.Size(75, 23);
+ this.okButton.TabIndex = 24;
+ this.okButton.Text = "&OK";
+ //
+ // AboutApplicationView
+ //
+ this.AcceptButton = this.okButton;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(435, 283);
+ this.Controls.Add(this.tableLayoutPanel);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "AboutApplicationView";
+ this.Padding = new System.Windows.Forms.Padding(9);
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "About Notepad";
+ this.tableLayoutPanel.ResumeLayout(false);
+ this.tableLayoutPanel.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel;
+ private System.Windows.Forms.PictureBox logoPictureBox;
+ private System.Windows.Forms.Label labelProductName;
+ private System.Windows.Forms.Label labelVersion;
+ private System.Windows.Forms.Label uxCopyright;
+ private System.Windows.Forms.Label uxCompanyName;
+ private System.Windows.Forms.TextBox uxDescription;
+ private System.Windows.Forms.Button okButton;
+ }
+}
diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs new file mode 100644 index 0000000..515a493 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs @@ -0,0 +1,99 @@ +using System.IO;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Notepad.Presentation.Views.Menu.Help {
+ public partial class AboutApplicationView : Form, IAboutApplicationView {
+ public AboutApplicationView() {
+ InitializeComponent();
+ labelProductName.Text = AssemblyProduct;
+ labelVersion.Text = string.Format("Version {0} {0}", AssemblyVersion);
+ uxCopyright.Text = AssemblyCopyright;
+ uxCompanyName.Text = AssemblyCompany;
+ uxDescription.Text = AssemblyDescription;
+ }
+
+ public void Display() {
+ ShowDialog();
+ }
+
+ public string AssemblyTitle
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
+ if (attributes.Length > 0)
+ {
+ AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
+ if (titleAttribute.Title != "")
+ {
+ return titleAttribute.Title;
+ }
+ }
+ return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
+ }
+ }
+
+ public string AssemblyVersion
+ {
+ get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
+ }
+
+ public string AssemblyDescription
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyDescriptionAttribute)attributes[0]).Description;
+ }
+ }
+
+ public string AssemblyProduct
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyProductAttribute)attributes[0]).Product;
+ }
+ }
+
+ public string AssemblyCopyright
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
+ }
+ }
+
+ public string AssemblyCompany
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCompanyAttribute)attributes[0]).Company;
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx @@ -0,0 +1,120 @@ +<?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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root>
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs b/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs new file mode 100644 index 0000000..6726cba --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs @@ -0,0 +1,5 @@ +namespace Notepad.Presentation.Views.Menu.Help {
+ public interface IAboutApplicationView {
+ void Display();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs b/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs new file mode 100644 index 0000000..6b6c717 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs @@ -0,0 +1,23 @@ +using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Presentation.Views.Shell;
+
+namespace Notepad.Presentation.Views.Menu {
+ public interface IMainMenuView {
+ void Add(ISubMenu menuToAddToTheMainMenu);
+ }
+
+ public class MainMenuView : IMainMenuView {
+ private readonly ISubMenuToToolStripMenuItemMapper mapper;
+ private readonly WindowShell mainShell;
+
+ public MainMenuView(WindowShell mainShell, ISubMenuToToolStripMenuItemMapper mapper) {
+ this.mapper = mapper;
+ this.mainShell = mainShell;
+ }
+
+ public void Add(ISubMenu menuToAddToTheMainMenu) {
+ mainShell.MenuStrip().Items.Add(mapper.MapFrom(menuToAddToTheMainMenu));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs b/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs new file mode 100644 index 0000000..f4d5a57 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs @@ -0,0 +1,69 @@ +using System.Windows.Forms;
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Presentation.Views.Shell;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu {
+ public class MainMenuViewSpecs {}
+
+ [TestFixture]
+ public class when_adding_sub_menus_to_the_main_menu_ {
+ private MockRepository mockery;
+ private ISubMenuToToolStripMenuItemMapper mapper;
+ private MenuStrip mainMenuStrip;
+ private WindowShell mainShell;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ mapper = mockery.DynamicMock<ISubMenuToToolStripMenuItemMapper>();
+ mainShell = mockery.DynamicMock<WindowShell>();
+ mainMenuStrip = new MenuStrip();
+
+ SetupResult.For(mainShell.MenuStrip()).Return(mainMenuStrip);
+ }
+
+ [Test]
+ public void should_leverage_the_mapper_to_map_the_sub_menu_to_a_tool_strip_menu_item() {
+ var subMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenuToolStripMenuItem = new ToolStripMenuItem();
+
+ using (mockery.Record()) {
+ Expect
+ .Call(mapper.MapFrom(subMenu))
+ .Return(subMenuToolStripMenuItem)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Add(subMenu);
+ }
+ }
+
+ [Test]
+ public void should_add_the_mapped_menu_strip_item_to_the_main_menu_strip() {
+ var subMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenuToolStripMenuItem = new ToolStripMenuItem();
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(mapper.MapFrom(subMenu))
+ .Return(subMenuToolStripMenuItem);
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Add(subMenu);
+ mainMenuStrip.Items.Contains(subMenuToolStripMenuItem)
+ .ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMainMenuView CreateSUT() {
+ return new MainMenuView(mainShell, mapper);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs new file mode 100644 index 0000000..a8214a8 --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs @@ -0,0 +1,15 @@ +using System.Windows.Forms;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public interface IMenuItemToToolStripMenuItemMapper : IMapper<IMenuItem, ToolStripMenuItem> {}
+
+ public class MenuItemToToolStripMenuItemMapper : IMenuItemToToolStripMenuItemMapper {
+ public ToolStripMenuItem MapFrom(IMenuItem item) {
+ var toolStripMenuItem = new ToolStripMenuItem(item.Name());
+ toolStripMenuItem.Click += delegate { item.Click(); };
+ return toolStripMenuItem;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs new file mode 100644 index 0000000..8c00cee --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs @@ -0,0 +1,10 @@ +using System.Windows.Forms;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public interface ISubMenuToToolStripMenuItemMapper : IMapper<ISubMenu, ToolStripMenuItem>
+ {
+
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs b/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs new file mode 100644 index 0000000..8d8b29c --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs @@ -0,0 +1,47 @@ +using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class MenuItemToToolStripMenuItemMapperSpecs {}
+
+ [TestFixture]
+ public class when_mapping_a_menu_item_to_a_tool_strip_menu_item_ {
+ private MockRepository mockery;
+ private IMenuItem menuItem;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ menuItem = mockery.DynamicMock<IMenuItem>();
+
+ SetupResult.For(menuItem.Name()).Return("&Save");
+ }
+
+ [Test]
+ public void should_return_a_tool_strip_menu_item_with_the_menu_items_name_applied_as_its_text() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(menuItem).Text.ShouldBeEqualTo("&Save");
+ }
+ }
+
+ [Test]
+ public void should_invoke_the_menu_items_click_method_when_the_tool_strip_menu_item_is_clicked() {
+ using (mockery.Record()) {
+ menuItem.Click();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(menuItem).PerformClick();
+ }
+ }
+
+ private IMenuItemToToolStripMenuItemMapper CreateSUT() {
+ return new MenuItemToToolStripMenuItemMapper();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs new file mode 100644 index 0000000..74efbbd --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs @@ -0,0 +1,21 @@ +using System.Windows.Forms;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class SubMenuToToolStripMenuItemMapper : ISubMenuToToolStripMenuItemMapper {
+ private readonly IMenuItemToToolStripMenuItemMapper mapper;
+
+ public SubMenuToToolStripMenuItemMapper(IMenuItemToToolStripMenuItemMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ public ToolStripMenuItem MapFrom(ISubMenu item) {
+ var toolStripMenuItem = new ToolStripMenuItem(item.Name());
+ foreach (var menuItem in item.AllMenuItems()) {
+ toolStripMenuItem.DropDownItems.Add(mapper.MapFrom(menuItem));
+ }
+ return toolStripMenuItem;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs new file mode 100644 index 0000000..d26391c --- /dev/null +++ b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic;
+using System.Windows.Forms;
+using MbUnit.Framework;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class SubMenuToToolStripMenuItemMapperSpecs {}
+
+ [TestFixture]
+ public class when_mapping_a_sub_menu_to_a_tool_strip_menu_item_ {
+ private MockRepository mockery;
+ private ISubMenu subMenu;
+ private IList<IMenuItem> menuItems;
+ private IMenuItemToToolStripMenuItemMapper mapper;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ subMenu = mockery.DynamicMock<ISubMenu>();
+ mapper = mockery.DynamicMock<IMenuItemToToolStripMenuItemMapper>();
+ menuItems = new List<IMenuItem>();
+
+ SetupResult.For(subMenu.Name()).Return("&File");
+ SetupResult.For(subMenu.AllMenuItems()).Return(menuItems);
+ }
+
+ [Test]
+ public void should_return_a_non_null_value() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).ShouldNotBeNull();
+ }
+ }
+
+ [Test]
+ public void should_return_a_menu_item_with_the_sub_menus_name_applied_as_its_text() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).Text.ShouldBeEqualTo("&File");
+ }
+ }
+
+ [Test]
+ public void should_map_each_of_the_sub_menus_menu_items_to_tool_strip_menu_items() {
+ var firstMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ menuItems.Add(firstMenuItem);
+ using (mockery.Record()) {
+ Expect
+ .Call(mapper.MapFrom(firstMenuItem))
+ .Return(new ToolStripMenuItem())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu);
+ }
+ }
+
+ [Test]
+ public void should_add_all_the_mapped_menu_items_to_the_menu_item_representing_the_sub_menu() {
+ var firstMenuItem = mockery.DynamicMock<IMenuItem>();
+ var mappedMenuItem = new ToolStripMenuItem();
+
+ menuItems.Add(firstMenuItem);
+ using (mockery.Record()) {
+ SetupResult
+ .For(mapper.MapFrom(firstMenuItem))
+ .Return(mappedMenuItem)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).DropDownItems.Contains(mappedMenuItem)
+ .ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMapper<ISubMenu, ToolStripMenuItem> CreateSUT() {
+ return new SubMenuToToolStripMenuItemMapper(mapper);
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs b/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs new file mode 100644 index 0000000..32282d8 --- /dev/null +++ b/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs @@ -0,0 +1,82 @@ +namespace Notepad.Presentation.Views.Shell {
+ partial class WindowShell
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.uxMainMenuStrip = new System.Windows.Forms.MenuStrip();
+ this.uxEditorTextBox = new System.Windows.Forms.RichTextBox();
+ this.uxStatusBar = new System.Windows.Forms.StatusStrip();
+ this.SuspendLayout();
+ //
+ // uxMainMenuStrip
+ //
+ this.uxMainMenuStrip.Location = new System.Drawing.Point(0, 0);
+ this.uxMainMenuStrip.Name = "uxMainMenuStrip";
+ this.uxMainMenuStrip.Size = new System.Drawing.Size(292, 24);
+ this.uxMainMenuStrip.TabIndex = 0;
+ this.uxMainMenuStrip.Text = "menuStrip1";
+ //
+ // uxEditorTextBox
+ //
+ this.uxEditorTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxEditorTextBox.Location = new System.Drawing.Point(0, 24);
+ this.uxEditorTextBox.Name = "uxEditorTextBox";
+ this.uxEditorTextBox.Size = new System.Drawing.Size(292, 249);
+ this.uxEditorTextBox.TabIndex = 1;
+ this.uxEditorTextBox.Text = "";
+ //
+ // uxStatusBar
+ //
+ this.uxStatusBar.Location = new System.Drawing.Point(0, 251);
+ this.uxStatusBar.Name = "uxStatusBar";
+ this.uxStatusBar.Size = new System.Drawing.Size(292, 22);
+ this.uxStatusBar.TabIndex = 2;
+ this.uxStatusBar.Text = "statusStrip1";
+ //
+ // MainShellView
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(292, 273);
+ this.Controls.Add(this.uxStatusBar);
+ this.Controls.Add(this.uxEditorTextBox);
+ this.Controls.Add(this.uxMainMenuStrip);
+ this.MainMenuStrip = this.uxMainMenuStrip;
+ this.Name = "MainShellView";
+ this.Text = "Notepad";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip uxMainMenuStrip;
+ private System.Windows.Forms.RichTextBox uxEditorTextBox;
+ private System.Windows.Forms.StatusStrip uxStatusBar;
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.cs b/src/Notepad/Presentation/Views/Shell/WindowShell.cs new file mode 100644 index 0000000..e90b11d --- /dev/null +++ b/src/Notepad/Presentation/Views/Shell/WindowShell.cs @@ -0,0 +1,13 @@ +using System.Windows.Forms;
+
+namespace Notepad.Presentation.Views.Shell {
+ public partial class WindowShell : Form {
+ public WindowShell() {
+ InitializeComponent();
+ }
+
+ public MenuStrip MenuStrip() {
+ return uxMainMenuStrip;
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.resx b/src/Notepad/Presentation/Views/Shell/WindowShell.resx new file mode 100644 index 0000000..c61a0c7 --- /dev/null +++ b/src/Notepad/Presentation/Views/Shell/WindowShell.resx @@ -0,0 +1,126 @@ +<?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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <metadata name="uxMainMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>17, 17</value>
+ </metadata>
+ <metadata name="uxStatusBar.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>126, 17</value>
+ </metadata>
+</root>
\ No newline at end of file diff --git a/src/Notepad/Program.cs b/src/Notepad/Program.cs new file mode 100644 index 0000000..8cb5ba9 --- /dev/null +++ b/src/Notepad/Program.cs @@ -0,0 +1,23 @@ +using System;
+using System.Windows.Forms;
+using Notepad.Infrastructure.Container;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Context;
+
+namespace Notepad {
+ internal static class Program {
+ [STAThread]
+ private static void Main() {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.ThreadException += (sender, e) => e.Exception.LogError();
+ try {
+ Start.TheApplication();
+ Application.Run(Resolve.DependencyFor<NotepadApplicationContext>());
+ }
+ catch (Exception e1) {
+ e1.LogError();
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Properties/Resources.Designer.cs b/src/Notepad/Properties/Resources.Designer.cs new file mode 100644 index 0000000..37c6928 --- /dev/null +++ b/src/Notepad/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1433
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Notepad.Properties
+{
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Notepad.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/src/Notepad/Properties/Resources.resx b/src/Notepad/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/src/Notepad/Properties/Resources.resx @@ -0,0 +1,117 @@ +<?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.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: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" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ </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" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ </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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root>
\ No newline at end of file diff --git a/src/Notepad/Properties/Settings.Designer.cs b/src/Notepad/Properties/Settings.Designer.cs new file mode 100644 index 0000000..2f10c11 --- /dev/null +++ b/src/Notepad/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1433
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Notepad.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/src/Notepad/Properties/Settings.settings b/src/Notepad/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/src/Notepad/Properties/Settings.settings @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+ <Profiles>
+ <Profile Name="(Default)" />
+ </Profiles>
+ <Settings />
+</SettingsFile>
diff --git a/src/Notepad/Start.cs b/src/Notepad/Start.cs new file mode 100644 index 0000000..1cb4d42 --- /dev/null +++ b/src/Notepad/Start.cs @@ -0,0 +1,18 @@ +using Notepad.Infrastructure.Container;
+using Notepad.Infrastructure.Container.Windsor;
+using Notepad.Presentation.Context;
+using Notepad.Presentation.Views.Menu.Help;
+using Notepad.Presentation.Views.Shell;
+
+namespace Notepad {
+ public class Start {
+ public static void TheApplication() {
+ var resolver = new WindsorDependencyRegistry();
+ Resolve.InitializeWith(resolver);
+
+ resolver.Register<WindowShell, WindowShell>();
+ resolver.Register<NotepadApplicationContext, NotepadApplicationContext>();
+ resolver.Register<IAboutApplicationView, AboutApplicationView>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Tasks/IDocumentTasks.cs b/src/Notepad/Tasks/IDocumentTasks.cs new file mode 100644 index 0000000..10c88e6 --- /dev/null +++ b/src/Notepad/Tasks/IDocumentTasks.cs @@ -0,0 +1,9 @@ +using Notepad.Domain.FileSystem;
+
+namespace Notepad.Tasks {
+ public interface IDocumentTasks {
+ void SaveActiveDocumentTo(IFilePath pathToSaveTheActiveDocumentTo);
+ bool HasAPathToSaveToBeenSpecified();
+ void SaveTheActiveDocument();
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Tasks/Stubs/StubDocumentTasks.cs b/src/Notepad/Tasks/Stubs/StubDocumentTasks.cs new file mode 100644 index 0000000..cc08542 --- /dev/null +++ b/src/Notepad/Tasks/Stubs/StubDocumentTasks.cs @@ -0,0 +1,19 @@ +using Notepad.Domain.FileSystem;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Tasks.Stubs {
+ public class StubDocumentTasks : IDocumentTasks {
+ public void SaveActiveDocumentTo(IFilePath pathToSaveTheActiveDocumentTo) {
+ pathToSaveTheActiveDocumentTo
+ .LogInformational("Save document to {0}", pathToSaveTheActiveDocumentTo.RawPathToFile());
+ }
+
+ public bool HasAPathToSaveToBeenSpecified() {
+ return false;
+ }
+
+ public void SaveTheActiveDocument() {
+ this.LogInformational("Save the active document please...");
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Test/Call.cs b/src/Notepad/Test/Call.cs new file mode 100644 index 0000000..67ab49c --- /dev/null +++ b/src/Notepad/Test/Call.cs @@ -0,0 +1,37 @@ +using System;
+using System.Linq.Expressions;
+
+namespace Notepad.Test {
+ public class Call {
+ public static IActionRecorder To(Expression<Action> actionToRecord) {
+ return new ActionRecorder(actionToRecord.Compile());
+ }
+ }
+
+ public class ActionRecorder : IActionRecorder {
+ private readonly Action actionToRecord;
+
+ public ActionRecorder(Action actionToRecord) {
+ this.actionToRecord = actionToRecord;
+ }
+
+ public Action ActionToRecord() {
+ return actionToRecord;
+ }
+ }
+
+ public interface IActionRecorder {
+ Action ActionToRecord();
+ }
+
+ public static class ActionRecorderExtensions {
+ public static void ShouldThrow<ThisException>(this IActionRecorder recorder) where ThisException : Exception {
+ try {
+ recorder.ActionToRecord()();
+ }
+ catch (ThisException) {
+ return;
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/src/Notepad/Test/Extensions/AssertionExtensions.cs b/src/Notepad/Test/Extensions/AssertionExtensions.cs new file mode 100644 index 0000000..980d325 --- /dev/null +++ b/src/Notepad/Test/Extensions/AssertionExtensions.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic;
+using System.Linq;
+using MbUnit.Framework;
+
+namespace Notepad.Test.Extensions {
+ public static class AssertionExtensions {
+ public static void ShouldBeEqualTo<T>(this T itemToValidate, T expectedValue) {
+ Assert.AreEqual(expectedValue, itemToValidate);
+ }
+
+ public static void ShouldBeSameInstanceAs<T>(this T left, T right) {
+ Assert.IsTrue(ReferenceEquals(left, right));
+ }
+
+ public static void ShouldNotBeNull<T>(this T itemToCheck) where T : class {
+ Assert.IsNotNull(itemToCheck);
+ }
+
+ public static void ShouldBeGreaterThan(this int actual, int expected) {
+ Assert.GreaterThan(actual, expected);
+ }
+
+ public static void ShouldBeLessThan(this int actual, int expected) {
+ Assert.Less(actual, expected);
+ }
+
+ public static void ShouldContain<T>(this IEnumerable<T> itemsToPeekInto, T itemToLookFor) {
+ Assert.IsTrue(itemsToPeekInto.Contains(itemToLookFor));
+ }
+
+ public static void ShouldNotContain<T>(this IEnumerable<T> itemsToPeekInto, T itemToLookFor) {
+ Assert.IsFalse(itemsToPeekInto.Contains(itemToLookFor));
+ }
+ }
+}
\ No newline at end of file |
