Add project files.

This commit is contained in:
2025-08-18 17:11:43 +03:30
parent 807dc263be
commit e5eb10d604
3943 changed files with 223719 additions and 0 deletions
@@ -0,0 +1,197 @@
using Elsa.Expressions.Models;
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.ContainerSerialization;
public class Tests
{
private readonly IServiceProvider _services;
private readonly IActivitySerializer _activitySerializer;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).Build();
_activitySerializer = _services.GetService<IActivitySerializer>()!;
}
[Fact]
public async void SerializeFlowchartContainerTest()
{
await _services.PopulateRegistriesAsync();
// Arrange
var start = new Start()
{
Id = "start",
Name = "Start",
};
var writeLine = new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
{
Id = "writeLine",
Name = "WriteLine",
Version = 3,
};
var end = new End()
{
Id = "end",
Name = "end",
};
var container = new Flowchart()
{
Id = "flowchart",
Name = "Flowchart",
Type = "Elsa.Flowchart",
Version = 42,
CustomProperties = new Dictionary<string, object>()
{
{ "purpose", "somePurpose" }
},
Metadata = new Dictionary<string, object>()
{
{ "int", 10 },
{ "bool", false },
{ "string", "str" },
},
Activities = new List<IActivity>() {
start,
writeLine,
end
},
Variables = new List<Variable>() {
new Variable<string>("TextVar", "This is the text to write")
},
Connections = new List<Connection>()
{
new Connection(start, writeLine),
new Connection(writeLine, end),
},
};
// Act
var serialized = _activitySerializer.Serialize(container);
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
// Assert
ValidateContainer(container, deserializedContainer);
}
[Fact]
public async void SerializeSequenceContainerTest()
{
await _services.PopulateRegistriesAsync();
// Arrange
var container = new Sequence()
{
Id = "sequence",
Name = "Sequence",
Type = "Elsa.Sequence",
Version = 42,
Variables = new List<Variable>() {
new Variable<string>("TextVar", "This is the text to write")
},
Activities = new List<IActivity>() {
new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
{
Id = "writeLine",
Name = "WriteLine",
CanStartWorkflow = true,
},
},
CustomProperties = new Dictionary<string, object>()
{
{ "purpose", "somePurpose" }
},
Metadata = new Dictionary<string, object>()
{
{ "int", 10 },
{ "bool", false },
{ "string", "str"},
}
};
// Act
var serialized = _activitySerializer.Serialize(container);
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
// Assert
ValidateContainer(container, deserializedContainer);
}
[Fact]
public async void SerializeParallelContainerTest()
{
await _services.PopulateRegistriesAsync();
// Arrange
var container = new Workflows.Activities.Parallel()
{
Id = "parallel",
Name = "Parallel",
Type = "Elsa.Parallel",
Version = 42,
Variables = new List<Variable>() {
new Variable<string>("TextVar", "This is the text to write")
},
Activities = new List<IActivity>() {
new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
{
Id = "writeLine",
Name = "WriteLine",
CanStartWorkflow = true,
},
},
CustomProperties = new Dictionary<string, object>()
{
{ "purpose", "somePurpose" }
},
Metadata = new Dictionary<string, object>()
{
{ "int", 10 },
{ "bool", false },
{ "string", "str"},
}
};
// Act
var serialized = _activitySerializer.Serialize(container);
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
// Assert
ValidateContainer(container, deserializedContainer);
}
private static void ValidateContainer(Container container, Container? deserializedContainer)
{
if (deserializedContainer == null)
throw new ArgumentNullException(nameof(deserializedContainer));
// Assert.Equivalent has trouble with the Behavior.Owner reference - since these aren't serialzied anyway, ignore them
deserializedContainer.Behaviors.Clear();
container.Behaviors.Clear();
foreach (Activity activity in deserializedContainer.Activities)
activity.Behaviors.Clear();
foreach (Activity activity in container.Activities)
activity.Behaviors.Clear();
// strict:false here allows "actual" to have extra public members that aren't part of "expected", and collection
// comparison allows "actual" to have more data in it than is present in "expected".
Assert.Equivalent(container, deserializedContainer, strict: false);
}
}
@@ -0,0 +1,8 @@
{
"StatusCode": "Created",
"Content": {
"_island": "[\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n]",
"_type": "Newtonsoft.Json.Linq.JArray, Newtonsoft.Json"
},
"_type": "ObjectMap"
}
@@ -0,0 +1,10 @@
{
"StatusCode": "Created",
"Content": [
{
"path": "folder1",
"command": "add"
}
],
"_type": "ObjectMap"
}
@@ -0,0 +1,8 @@
{
"StatusCode": "Created",
"Content": {
"_island": "{\r\n \u0022file1\u0022: {\r\n \u0022script\u0022: [\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n ]\r\n }\r\n}",
"_type": "Newtonsoft.Json.Linq.JObject, Newtonsoft.Json"
},
"_type": "ObjectMap"
}
@@ -0,0 +1,14 @@
{
"StatusCode": "Created",
"Content": {
"file1": {
"script": [
{
"path": "folder1",
"command": "add"
}
]
}
},
"_type": "ObjectMap"
}
@@ -0,0 +1,8 @@
{
"StatusCode": "Created",
"Content": {
"_island": "[\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n]",
"_type": "System.Text.Json.Nodes.JsonArray, System.Text.Json"
},
"_type": "ObjectMap"
}
@@ -0,0 +1,6 @@
[
{
"path": "folder1",
"command": "add"
}
]
@@ -0,0 +1,8 @@
{
"StatusCode": "Created",
"Content": {
"_island": "{\r\n \u0022file1\u0022: {\r\n \u0022script\u0022: [\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n ]\r\n }\r\n}",
"_type": "System.Text.Json.Nodes.JsonObject, System.Text.Json"
},
"_type": "ObjectMap"
}
@@ -0,0 +1,14 @@
{
"StatusCode": "Created",
"Content": {
"file1": {
"script": [
{
"path": "folder1",
"command": "add"
}
]
}
},
"_type": "ObjectMap"
}
@@ -0,0 +1,185 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.JsonSerialization;
public class SerializationTests(ITestOutputHelper testOutputHelper)
{
private readonly IServiceProvider _services = new TestApplicationBuilder(testOutputHelper).Build();
[Theory(DisplayName = "write")]
[InlineData(typeof(JsonObject), "JsonObjectIsland")]
[InlineData(typeof(JObject), "JObjectIsland")]
[InlineData(typeof(JsonArray), "JsonArrayIsland")]
[InlineData(typeof(JArray), "JArrayIsland")]
public void Test_Serialization_create_Island(Type type, string fileName)
{
var dict = GetContent(type);
var result = SerializeUsingPayloadSerializer(dict);
var expected = File.ReadAllText($"Serialization/JsonSerialization/{fileName}.json");
CompareJsonsObjects(expected, result);
}
[Theory(DisplayName = "roundtrip")]
[InlineData(typeof(JsonObject))]
[InlineData(typeof(JObject))]
[InlineData(typeof(JsonArray))]
[InlineData(typeof(JArray))]
public void Test_Serialization_roundtrip(Type type)
{
var dict = GetContent(type);
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
var result = transformationModel["Content"].ToString()!;
var expected = GetExpected(type);
CompareJsonsObjects(expected, result);
}
[Theory(DisplayName = "read")]
[InlineData(typeof(JsonObject), "JsonObjectIsland", "JsonObjectWithoutType")]
[InlineData(typeof(JObject), "JObjectIsland", "JObjectWithoutType")]
[InlineData(typeof(JsonArray), "JsonArrayIsland", "JsonArrayWithoutType")]
[InlineData(typeof(JArray), "JArrayIsland", "JArrayWithoutType")]
public void Test_Serialization_read(Type type, string fileName, string compareFileName)
{
var jsonContent = File.ReadAllText(@$"Serialization/JsonSerialization/{fileName}.json");
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonContent);
string result;
if (type == typeof(JObject) || type == typeof(JArray))
{
result = Newtonsoft.Json.JsonConvert.SerializeObject(transformationModel);
}
else
{
result = type == typeof(JsonObject) ? JsonObject.Create(JsonSerializer.SerializeToElement(transformationModel))!.ToString() : JsonArray.Create(JsonSerializer.SerializeToElement(transformationModel["Content"]))!.ToString();
}
var expected = File.ReadAllText(@$"Serialization/JsonSerialization/{compareFileName}.json");
CompareJsonsObjects(expected, result);
}
[Fact]
public void RoundtripComplexEnumerableObject()
{
var dict = new Dictionary<string, object>
{
{
"Content", new List<TestObject>()
{
new()
{
Data = "Hello World"
}
}
}
};
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
var result = transformationModel["Content"];
Assert.Equal(typeof(List<TestObject>), result.GetType());
}
[Fact]
public void RoundtripPrimitiveCollections()
{
var dict = new Dictionary<string, object>
{
{
"Content", new List<Guid>
{
Guid.NewGuid()
}
}
};
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
var result = transformationModel["Content"];
Assert.Equal(typeof(List<Guid>), result.GetType());
}
[Fact]
public void RoundtripPrimitiveArrays()
{
var dict = new Dictionary<string, object>
{
{
"Content", new[]
{
Guid.NewGuid()
}
}
};
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
var result = transformationModel["Content"];
Assert.Equal(typeof(Guid[]), result.GetType());
}
private string SerializeUsingPayloadSerializer(object obj)
{
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
return payloadSerializer.Serialize(obj);
}
private IDictionary<string, object> DeSerializeDictionaryUsingPayloadSerializer(string jsonString)
{
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
return payloadSerializer.Deserialize<IDictionary<string, object>>(jsonString);
}
private void CompareJsonsObjects(string expected, string actual)
{
var jsonActual = NormalizeNewlines(JsonNode.Parse(actual!)?.ToString());
var jsonExpected = NormalizeNewlines(JsonNode.Parse(expected!)?.ToString());
Assert.Equal(jsonExpected, jsonActual);
}
private IDictionary<string, object> GetContent(Type type)
{
var isArray = type == typeof(JsonArray) || type == typeof(JArray);
var jsonContent = isArray ? "[{\"path\":\"folder1\",\"command\":\"add\"}]" : "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
var dict = new Dictionary<string, object>
{
{
"StatusCode", "Created"
},
{
"Content", isArray ? (type == typeof(JArray) ? JArray.Parse(jsonContent) : JsonArray.Parse(jsonContent)) : (type == typeof(JObject) ? JObject.Parse(jsonContent) : JsonObject.Parse(jsonContent))
}
};
return dict;
}
private string GetExpected(Type type)
{
if (type == typeof(JsonArray) || type == typeof(JArray))
{
return "[{\"path\":\"folder1\",\"command\":\"add\"}]";
}
else
{
return "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
}
}
private static string? NormalizeNewlines(string? input) => input?.Replace("\r\n", "\n").Replace("\\r\\n", "\\n");
}
public class TestObject
{
public string? Data { get; set; }
}
@@ -0,0 +1,26 @@
using Elsa.Extensions;
namespace Elsa.Workflows.IntegrationTests.Serialization.Polymorphism;
/// <summary>
/// A model that contains a mixed bag of primitive, complex and expando objects.
/// </summary>
public record Model(
string? Text = default,
long Number = default,
bool Flag = default,
ICollection<Model>? Items = default,
object? Metadata = default,
object? Payload = default,
object? HashSet = default,
IDictionary<string, Model>? Properties = default)
{
public Model() : this(null, default, default, null, default, default, default, default)
{
}
}
public class CustomDictionary : Dictionary<string, string[]>
{
public string? ContentType => this.GetValue("content-type")?[0];
}
@@ -0,0 +1,90 @@
using System.Dynamic;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Elsa.Expressions.Services;
using Elsa.Workflows.Serialization.Converters;
using Elsa.Workflows.Serialization.ReferenceHandlers;
namespace Elsa.Workflows.IntegrationTests.Serialization.Polymorphism;
public class Tests
{
[Fact(DisplayName = "Objects mixed with primitive, complex, expando objects and arrays of these can be serialized")]
public void Test1()
{
var model = CreateModel();
var expectedJson = File.ReadAllText("Serialization/Polymorphism/data.json");
var actualJson = JsonSerializer.Serialize(model, GetSerializerOptions());
Assert.Equal(expectedJson, actualJson);
}
[Fact(DisplayName = "Objects mixed with primitive, complex, expando objects and arrays of these can be round-tripped")]
public void Test2()
{
var model = CreateModel();
var json = JsonSerializer.Serialize(model, GetSerializerOptions());
var deserializedModel = JsonSerializer.Deserialize<Model>(json, GetSerializerOptions());
var roundTrippedJson = JsonSerializer.Serialize(deserializedModel, GetSerializerOptions());
Assert.Equal(json, roundTrippedJson);
}
private Model CreateModel()
{
// Create a model with various nested properties
var metadata = new ExpandoObject() as IDictionary<string, object>;
// Initialize metadata with test data.
metadata["Foo"] = "Bar";
metadata["Number"] = 123;
metadata["Flag"] = true;
metadata["Models"] = new List<Model>
{
new(Text: "Hello", Number: 1, Flag: true, Metadata: metadata),
new(Text: "World", Number: 2)
};
metadata["CustomDictionary"] = new CustomDictionary { ["content-type"] = new[] { "application/json" } };
return new Model(
"Hello World",
123,
true,
new List<Model>
{
new("Hello", 1, true, new List<Model>() { new(Metadata: metadata) }),
new("World", 2)
},
metadata,
new Model("Payload"),
new HashSet<Model>
{
new()
{
Text = "I'm a model in a set!"
}
},
new Dictionary<string, Model>
{
["Hello"] = new("Hello", 1, true),
["World"] = new("World", 2),
}
);
}
private JsonSerializerOptions GetSerializerOptions()
{
var referenceHandler = new CrossScopedReferenceHandler();
var options = new JsonSerializerOptions
{
ReferenceHandler = referenceHandler,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(JsonMetadataServices.TimeSpanConverter);
options.Converters.Add(new PolymorphicObjectConverterFactory(new WellKnownTypeRegistry()));
return options;
}
}
@@ -0,0 +1 @@
{"$id":"1","text":"Hello World","number":123,"flag":true,"items":{"$id":"2","$values":[{"$id":"3","text":"Hello","number":1,"flag":true,"items":{"$id":"4","$values":[{"$id":"5","number":0,"flag":false,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$id":"6","$values":[{"$id":"7","text":"Hello","number":1,"flag":true,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$id":"8","content-type":["application/json"],"_type":"Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.CustomDictionary, Elsa.Workflows.IntegrationTests"}}},{"$id":"9","text":"World","number":2,"flag":false}],"_type":"System.Collections.Generic.List\u00601[[Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests]], System.Private.CoreLib"},"CustomDictionary":{"$ref":"8"}}}]}},{"$id":"10","text":"World","number":2,"flag":false}]},"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$ref":"8"}},"payload":{"$id":"11","text":"Payload","number":0,"flag":false,"_type":"Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests"},"hashSet":{"$id":"12","$values":[{"$id":"13","text":"I\u0027m a model in a set!","number":0,"flag":false}],"_type":"System.Collections.Generic.HashSet\u00601[[Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests]], System.Private.CoreLib"},"properties":{"$id":"14","Hello":{"$id":"15","text":"Hello","number":1,"flag":true},"World":{"$id":"16","text":"World","number":2,"flag":false}}}
@@ -0,0 +1,38 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates;
/// <summary>
/// Contains tests for variable expressions serialization.
/// </summary>
public class Tests
{
private readonly IActivitySerializer _activitySerializer;
private readonly IWorkflowBuilder _workflowBuilder;
/// <summary>
/// Initializes a new instance of the <see cref="Tests"/> class.
/// </summary>
public Tests(ITestOutputHelper testOutputHelper)
{
var serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
_activitySerializer = serviceProvider.GetRequiredService<IActivitySerializer>();
var workflowBuilderFactory = serviceProvider.GetRequiredService<IWorkflowBuilderFactory>();
_workflowBuilder = workflowBuilderFactory.CreateBuilder();
}
/// <summary>
/// Tests that programmatic workflows with inputs using delegates do not throw exceptions when serialized.
/// </summary>
[Fact(DisplayName = "Programmatic workflows with inputs using delegates do not throw exceptions when serialized")]
public async Task Test1()
{
var workflow = await _workflowBuilder.BuildWorkflowAsync<GreeterWorkflow>();
var serializedWorkflow = _activitySerializer.Serialize(workflow);
// If it reached here, the test passed
}
}
@@ -0,0 +1,17 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates;
/// <inheritdoc />
public class GreeterWorkflow : WorkflowBase
{
/// <inheritdoc />
protected override void Build(IWorkflowBuilder builder)
{
var messageInput = builder.WithInput<string>("Message", "The message to write to the console.");
builder.Name = "Greeter Workflow";
builder.Root = new WriteLine(context => context.GetInput<string>(messageInput));
}
}
@@ -0,0 +1,38 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json.Nodes;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.SpecialPropertySerialization
{
public class Tests
{
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).Build();
}
[Fact(DisplayName = "Serialize and deserialize properties with $ prefix")]
public void Test_Special_Properties_Serialization_roundtrip()
{
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
var jsonContent = "{\"prop1\":{\"script\":[{\"$id\":\"someid\"}]} }";
var dict = new Dictionary<string, object>
{
{ "Content", JsonNode.Parse(jsonContent)! }
};
var jsonSerialized = payloadSerializer.Serialize(dict);
var transformationModel = payloadSerializer.Deserialize<IDictionary<string, object>>(jsonSerialized);
var result = transformationModel["Content"].ToString()!;
var expected = "{\"prop1\":{\"script\":[{\"$id\":\"someid\"}]} }";
var jsonResult = JsonNode.Parse(result)?.ToString();
var jsonExpected = JsonNode.Parse(expected)?.ToString();
Assert.Equal(jsonExpected, jsonResult);
}
}
}
@@ -0,0 +1,58 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.UIHints;
using Elsa.Workflows.UIHints.Dropdown;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
/// <summary>
/// Initializes a new instance of the <see cref="MigrationTests"/> class.
/// </summary>
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureElsa(elsa =>
{
elsa.AddActivity<TestActivity>();
})
.Build();
}
[Fact(DisplayName = "Enum input types get a dropdown UIHint by default")]
public async Task Test1()
{
var activityDescriber = _services.GetRequiredService<IActivityDescriber>();
var description = await activityDescriber.DescribeActivityAsync(typeof(TestActivity));
var inputDescription = description.Inputs.First();
Assert.Equal(InputUIHints.DropDown, inputDescription.UIHint);
}
[Fact(DisplayName = "Enum input types get a dropdown UIHint by default")]
public async Task Test2()
{
var activityDescriber = _services.GetRequiredService<IActivityDescriber>();
var description = await activityDescriber.DescribeActivityAsync(typeof(TestActivity));
var inputDescription = description.Inputs.First();
Assert.True(inputDescription.UISpecifications.ContainsKey(InputUIHints.DropDown));
Assert.True(inputDescription.UISpecifications[InputUIHints.DropDown] is DropDownProps);
var dropDownProperties = (DropDownProps) inputDescription.UISpecifications[InputUIHints.DropDown];
Assert.Collection(dropDownProperties.SelectList.Items,
item => { Assert.Equal("OptionsAreNice", item.Text); Assert.Equal("OptionsAreNice", item.Value); },
item => { Assert.Equal("ToHave", item.Text); Assert.Equal("ToHave", item.Value); },
item => { Assert.Equal("IfYouCanChooseThem", item.Text); Assert.Equal("IfYouCanChooseThem", item.Value); });
}
}
@@ -0,0 +1,29 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
/// <summary>
/// Write a line of text to the console.
/// </summary>
[Activity("Elsa", "Test", "Used in Testing - not a real activity")]
public class TestActivity : CodeActivity
{
/// <summary>
/// The text to write.
/// </summary>
[Description("The text to write.")]
public Input<TestEnumType> Option { get; set; } = null!;
[JsonConstructor]
private TestActivity(string? source = null, int? line = null) : base(source, line)
{
}
/// <inheritdoc />
public TestActivity(Input<TestEnumType> option, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => Option = option;
}
@@ -0,0 +1,8 @@
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
public enum TestEnumType
{
OptionsAreNice,
ToHave,
IfYouCanChooseThem
}
@@ -0,0 +1,41 @@
using System.Text.Json.Serialization;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
/// <inheritdoc />
public class NumberActivity : CodeActivity
{
/// <inheritdoc />
[JsonConstructor]
public NumberActivity()
{
}
/// <inheritdoc />
public NumberActivity(Variable<int> variable)
{
Number = new(variable);
}
/// <inheritdoc />
public NumberActivity(Literal<int> literal)
{
Number = new(literal);
}
/// <summary>
/// Gets or sets the number.
/// </summary>
public Input<int> Number { get; set; } = default!;
/// <inheritdoc />
protected override void Execute(ActivityExecutionContext context)
{
var number = Number.Get(context);
Console.WriteLine(number.ToString());
}
}
@@ -0,0 +1,49 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Management;
using Elsa.Workflows.Memory;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
/// <summary>
/// Contains tests for variable expressions serialization.
/// </summary>
public class Tests
{
private readonly IWorkflowSerializer _workflowSerializer;
private readonly IWorkflowBuilder _workflowBuilder;
private readonly IWorkflowRunner _workflowRunner;
/// <summary>
/// Initializes a new instance of the <see cref="Tests"/> class.
/// </summary>
public Tests(ITestOutputHelper testOutputHelper)
{
var serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
_workflowSerializer = serviceProvider.GetRequiredService<IWorkflowSerializer>();
IWorkflowBuilderFactory workflowBuilderFactory = serviceProvider.GetRequiredService<IWorkflowBuilderFactory>();
_workflowBuilder = workflowBuilderFactory.CreateBuilder();
_workflowRunner = serviceProvider.GetRequiredService<IWorkflowRunner>();
}
/// <summary>
/// Variable types remain intact after serialization.
/// </summary>
[Fact(DisplayName = "Variable types remain intact after serialization")]
public async Task Test1()
{
var workflow = await _workflowBuilder.BuildWorkflowAsync<SampleWorkflow>();
var serialized = _workflowSerializer.Serialize(workflow);
var deserializedWorkflow = _workflowSerializer.Deserialize(serialized);
var rehydratedWriteLine1 = (WriteLine)((Sequence)deserializedWorkflow.Root).Activities.ElementAt(0);
var rehydratedNumberActivity1 = (NumberActivity)((Sequence)deserializedWorkflow.Root).Activities.ElementAt(2);
Assert.IsType<Variable<string>>(rehydratedWriteLine1.Text.Expression!.Value);
Assert.IsType<Variable<int>>(rehydratedNumberActivity1.Number.Expression!.Value);
await _workflowRunner.RunAsync(workflow);
}
}
@@ -0,0 +1,30 @@
using Elsa.Expressions.Models;
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
class SampleWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var variable1 = workflow.WithVariable("Some variable");
var variable2 = workflow.WithVariable(42);
var literal1 = new Literal<string>("Some literal");
var literal2 = new Literal<int>(84);
var writeLine1 = new WriteLine(variable1);
var writeLine2 = new WriteLine(literal1);
var numberActivity1 = new NumberActivity(variable2);
var numberActivity2 = new NumberActivity(literal2);
workflow.Root = new Sequence
{
Activities =
{
writeLine1,
writeLine2,
numberActivity1,
numberActivity2
}
};
}
}
@@ -0,0 +1,5 @@
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
internal record VariablesContainer(ICollection<Variable> Variables);
@@ -0,0 +1,44 @@
using System.Collections.ObjectModel;
using Elsa.Testing.Shared;
using Elsa.Workflows.Memory;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
public class Tests
{
private readonly IPayloadSerializer _payloadSerializer;
public Tests(ITestOutputHelper testOutputHelper)
{
var services = new TestApplicationBuilder(testOutputHelper).Build();
_payloadSerializer = services.GetRequiredService<IPayloadSerializer>();
}
[Fact(DisplayName = "Variable types remain intact after serialization")]
public void Test1()
{
// Create collection of variables to serialize.
var variables = new Collection<Variable>();
var model = new VariablesContainer(variables);
// Create a typed variable.
var variable = new Variable<bool>("Variable", false);
// Add variable to collection.
variables.Add(variable);
// Serialize collection.
var json = _payloadSerializer.Serialize(model);
// Deserialize collection.
var deserializedModel = _payloadSerializer.Deserialize<VariablesContainer>(json);
// Get the first variable.
var deserializedVariable = deserializedModel.Variables.First();
// Assert that the variable is of the correct type.
Assert.IsType<Variable<bool>>(deserializedVariable);
}
}
@@ -0,0 +1,9 @@
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
class SampleWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.WithVariable<bool>();
}
}