diff --git a/src/common/Elsa.Testing.Shared/UnicodeRangeGenerator.cs b/src/common/Elsa.Testing.Shared/UnicodeRangeGenerator.cs new file mode 100644 index 000000000..24eb7c64e --- /dev/null +++ b/src/common/Elsa.Testing.Shared/UnicodeRangeGenerator.cs @@ -0,0 +1,70 @@ +namespace Elsa.Testing.Shared; + +/// +/// Generates Unicode ranges. +/// +public static class UnicodeRangeGenerator +{ + /// + /// Generates a range of Unicode characters. + /// + public static string GenerateUnicodeString() + { + // This method generates a string containing characters from various Unicode ranges. + var sb = new System.Text.StringBuilder(); + + // Add some characters from various ranges + sb.Append('a'); // Basic Latin + sb.Append('\u0370'); // Greek and Coptic + sb.Append('\u200B'); // Zero width space + sb.Append('\u4E00'); // CJK Unified Ideographs + + // German umlauts (in unicode): + sb.Append('\u00E4'); // ä + sb.Append('\u00F6'); // ö + sb.Append('\u00FC'); // ü + sb.Append('\u00DF'); // ß + + // French characters (in unicode): + sb.Append('\u00E0'); // à + sb.Append('\u00E2'); // â + sb.Append('\u00E7'); // ç + sb.Append('\u00E8'); // è + + // Chinese characters (in unicode): + sb.Append('\u4E00'); // 一 + sb.Append('\u4E01'); // 丁 + sb.Append('\u4E02'); // 丂 + sb.Append('\u4E03'); // 七 + + // Russian characters (in unicode): + sb.Append('\u0410'); // А + sb.Append('\u0411'); // Б + sb.Append('\u0412'); // В + sb.Append('\u0413'); // Г + sb.Append('\u0414'); // Д + sb.Append('\u0415'); // Е + + // Arabic characters (in unicode): + sb.Append('\u0627'); // ا + sb.Append('\u0628'); // ب + sb.Append('\u0629'); // ة + sb.Append('\u062A'); // ت + + // Syriac characters (in unicode): + sb.Append('\u0710'); // ܐ + sb.Append('\u0711'); // ܑ + sb.Append('\u0712'); // ܒ + sb.Append('\u0713'); // ܓ + sb.Append('\u0714'); // ܔ + + // Hebrew characters (in unicode): + sb.Append('\u05D0'); // א + sb.Append('\u05D1'); // ב + sb.Append('\u05D2'); // ג + sb.Append('\u05D3'); // ד + + return sb.ToString(); + } + +} \ No newline at end of file diff --git a/test/unit/Elsa.JavaScript.UnitTests/Elsa.JavaScript.UnitTests.csproj b/test/unit/Elsa.JavaScript.UnitTests/Elsa.JavaScript.UnitTests.csproj index f14095ccd..d6cf18b8e 100644 --- a/test/unit/Elsa.JavaScript.UnitTests/Elsa.JavaScript.UnitTests.csproj +++ b/test/unit/Elsa.JavaScript.UnitTests/Elsa.JavaScript.UnitTests.csproj @@ -5,6 +5,7 @@ + diff --git a/test/unit/Elsa.JavaScript.UnitTests/ToJsonTests.cs b/test/unit/Elsa.JavaScript.UnitTests/ToJsonTests.cs new file mode 100644 index 000000000..20d6b1719 --- /dev/null +++ b/test/unit/Elsa.JavaScript.UnitTests/ToJsonTests.cs @@ -0,0 +1,48 @@ +using System.Text.Json; +using Elsa.Expressions.Models; +using Elsa.JavaScript.Contracts; +using Elsa.Testing.Shared; +using Elsa.Workflows.Memory; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Elsa.JavaScript.UnitTests; + +public class ToJsonTests(ITestOutputHelper testOutputHelper) +{ + private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build(); + + [Fact(DisplayName = "Serialize large unicode string using JavaScript's toJson function")] + public async Task Test1() + { + var javaScriptEvaluator = _serviceProvider.GetRequiredService(); + var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString(); + var script = $"toJson({{ text: '{unicodeString}' }})"; + var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister()); + var result = (string)(await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext))!; + var serializedText = JsonDocument.Parse(result).RootElement.GetProperty("text").GetString(); + Assert.Equal(unicodeString, serializedText); + } + + [Fact(DisplayName = "Serialize large unicode string using JavaScript's toJson function from a workflow variable")] + public async Task Test2() + { + var javaScriptEvaluator = _serviceProvider.GetRequiredService(); + var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister()); + var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString(); + var payloadVariable = new Variable + { + Name = "Payload" + }; + var payload = new + { + Text = unicodeString + }; + payloadVariable.Set(expressionExecutionContext, payload); + var script = "toJson(getPayload())"; + var result = (string)(await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext))!; + var serializedText = JsonDocument.Parse(result).RootElement.GetProperty("Text").GetString(); + Assert.Equal(unicodeString, serializedText); + } +} \ No newline at end of file diff --git a/test/unit/Elsa.JavaScript.UnitTests/WorkflowDefinitionActivityJavaScriptHandlerTests.cs b/test/unit/Elsa.JavaScript.UnitTests/WorkflowDefinitionActivityJavaScriptHandlerTests.cs deleted file mode 100644 index e809076e2..000000000 --- a/test/unit/Elsa.JavaScript.UnitTests/WorkflowDefinitionActivityJavaScriptHandlerTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Xunit; - -namespace Elsa.JavaScript.UnitTests; - -public class WorkflowDefinitionActivityJavaScriptHandlerTests -{ - [Fact] - public void Test1() - { - - } - -} \ No newline at end of file diff --git a/test/unit/Elsa.Workflows.Core.UnitTests/Elsa.Workflows.Core.UnitTests.csproj b/test/unit/Elsa.Workflows.Core.UnitTests/Elsa.Workflows.Core.UnitTests.csproj index 524932680..688b8b90c 100644 --- a/test/unit/Elsa.Workflows.Core.UnitTests/Elsa.Workflows.Core.UnitTests.csproj +++ b/test/unit/Elsa.Workflows.Core.UnitTests/Elsa.Workflows.Core.UnitTests.csproj @@ -5,6 +5,7 @@ + diff --git a/test/unit/Elsa.Workflows.Core.UnitTests/SerializerEncodingTests.cs b/test/unit/Elsa.Workflows.Core.UnitTests/SerializerEncodingTests.cs new file mode 100644 index 000000000..803cb7f9b --- /dev/null +++ b/test/unit/Elsa.Workflows.Core.UnitTests/SerializerEncodingTests.cs @@ -0,0 +1,85 @@ +using System.Text.Json; +using Elsa.Testing.Shared; +using Elsa.Workflows.Contracts; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; + +namespace Elsa.Workflows.Core.UnitTests; + +public class SerializerUnicodeEncodingTests(ITestOutputHelper testOutputHelper) +{ + private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build(); + + [Fact] + public void TestPayloadSerializer() + { + var serializer = _serviceProvider.GetRequiredService(); + TestSerializer(input => serializer.Serialize(input)); + } + + [Fact] + public void TestApiSerializer() + { + var serializer = _serviceProvider.GetRequiredService(); + TestSerializer(input => serializer.Serialize(input)); + } + + [Fact] + public void TestBookmarkPayloadSerializer() + { + var serializer = _serviceProvider.GetRequiredService(); + TestSerializer(input => serializer.Serialize(input)); + } + + [Fact] + public async Task TestSafeSerializer() + { + var serializer = _serviceProvider.GetRequiredService(); + await TestSerializerAsync(input => serializer.SerializeAsync(input).AsTask()); + } + + [Fact] + public async Task TestWorkflowStateSerializer() + { + var serializer = _serviceProvider.GetRequiredService(); + await TestSerializerAsync(input => serializer.SerializeAsync(input)); + } + + private void TestSerializer(Func serialize) + { + var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString(); + var anonymousObject = new + { + Text = unicodeString + }; + var serializedJson = serialize(anonymousObject); + var serializedStringValue = GetSerializedTextValue(serializedJson); + Assert.Equal(unicodeString, serializedStringValue); + } + + private async Task TestSerializerAsync(Func> serialize) + { + var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString(); + var anonymousObject = new + { + Text = unicodeString + }; + var serializedJson = await serialize(anonymousObject); + var serializedStringValue = GetSerializedTextValue(serializedJson); + Assert.Equal(unicodeString, serializedStringValue); + } + + private string GetSerializedTextValue(string serializedJson) + { + var rootElement = JsonDocument.Parse(serializedJson).RootElement; + return GetCaseInsensitiveSerializedTextValue(rootElement, "text", "Text"); + } + + private string GetCaseInsensitiveSerializedTextValue(JsonElement jsonElement, params string[] propertyNames) + { + foreach (string propertyName in propertyNames) + if (jsonElement.TryGetProperty(propertyName, out var property)) + return property.GetString()!; + throw new KeyNotFoundException($"None of the following properties were found: {string.Join(", ", propertyNames)}"); + } +} \ No newline at end of file