From ab0da85c4b53cfecb29fd52e87520f12426109bd Mon Sep 17 00:00:00 2001 From: MrLuje Date: Mon, 11 May 2020 16:57:59 +0200 Subject: [PATCH] Fix init config (#32) * fix(config): error if user doesn't want to init config * ensure same config after init * test: ensure ioc is reset before all tests --- .../OpenGenerateLocalConfigViewModelTests.cs | 136 ++++++++++++++++++ .../vs-commitizen.Tests.csproj | 4 + .../Commands/OpenGenerateLocalConfig.cs | 36 +---- .../Infrastructure/ConfigFileProvider.cs | 4 +- .../OpenGenerateLocalConfigViewModel.cs | 58 ++++++++ vs-commitizen/defaultConfigFile.json | 2 +- vs-commitizen/vs-commitizen.csproj | 1 + 7 files changed, 208 insertions(+), 33 deletions(-) create mode 100644 vs-commitizen.Tests/OpenGenerateLocalConfigViewModelTests.cs create mode 100644 vs-commitizen/ViewModels/OpenGenerateLocalConfigViewModel.cs diff --git a/vs-commitizen.Tests/OpenGenerateLocalConfigViewModelTests.cs b/vs-commitizen.Tests/OpenGenerateLocalConfigViewModelTests.cs new file mode 100644 index 0000000..36cf7eb --- /dev/null +++ b/vs-commitizen.Tests/OpenGenerateLocalConfigViewModelTests.cs @@ -0,0 +1,136 @@ +using AutoFixture; +using AutoFixture.AutoNSubstitute; +using AutoFixture.Xunit2; +using NSubstitute; +using NSubstitute.Extensions; +using Shouldly; +using System.Threading.Tasks; +using vs_commitizen.Infrastructure; +using vs_commitizen.Settings; +using vs_commitizen.Tests.TestAttributes; +using vs_commitizen.ViewModels; +using Xunit; + +namespace vs_commitizen.Tests +{ + public class OpenGenerateLocalConfigViewModelTests + { + OpenGenerateLocalConfigViewModel getSut(Fixture fixture, ConfigFileProvider configFileProvider, IFileAccessor fileAccessor, string configPath, bool fileExists) + { + configFileProvider.Configure().TryGetLocalConfigAsync().Returns(configPath); + fileAccessor.Exists(Arg.Any()).ReturnsForAnyArgs(fileExists); + + IoC.Container.EjectAllInstancesOf(); + IoC.Container.Inject(configFileProvider); + + IoC.Container.EjectAllInstancesOf(); + IoC.Container.Inject(fileAccessor); + + return fixture.Create(); + } + + [Theory] + [InlineTestConventions(true, "path to config")] + [InlineTestConventions(false, null)] + public async Task Command_Is_Enabled_Based_On_Solution_Loaded( + bool solutionLoaded, + string configPath, + [Frozen]IFileAccessor fileAccessor, + [Frozen][Substitute] ConfigFileProvider configFileProvider, + Fixture fixture + ) + { + var sut = getSut(fixture, configFileProvider, fileAccessor, configPath, solutionLoaded); + + var expectedEnabledState = solutionLoaded; + (await sut.IsCommandEnabledAsync()).ShouldBe(expectedEnabledState); + } + + [Theory] + [InlineTestConventions(true, "path to config")] + public async Task Execute_With_Existing_Config_Opens_The_File( + bool solutionLoaded, + string configPath, + [Frozen]IFileAccessor fileAccessor, + [Frozen][Substitute] IPopupManager popupManager, + [Frozen][Substitute] ConfigFileProvider configFileProvider, + Fixture fixture + ) + { + // Arrange + var sut = getSut(fixture, configFileProvider, fileAccessor, configPath, solutionLoaded); + var called = false; + + // Act + await sut.ExecuteAsync(s => + { + called = true; + return Task.CompletedTask; + }); + + // Assert + called.ShouldBeTrue(); + popupManager.DidNotReceiveWithAnyArgs().Confirm(Arg.Any(), Arg.Any()); + } + + [Theory] + [InlineTestConventions(false, "path to config")] + public async Task Execute_With_NonExisting_Config_Asks_To_Create_It( + bool solutionLoaded, + string configPath, + [Frozen]IFileAccessor fileAccessor, + [Frozen][Substitute] IPopupManager popupManager, + [Frozen][Substitute] ConfigFileProvider configFileProvider, + Fixture fixture + ) + { + // Arrange + IoC.Container.Inject(popupManager); + var sut = getSut(fixture, configFileProvider, fileAccessor, configPath, solutionLoaded); + var called = false; + + // Act + await sut.ExecuteAsync(s => + { + called = true; + return Task.CompletedTask; + }); + + // Assert + called.ShouldBeFalse(); + popupManager.Received().Confirm(Arg.Any(), Arg.Any()); + } + + [Theory] + [InlineTestConventions(false, "path to config", true)] + [InlineTestConventions(false, "path to config", false)] + public async Task Response_To_Popup_Should_Open_File( + bool solutionLoaded, + string configPath, + bool userWantsToCreateFile, + [Frozen]IFileAccessor fileAccessor, + [Frozen][Substitute] IPopupManager popupManager, + [Frozen][Substitute] ConfigFileProvider configFileProvider, + Fixture fixture + ) + { + // Arrange + popupManager.Configure().Confirm(Arg.Any(), Arg.Any()).Returns(userWantsToCreateFile); + IoC.Container.Inject(popupManager); + var sut = getSut(fixture, configFileProvider, fileAccessor, configPath, solutionLoaded); + var called = false; + + // Act + await sut.ExecuteAsync(s => + { + called = true; + return Task.CompletedTask; + }); + + // Assert + var expectedResult = userWantsToCreateFile; + called.ShouldBe(expectedResult); + popupManager.Received().Confirm(Arg.Any(), Arg.Any()); + } + } +} \ No newline at end of file diff --git a/vs-commitizen.Tests/vs-commitizen.Tests.csproj b/vs-commitizen.Tests/vs-commitizen.Tests.csproj index 6f05dc6..b3840eb 100644 --- a/vs-commitizen.Tests/vs-commitizen.Tests.csproj +++ b/vs-commitizen.Tests/vs-commitizen.Tests.csproj @@ -56,6 +56,9 @@ ..\lib\vs2019\Microsoft.TeamFoundation.Controls.dll + + ..\lib\vs2019\Microsoft.TeamFoundation.Git.Provider.dll + @@ -73,6 +76,7 @@ + diff --git a/vs-commitizen/Commands/OpenGenerateLocalConfig.cs b/vs-commitizen/Commands/OpenGenerateLocalConfig.cs index d13c086..14f51c5 100644 --- a/vs-commitizen/Commands/OpenGenerateLocalConfig.cs +++ b/vs-commitizen/Commands/OpenGenerateLocalConfig.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.Shell.Interop; using vs_commitizen.Infrastructure; using vs_commitizen.Settings; +using vs_commitizen.ViewModels; using Task = System.Threading.Tasks.Task; namespace vs_commitizen.Commands @@ -30,6 +31,7 @@ internal sealed class OpenGenerateLocalConfig /// VS Package that provides this command, not null. /// private readonly AsyncPackage package; + private OpenGenerateLocalConfigViewModel viewModel; /// /// Initializes a new instance of the class. @@ -41,6 +43,7 @@ private OpenGenerateLocalConfig(AsyncPackage package, OleMenuCommandService comm { this.package = package ?? throw new ArgumentNullException(nameof(package)); commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); + viewModel = new OpenGenerateLocalConfigViewModel(); var menuCommandID = new CommandID(CommandSet, CommandId); var menuItem = new OleMenuCommand(Execute, menuCommandID); @@ -54,7 +57,7 @@ private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) } - private static async Task HandleIsCommandVisibleAsync(object sender) + private async Task HandleIsCommandVisibleAsync(object sender) { try { @@ -63,11 +66,7 @@ private static async Task HandleIsCommandVisibleAsync(object sender) switch (menuCommand.CommandID.ID) { case (int)PackageIds.OpenGenerateLocalConfigCmd: - var configFileProvider = IoC.GetInstance(); - var fileAccessor = IoC.GetInstance(); - - var localConfigPath = await configFileProvider.TryGetLocalConfigAsync(); - menuCommand.Enabled = !string.IsNullOrWhiteSpace(localConfigPath); + menuCommand.Enabled = await this.viewModel.IsCommandEnabledAsync(); break; } } @@ -124,30 +123,7 @@ private void Execute(object sender, EventArgs e) private async Task ProcessAsync() { - var popupManager = IoC.GetInstance(); - - try - { - var configFileProvider = IoC.GetInstance(); - var fileAccessor = IoC.GetInstance(); - - var localConfigPath = await configFileProvider.TryGetLocalConfigAsync(); - if (string.IsNullOrWhiteSpace(localConfigPath)) return; - - if (!fileAccessor.Exists(localConfigPath)) - { - if (popupManager.Confirm("There is no local configuration file yet, do you want to create it for this repository ?", "Init local config file")) - { - fileAccessor.CopyFile(ConfigFileProvider.ConfigPathUserProfile, localConfigPath); - } - } - - await OpenFileInEditorAsync(localConfigPath); - } - catch (Exception ex) - { - popupManager.Show(ex.ToString(), "An error occured"); - } + await this.viewModel.ExecuteAsync(OpenFileInEditorAsync); } private async Task OpenFileInEditorAsync(string localConfigPath) diff --git a/vs-commitizen/Infrastructure/ConfigFileProvider.cs b/vs-commitizen/Infrastructure/ConfigFileProvider.cs index 427d90f..8e8f6ee 100644 --- a/vs-commitizen/Infrastructure/ConfigFileProvider.cs +++ b/vs-commitizen/Infrastructure/ConfigFileProvider.cs @@ -107,7 +107,7 @@ private async Task GetCacheKeyAsync() return (repository != null, repository?.RepositoryPath); } - public async Task TryGetLocalConfigAsync() + public virtual async Task TryGetLocalConfigAsync() { var (isRepositoryLoaded, repositoryPath) = await GetLocalPathAsync(); // await GetCurrentSolutionAsync(); if (isRepositoryLoaded) @@ -181,7 +181,7 @@ private async Task GenerateDefaultConfigFileAsync(string configFileInUse var defaultConfigFileContent = await reader.ReadToEndAsync(); await fileStream.WriteAsync(defaultConfigFileContent); - return defaultConfigFileContent; + return JObject.Parse(defaultConfigFileContent).SelectToken("types").ToString(); } } } diff --git a/vs-commitizen/ViewModels/OpenGenerateLocalConfigViewModel.cs b/vs-commitizen/ViewModels/OpenGenerateLocalConfigViewModel.cs new file mode 100644 index 0000000..e5bae10 --- /dev/null +++ b/vs-commitizen/ViewModels/OpenGenerateLocalConfigViewModel.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using vs_commitizen.Infrastructure; +using vs_commitizen.Settings; + +namespace vs_commitizen.ViewModels +{ + public class OpenGenerateLocalConfigViewModel + { + private readonly IConfigFileProvider configFileProvider; + private readonly IFileAccessor fileAccessor; + private readonly IPopupManager popupManager; + + public OpenGenerateLocalConfigViewModel() + { + configFileProvider = IoC.GetInstance(); + fileAccessor = IoC.GetInstance(); + popupManager = IoC.GetInstance(); + + } + + public async Task IsCommandEnabledAsync() + { + var localConfigPath = await this.configFileProvider.TryGetLocalConfigAsync(); + + return !string.IsNullOrWhiteSpace(localConfigPath); + } + + public async Task ExecuteAsync(Func openFunc) + { + try + { + var localConfigPath = await this.configFileProvider.TryGetLocalConfigAsync(); + if (string.IsNullOrWhiteSpace(localConfigPath)) return; + + if (fileAccessor.Exists(localConfigPath)) + { + await openFunc(localConfigPath); + } + else + { + if (popupManager.Confirm("There is no local configuration file yet, do you want to create it for this repository ?", "Init local config file")) + { + fileAccessor.CopyFile(ConfigFileProvider.ConfigPathUserProfile, localConfigPath); + await openFunc(localConfigPath); + } + } + } + catch (Exception ex) + { + popupManager.Show(ex.ToString(), "An error occured"); + } + } + } +} diff --git a/vs-commitizen/defaultConfigFile.json b/vs-commitizen/defaultConfigFile.json index 9112816..4bb1480 100644 --- a/vs-commitizen/defaultConfigFile.json +++ b/vs-commitizen/defaultConfigFile.json @@ -1,5 +1,5 @@ { - "$schema": "https://github.com/MrLuje/vs-commitizen/config-schema.json", + "$schema": "https://raw.githubusercontent.com/MrLuje/vs-commitizen/master/config-schema.json", "types": [ { "type": "feat", diff --git a/vs-commitizen/vs-commitizen.csproj b/vs-commitizen/vs-commitizen.csproj index 1a3894d..cebc3af 100644 --- a/vs-commitizen/vs-commitizen.csproj +++ b/vs-commitizen/vs-commitizen.csproj @@ -64,6 +64,7 @@ + True True