diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..097ee00 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,34 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore -c Release -p:TreatWarningsAsErrors=true + - name: Test + run: dotnet test --no-build -c Release --verbosity normal --logger "trx;LogFileName=test_results.trx" + - name: Upload Test Results + uses: actions/upload-artifact@v4 + if: always() # This ensures it runs even if tests fail + with: + name: test-results + path: "**/test_results.trx" diff --git a/SlugGeneratorLibrary/SlugGenerator.cs b/SlugGeneratorLibrary/SlugGenerator.cs index 726f0eb..8740f03 100644 --- a/SlugGeneratorLibrary/SlugGenerator.cs +++ b/SlugGeneratorLibrary/SlugGenerator.cs @@ -1,19 +1,46 @@ -using System; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace SlugGeneratorLibrary { public static class SlugGenerator { - public static string Generate(string text) + public static string CustomGenerate(string text, char separator) { - if (text is null) - throw new ArgumentNullException(nameof(text)); - text = text.Trim() - .ToLowerInvariant(); - text = Regex.Replace(text, @"[+()^*%#@!/\\.,|`~]+", string.Empty); - text = Regex.Replace(text, @"[\s_-]+", "-"); + ArgumentNullException.ThrowIfNull(text); + text = Regex.Replace(text.Trim() + .ToLowerInvariant(), + @"[+()^*%#@!/\\.,|`~]+", string.Empty); + text = Regex.Replace(text.Trim(), @"[\s_-]+", separator.ToString()); return text; } + + public static string GenerateHyphens(string text) + { + return CustomGenerate(text, '-'); + } + + public static string GenerateUnderscores(string text) + { + return CustomGenerate(text, '_'); + } + + public static string GenerateUnique(string text) + { + ArgumentNullException.ThrowIfNull(text); + // append text with a GUID-based suffix to greatly reduce collision risk + string uniqueText = text; + string slugifiedText = GenerateHyphens(uniqueText); + slugifiedText += '-' + Guid.NewGuid().ToString("N"); + return slugifiedText; + } + } + + public static class StringExtensions + { + /// Convert string to slug separated by hyphens. + public static string ToSlug(this string text) + { + return SlugGenerator.GenerateHyphens(text); + } } } \ No newline at end of file diff --git a/SlugGeneratorUnitTesting/SlugAlgorithmUnitTest.cs b/SlugGeneratorUnitTesting/SlugAlgorithmUnitTest.cs index f2af7c6..d9dab6d 100644 --- a/SlugGeneratorUnitTesting/SlugAlgorithmUnitTest.cs +++ b/SlugGeneratorUnitTesting/SlugAlgorithmUnitTest.cs @@ -1,5 +1,4 @@ using SlugGeneratorLibrary; -using Xunit; namespace SlugGeneratorUnitTesting { @@ -8,64 +7,92 @@ public class SlugAlgorithmUnitTest [Fact] public void RemoveWhiteSpaces() { - Assert.Equal("hello", SlugGenerator.Generate("Hello")); - Assert.Equal("hello", SlugGenerator.Generate(" Hello ")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens(" Hello ")); } [Fact] public void ConvertToLowerCase() { - Assert.Equal("hello", SlugGenerator.Generate("hElLo")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("hElLo")); } [Fact] public void NullInput() { - Assert.Throws(() => SlugGenerator.Generate(null)); + Assert.Throws(() => SlugGenerator.GenerateHyphens(null!)); } [Fact] public void EmptyInput() { - Assert.Equal(string.Empty, SlugGenerator.Generate(string.Empty)); + Assert.Equal(string.Empty, SlugGenerator.GenerateHyphens(string.Empty)); } [Fact] public void InputWithOnlyWhiteSpaces() { - Assert.Equal(string.Empty, SlugGenerator.Generate(" ")); + Assert.Equal(string.Empty, SlugGenerator.GenerateHyphens(" ")); } [Fact] public void InputWithSpecialCharacters() { - Assert.Equal("hello-world", SlugGenerator.Generate("Hello World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello-World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello_World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello____World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello _ World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello - _ - World")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello -_World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello-World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello_World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello____World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello _ World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello - _ - World")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello -_World")); } [Fact] public void InputWithSpecialCharactersToRemove() { - Assert.Equal("hello", SlugGenerator.Generate("Hello!")); - Assert.Equal("hello", SlugGenerator.Generate("Hello@")); - Assert.Equal("hello", SlugGenerator.Generate("Hello#")); - Assert.Equal("hello", SlugGenerator.Generate("Hello%")); - Assert.Equal("hello", SlugGenerator.Generate("Hello^")); - Assert.Equal("hello", SlugGenerator.Generate("Hello*")); - Assert.Equal("hello", SlugGenerator.Generate("Hello()")); - Assert.Equal("helloworld", SlugGenerator.Generate("Hello()!@#%^*+/\\.|`~,world")); - Assert.Equal("hello-world", SlugGenerator.Generate("Hello_- ()!@#%^*+/\\.|`~,- world")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello!")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello@")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello#")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello%")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello^")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello*")); + Assert.Equal("hello", SlugGenerator.GenerateHyphens("Hello()")); + Assert.Equal("helloworld", SlugGenerator.GenerateHyphens("Hello()!@#%^*+/\\.|`~,world")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("Hello_- ()!@#%^*+/\\.|`~,- world")); + Assert.Equal("hello-world", SlugGenerator.GenerateHyphens("!@# Hello World")); } [Fact] public void InputWithArabicCharacters() { - Assert.Equal("مرحبا-بالعالم", SlugGenerator.Generate("مرحبا بالعالم")); + Assert.Equal("مرحبا-بالعالم", SlugGenerator.GenerateHyphens("مرحبا بالعالم")); + } + + [Fact] + public void SlugStringExtension() + { + Assert.Equal("hello-world", "hello world".ToSlug()); + } + + [Fact] + public void SlugWithUnderscores() + { + Assert.Equal("hello_world", SlugGenerator.GenerateUnderscores("hello world")); + } + + [Fact] + public void CustomGenerate() + { + Assert.Equal("hello.world", SlugGenerator.CustomGenerate("hello world", '.')); + Assert.Equal("hello*world", SlugGenerator.CustomGenerate("hello world", '*')); + } + + [Fact] + public void UniqueSlug() + { + string input = "شقة في الجميلية"; + Assert.NotEqual(SlugGenerator.GenerateUnique(input), + SlugGenerator.GenerateUnique(input)); } } }