diff --git a/nuspec/nuget/Cake.Issues.Testing.nuspec b/nuspec/nuget/Cake.Issues.Testing.nuspec index 6a653fa18..9201296c7 100644 --- a/nuspec/nuget/Cake.Issues.Testing.nuspec +++ b/nuspec/nuget/Cake.Issues.Testing.nuspec @@ -17,7 +17,7 @@ Common helpers for testing add-ins based on Cake.Issues Copyright © BBT Software AG and contributors Cake Script Cake-Issues Issues Testing - https://github.com/cake-contrib/Cake.Issues/releases/tag/0.6.0 + https://github.com/cake-contrib/Cake.Issues/releases/tag/0.6.1 diff --git a/nuspec/nuget/Cake.Issues.nuspec b/nuspec/nuget/Cake.Issues.nuspec index 23f76af80..c07df87ae 100644 --- a/nuspec/nuget/Cake.Issues.nuspec +++ b/nuspec/nuget/Cake.Issues.nuspec @@ -24,7 +24,7 @@ See the Project Site for an overview of the whole ecosystem of addins for workin Copyright © BBT Software AG and contributors Cake Script Cake-Issues CodeAnalysis Linting Issues - https://github.com/cake-contrib/Cake.Issues/releases/tag/0.6.0 + https://github.com/cake-contrib/Cake.Issues/releases/tag/0.6.1 diff --git a/src/Cake.Issues.Tests/IIssueExtensionsTests.cs b/src/Cake.Issues.Tests/IIssueExtensionsTests.cs new file mode 100644 index 000000000..500e9bb95 --- /dev/null +++ b/src/Cake.Issues.Tests/IIssueExtensionsTests.cs @@ -0,0 +1,332 @@ +namespace Cake.Issues.Tests +{ + using System; + using Cake.Issues.Testing; + using Shouldly; + using Xunit; + + public sealed class IIssueExtensionsTests + { + public sealed class TheProjectPathExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.ProjectPath()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Return_Full_Path() + { + // Given + var projectPath = @"src\Cake.Issues\Cake.Issues.csproj"; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InProjectFile(projectPath) + .Create(); + + // When + var result = issue.ProjectPath(); + + // Then + result.ShouldBe(@"src/Cake.Issues/Cake.Issues.csproj"); + } + + [Fact] + public void Should_Return_Null_If_Project_Is_Not_Set() + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = issue.ProjectPath(); + + // Then + result.ShouldBeNull(); + } + } + + public sealed class TheProjectDirectoryExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.ProjectDirectory()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Return_Full_Path() + { + // Given + var filePath = @"src\Cake.Issues\Foo.cs"; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InProjectFile(filePath) + .Create(); + + // When + var result = issue.ProjectDirectory(); + + // Then + result.ShouldBe(@"src/Cake.Issues"); + } + + [Fact] + public void Should_Return_Null_If_File_Is_Not_Set() + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = issue.ProjectDirectory(); + + // Then + result.ShouldBeNull(); + } + } + + public sealed class TheFilePathExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.FilePath()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Return_Full_Path() + { + // Given + var filePath = @"src\Cake.Issues\Foo.cs"; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InFile(filePath) + .Create(); + + // When + var result = issue.FilePath(); + + // Then + result.ShouldBe(@"src/Cake.Issues/Foo.cs"); + } + + [Fact] + public void Should_Return_Null_If_File_Is_Not_Set() + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = issue.FilePath(); + + // Then + result.ShouldBeNull(); + } + } + + public sealed class TheFileDirectoryExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.FileDirectory()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Return_Full_Path() + { + // Given + var filePath = @"src\Cake.Issues\Foo.cs"; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InFile(filePath) + .Create(); + + // When + var result = issue.FileDirectory(); + + // Then + result.ShouldBe(@"src/Cake.Issues"); + } + + [Fact] + public void Should_Return_Null_If_File_Is_Not_Set() + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = issue.FileDirectory(); + + // Then + result.ShouldBeNull(); + } + } + + public sealed class TheFileNameExtension + { + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + IIssue issue = null; + + // When + var result = Record.Exception(() => issue.FileName()); + + // Then + result.IsArgumentNullException("issue"); + } + + [Fact] + public void Should_Return_Full_Path() + { + // Given + var filePath = @"src\Cake.Issues\Foo.cs"; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InFile(filePath) + .Create(); + + // When + var result = issue.FileName(); + + // Then + result.ShouldBe("Foo.cs"); + } + + [Fact] + public void Should_Return_Null_If_File_Is_Not_Set() + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = issue.FileName(); + + // Then + result.ShouldBeNull(); + } + } + + public sealed class TheReplaceIssuePatternExtension + { + [Fact] + public void Should_Throw_If_Pattern_Is_Null() + { + // Given + string pattern = null; + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .Create(); + + // When + var result = Record.Exception(() => pattern.ReplaceIssuePattern(issue)); + + // Then + result.IsArgumentNullException("pattern"); + } + + [Fact] + public void Should_Throw_If_Issue_Is_Null() + { + // Given + var pattern = "foo"; + IIssue issue = null; + + // When + var result = Record.Exception(() => pattern.ReplaceIssuePattern(issue)); + + // Then + result.IsArgumentNullException("issue"); + } + + [Theory] + [InlineData("", "")] + [InlineData(" ", " ")] + [InlineData("foo", "foo")] + [InlineData("{foo}", "{foo}")] + [InlineData("foo {ProviderType} bar", "foo ProviderType Foo bar")] + [InlineData("foo {ProviderName} bar", "foo ProviderName Foo bar")] + [InlineData("foo {Priority} bar", "foo 400 bar")] + [InlineData("foo {PriorityName} bar", "foo Error bar")] + [InlineData("foo {ProjectPath} bar", "foo src/Cake.Issues/Cake.Issues.csproj bar")] + [InlineData("foo {ProjectDirectory} bar", "foo src/Cake.Issues bar")] + [InlineData("foo {ProjectName} bar", "foo Cake.Issues bar")] + [InlineData("foo {FilePath} bar", "foo src/Cake.Issues/foo.cs bar")] + [InlineData("foo {FileDirectory} bar", "foo src/Cake.Issues bar")] + [InlineData("foo {FileName} bar", "foo foo.cs bar")] + [InlineData("foo {Line} bar", "foo 42 bar")] + [InlineData("foo {Rule} bar", "foo Rule Foo bar")] + [InlineData("foo {RuleUrl} bar", "foo https://google.com/ bar")] + [InlineData("foo {Message} bar", "foo Message Foo bar")] + public void Should_Replace_Tokens(string pattern, string expectedResult) + { + // Given + var issue = + IssueBuilder + .NewIssue("Message Foo", "ProviderType Foo", "ProviderName Foo") + .InFile(@"src/Cake.Issues/foo.cs", 42) + .InProject(@"src/Cake.Issues/Cake.Issues.csproj", "Cake.Issues") + .OfRule("Rule Foo", new Uri("https://google.com")) + .WithPriority(IssuePriority.Error) + .Create(); + + // When + var result = pattern.ReplaceIssuePattern(issue); + + // Then + result.ShouldBe(expectedResult); + } + } + } +} diff --git a/src/Cake.Issues/IIssueExtensions.cs b/src/Cake.Issues/IIssueExtensions.cs new file mode 100644 index 000000000..eccddde38 --- /dev/null +++ b/src/Cake.Issues/IIssueExtensions.cs @@ -0,0 +1,161 @@ +namespace Cake.Issues +{ + /// + /// Extensions for . + /// + public static class IIssueExtensions + { + /// + /// Returns the full path of or null. + /// + /// Issue for which the path should be returned. + /// Full path to the project to which the file affected by the issue belongs. + public static string ProjectPath(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return issue.ProjectFileRelativePath?.FullPath; + } + + /// + /// Returns the directory of the . + /// + /// Issue for which the project directory should be returned. + /// Directory of the project to which the file affected by the issue belongs. + public static string ProjectDirectory(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return issue.ProjectFileRelativePath?.GetDirectory().FullPath; + } + + /// + /// Returns the full path of the . + /// + /// Issue for which the path should be returned. + /// Full path of the file affected by the issue. + public static string FilePath(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return issue.AffectedFileRelativePath?.FullPath; + } + + /// + /// Returns the directory of the . + /// + /// Issue for which the directory should be returned. + /// Directory of the file affected by the issue. + public static string FileDirectory(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return issue.AffectedFileRelativePath?.GetDirectory().FullPath; + } + + /// + /// Returns the name of the file of the . + /// + /// Issue for which the file name should be returned. + /// Name of the file affected by the issue. + public static string FileName(this IIssue issue) + { + issue.NotNull(nameof(issue)); + + return issue.AffectedFileRelativePath?.GetFilename().ToString(); + } + + /// + /// Returns a string with all patterns replaced by the values of . + /// + /// Pattern whose values should be replaced. + /// The following patterns are supported: + /// + /// + /// Pattern + /// Description + /// + /// + /// {ProviderType} + /// The value of . + /// + /// + /// {ProviderName} + /// The value of . + /// + /// + /// {Priority} + /// The value of . + /// + /// + /// {PriorityName} + /// The value of . + /// + /// + /// {ProjectPath} + /// The value of . + /// + /// + /// {ProjectDirectory} + /// The value of . + /// + /// + /// {ProjectName} + /// The value of . + /// + /// + /// {FilePath} + /// The value of . + /// + /// + /// {FileDirectory} + /// The value of . + /// + /// + /// {FileName} + /// The value of . + /// + /// + /// {Line} + /// The value of . + /// + /// + /// {Rule} + /// The value of . + /// + /// + /// {RuleUrl} + /// The value of . + /// + /// + /// {Message} + /// The value of . + /// + /// + /// + /// Issue whose values should be used to replace the patterns. + /// Value with all patterns replaced. + public static string ReplaceIssuePattern(this string pattern, IIssue issue) + { + pattern.NotNull(nameof(pattern)); + issue.NotNull(nameof(issue)); + + return + pattern + .Replace("{ProviderType}", issue.ProviderType) + .Replace("{ProviderName}", issue.ProviderName) + .Replace("{Priority}", issue.Priority?.ToString()) + .Replace("{PriorityName}", issue.PriorityName) + .Replace("{ProjectPath}", issue.ProjectPath()) + .Replace("{ProjectDirectory}", issue.ProjectDirectory()) + .Replace("{ProjectName}", issue.ProjectName) + .Replace("{FilePath}", issue.FilePath()) + .Replace("{FileDirectory}", issue.FileDirectory()) + .Replace("{FileName}", issue.FileName()) + .Replace("{Line}", issue.Line?.ToString()) + .Replace("{Rule}", issue.Rule) + .Replace("{RuleUrl}", issue.RuleUrl?.ToString()) + .Replace("{Message}", issue.Message); + } + } +} \ No newline at end of file