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,64 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Activities.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class BreakTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public BreakTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Break exits out of ForEach")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BreakForEachWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "C#", "End" }, lines);
}
[Fact(DisplayName = "Break exits out of immediate ForEach only")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<NestedForEachWithBreakWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "C#", "Classes", "Rust", "Classes", "Go", "Classes" }, lines);
}
[Fact(DisplayName = "Break exits out of For")]
public async Task Test3()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BreakForWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "0", "1", "End" }, lines);
}
[Fact(DisplayName = "Break exits out of While")]
public async Task Test4()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BreakWhileWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "1", "2", "End" }, lines);
}
[Fact(DisplayName = "Break removes the right bookmarks")]
public async Task Test5()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BreakWhileWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "1", "2", "End" }, lines);
}
}
@@ -0,0 +1,38 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
class BreakForEachWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var items = new[] { "C#", "Rust", "Go" };
var currentItem = new Variable<string>("CurrentItem", "");
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Start"),
new ForEach<string>
{
Items = new(items),
CurrentValue = new (currentItem),
Body = new Sequence
{
Activities =
{
new If(context => currentItem.Get(context) == "Rust")
{
Then = new Break()
},
new WriteLine(currentItem)
}
}
},
new WriteLine("End"),
}
};
}
}
@@ -0,0 +1,36 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
class BreakForWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", null);
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Start"),
new For(0, 3, 1)
{
CurrentValue = new (currentValue),
Body = new Sequence
{
Activities =
{
new If(context => currentValue.Get(context) == 2)
{
Then = new Break()
},
new WriteLine(context => currentValue.Get(context).ToString()),
}
}
},
new WriteLine("End"),
}
};
}
}
@@ -0,0 +1,51 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
public class BreakWhileForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", 0);
workflow.Root = new Sequence
{
Variables = { currentValue },
Activities =
{
While.True(new Fork
{
Branches =
{
new Sequence
{
Activities =
{
new SetVariable
{
Variable = currentValue,
Value = new (context => currentValue.Get(context) + 1)
},
new If(context => currentValue.Get(context) == 3)
{
Then = new Break()
}
}
},
new Sequence
{
Activities =
{
new WriteLine("Waiting for event..."),
new Event("Some event") { Id = "SomeEvent" },
new WriteLine("Resuming"),
}
}
}
}),
}
};
}
}
@@ -0,0 +1,38 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
public class BreakWhileWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", 0);
workflow.Root = new Sequence
{
Variables = { currentValue },
Activities =
{
new WriteLine("Start"),
While.True(new Sequence
{
Activities =
{
new SetVariable
{
Variable = currentValue,
Value = new (context => currentValue.Get(context) + 1)
},
new If(context => currentValue.Get(context) == 3)
{
Then = new Break()
},
new WriteLine(context => currentValue.Get(context).ToString()),
}
}),
new WriteLine("End"),
}
};
}
}
@@ -0,0 +1,34 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class FinishForkedWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Fork
{
JoinMode = ForkJoinMode.WaitAll,
Branches =
{
new Sequence
{
Activities =
{
new WriteLine("Branch 1"),
new Event("Event 1")
}
},
new Sequence
{
Activities =
{
new WriteLine("Branch 2"),
new Finish()
}
}
}
};
}
}
@@ -0,0 +1,19 @@
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class FinishSequentialWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Line 1"),
new Finish(),
new WriteLine("Line 2")
}
};
}
}
@@ -0,0 +1,45 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class FinishTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public FinishTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Subsequent activities are not executed")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<FinishSequentialWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1" }, lines);
}
[Fact(DisplayName = "Workflow status is set to Finished")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync<FinishSequentialWorkflow>();
var workflowState = result.WorkflowState;
Assert.Equal(WorkflowStatus.Finished, workflowState.Status);
Assert.Equal(WorkflowSubStatus.Finished, workflowState.SubStatus);
}
[Fact(DisplayName = "All bookmarks are removed")]
public async Task Test3()
{
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync<FinishSequentialWorkflow>();
Assert.Empty(result.WorkflowState.Bookmarks);
}
}
@@ -0,0 +1,28 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class ForEachTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public ForEachTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "ForEach outputs each iteration")]
public async Task Test1()
{
var items = new[] { "C#", "Rust", "Go"};
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync(new ForEachWorkflow(items));
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(items, lines);
}
}
@@ -0,0 +1,33 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Activities;
class ForEachWorkflow : WorkflowBase
{
private readonly ICollection<string> _items;
public ForEachWorkflow(ICollection<string> items)
{
_items = items;
}
protected override void Build(IWorkflowBuilder workflow)
{
var currentItem = new Variable<string>("CurrentItem", "");
workflow.Root = new Sequence
{
Variables = { currentItem },
Activities =
{
new ForEach<string>
{
Items = new(_items),
CurrentValue = new(currentItem),
Body = new WriteLine(currentItem)
},
}
};
}
}
@@ -0,0 +1,42 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Activities;
class NestedForEachWithBreakWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var outerItems = new[] { "C#", "Rust", "Go" };
var innerItems = new[] { "Classes", "Functions", "Modules" };
var currentOuterItem = new Variable<string>("CurrentOuterItem", "");
var currentInnerItem = new Variable<string>("CurrentInnerItem", "");
workflow.Root = new ForEach<string>(outerItems)
{
CurrentValue = new (currentOuterItem),
Body = new Sequence
{
Activities =
{
new WriteLine(currentOuterItem),
new ForEach<string>(innerItems)
{
CurrentValue = new (currentInnerItem),
Body = new Sequence
{
Activities =
{
new If(context => currentInnerItem.Get(context) == "Functions")
{
Then = new Break()
},
new WriteLine(currentInnerItem)
}
}
}
}
}
};
}
}
@@ -0,0 +1,19 @@
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class BasicForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Fork
{
Branches =
{
new WriteLine("Branch 1"),
new WriteLine("Branch 2"),
new WriteLine("Branch 3")
}
};
}
}
@@ -0,0 +1,54 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Options;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class ForkTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private IServiceProvider _services;
public ForkTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Each branch executes")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<BasicForkWorkflow>();
await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Branch 1", "Branch 2", "Branch 3" }, lines);
}
[Fact(DisplayName = "Wait AnyAsync causes workflow to continue")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<JoinAnyForkWorkflow>();
// First run.
var result = await _workflowRunner.RunAsync(workflow);
// Collect one of the bookmarks to resume the workflow.
var bookmark = result.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Event2");
Assert.NotNull(bookmark);
// Resume the workflow.
var runOptions = new RunWorkflowOptions { BookmarkId = bookmark.Id };
await _workflowRunner.RunAsync(workflow, result.WorkflowState, runOptions);
// Verify output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Branch 2", "End" }, lines);
}
}
@@ -0,0 +1,59 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class JoinAnyForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Start"),
new Fork
{
Branches =
{
new Sequence
{
Activities =
{
new Event("Event 1")
{
Id = "Event1"
},
new WriteLine("Branch 1")
}
},
new Sequence
{
Activities =
{
new Event("Event 2")
{
Id = "Event2"
},
new WriteLine("Branch 2")
}
},
new Sequence
{
Activities =
{
new Event("Event 3")
{
Id = "Event3"
},
new WriteLine("Branch 3")
}
},
},
JoinMode = ForkJoinMode.WaitAny
},
new WriteLine("End")
}
};
}
}
@@ -0,0 +1,59 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class IfTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public IfTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Theory(DisplayName = "The correct branch executes when condition is true")]
[InlineData(true)]
[InlineData(false)]
public async Task Test1(bool conditionResult)
{
var result = default(bool?);
var activity = new If(() => conditionResult)
{
Then = new Inline(() => result = true),
Else = new Inline(() => result = false)
};
await _services.RunActivityAsync(activity);
Assert.Equal(conditionResult, result);
}
[Theory(DisplayName = "The If activity completes only after either one of its branches completed")]
[InlineData(true)]
[InlineData(false)]
public async Task Test2(bool conditionResult)
{
var activity = new If(() => conditionResult)
{
Then = new Inline(),
Else = new Inline()
};
var result = await _services.RunActivityAsync(activity);
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
}
[Fact(DisplayName = "The If activity produces a result when one of its branches completes")]
public async Task Test3()
{
var activity = new If(() => true);
var result = await _services.RunActivityAsync(activity);
var activityResult = result.GetActivityOutput<bool>(activity);
Assert.True(activityResult);
}
}
@@ -0,0 +1,27 @@
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
class NestedSequentialWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Start"),
new Sequence
{
Activities =
{
new WriteLine("Line 1"),
new WriteLine("Line 2"),
new WriteLine("Line 3")
}
},
new WriteLine("End"),
}
};
}
}
@@ -0,0 +1,38 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class SequenceTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public SequenceTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Sequence completes after its child activities complete")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync(new SequentialWorkflow());
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1", "Line 2", "Line 3" }, lines);
}
[Fact(DisplayName = "Sequence completes after its child sequence activity complete")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync(new NestedSequentialWorkflow());
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Line 1", "Line 2", "Line 3", "End" }, lines);
}
}
@@ -0,0 +1,19 @@
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Activities;
class SequentialWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Line 1"),
new WriteLine("Line 2"),
new WriteLine("Line 3")
}
};
}
}
@@ -0,0 +1,30 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Activities;
public class WriteLineTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public WriteLineTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Run a simple workflow")]
public async Task Test1()
{
var expectedOutput = "Hello World!";
var workflow = Workflow.FromActivity(new WriteLine(expectedOutput));
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync(workflow);
var line = _capturingTextWriter.Lines.FirstOrDefault();
Assert.Equal(expectedOutput, line);
}
}
@@ -0,0 +1,29 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.Activities;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Core;
public class ScheduleActivityExecutionContextTests(ITestOutputHelper testOutputHelper)
{
private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
[Fact(DisplayName = "Scheduling an activity that is not part of the workflow should throw an exception")]
public async Task ScheduleActivityAsync_WithActivityNotPartOfWorkflow_ShouldThrowException()
{
await _serviceProvider.PopulateRegistriesAsync();
var writeLineA = new WriteLine("Test");
var writeLineB = new WriteLine("Test");
var workflow = new Workflow
{
Root = writeLineA
};
var workflowGraphBuilder = _serviceProvider.GetRequiredService<IWorkflowGraphBuilder>();
var workflowGraph = await workflowGraphBuilder.BuildAsync(workflow);
var workflowExecutionContext = await WorkflowExecutionContext.CreateAsync(_serviceProvider, workflowGraph, "test");
var activityExecutionContext = await workflowExecutionContext.CreateActivityExecutionContextAsync(writeLineA);
await Assert.ThrowsAsync<InvalidOperationException>(() => activityExecutionContext.ScheduleActivityAsync(writeLineB).AsTask());
}
}
@@ -0,0 +1,72 @@
using System.Text.Json;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Core;
public class SerializerUnicodeEncodingTests(ITestOutputHelper testOutputHelper)
{
private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
[Fact]
public void TestPayloadSerializer()
{
var serializer = _serviceProvider.GetRequiredService<IPayloadSerializer>();
TestSerializer(input => serializer.Serialize(input));
}
[Fact]
public void TestApiSerializer()
{
var serializer = _serviceProvider.GetRequiredService<IApiSerializer>();
TestSerializer(input => serializer.Serialize(input));
}
[Fact]
public void TestBookmarkPayloadSerializer()
{
var serializer = _serviceProvider.GetRequiredService<IBookmarkPayloadSerializer>();
TestSerializer(input => serializer.Serialize(input));
}
[Fact]
public void TestSafeSerializer()
{
var serializer = _serviceProvider.GetRequiredService<ISafeSerializer>();
TestSerializer(input => serializer.Serialize(input));
}
[Fact]
public void TestWorkflowStateSerializer()
{
var serializer = _serviceProvider.GetRequiredService<IWorkflowStateSerializer>();
TestSerializer(input => serializer.Serialize(input));
}
private void TestSerializer(Func<object, string> serialize)
{
var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString();
var anonymousObject = new
{
Text = unicodeString
};
var serializedJson = 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)}");
}
}
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{ca65b7d5-57e8-4932-b8b8-732d040c9b3a}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Proto.Persistence.Sqlite" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.Http\Elsa.Http.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.JavaScript\Elsa.JavaScript.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Runtime.ProtoActor\Elsa.Workflows.Runtime.ProtoActor.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.Scheduling\Elsa.Scheduling.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.WorkflowProviders.BlobStorage\Elsa.WorkflowProviders.BlobStorage.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj" />
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Serialization\**\*.json" CopyToOutputDirectory="PreserveNewest" />
<None Update="Scenarios\**\Workflows\*.json" CopyToOutputDirectory="PreserveNewest" />
<None Update="Scenarios\DependencyWorkflowsPublishing\Workflows\child.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scenarios\DependencyWorkflowsPublishing\Workflows\parent.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cbreak/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cfinish/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cforeach/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cfork/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csequence/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cwriteline/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=break/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=foreach/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=if/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=sequence/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=writeline/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -0,0 +1,7 @@
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityNotificationsMiddleware;
public class Spy
{
public bool ActivityExecutingWasCalled { get; set; }
public bool ActivityExecutedWasCalled { get; set; }
}
@@ -0,0 +1,26 @@
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Notifications;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityNotificationsMiddleware;
public class TestHandler : INotificationHandler<ActivityExecuting>, INotificationHandler<ActivityExecuted>
{
private readonly Spy _spy;
public TestHandler(Spy spy)
{
_spy = spy;
}
public Task HandleAsync(ActivityExecuting notification, CancellationToken cancellationToken)
{
_spy.ActivityExecutingWasCalled = true;
return Task.CompletedTask;
}
public Task HandleAsync(ActivityExecuted notification, CancellationToken cancellationToken)
{
_spy.ActivityExecutedWasCalled = true;
return Task.CompletedTask;
}
}
@@ -0,0 +1,38 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.Notifications;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityNotificationsMiddleware;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly Spy _spy;
public Tests(ITestOutputHelper testOutputHelper)
{
var services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureServices(s =>
{
s.AddSingleton<Spy>();
s.AddNotificationHandler<TestHandler, ActivityExecuting>();
s.AddNotificationHandler<TestHandler, ActivityExecuted>();
})
.Build();
_workflowRunner = services.GetRequiredService<IWorkflowRunner>();
_spy = services.GetRequiredService<Spy>();
}
[Fact(DisplayName = "Running workflow with activity notifications middleware results in notifications being published")]
public async Task Test1()
{
await _workflowRunner.RunAsync<HelloWorldWorkflow>();
Assert.True(_spy.ActivityExecutingWasCalled);
Assert.True(_spy.ActivityExecutedWasCalled);
}
}
@@ -0,0 +1,11 @@
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityNotificationsMiddleware;
class HelloWorldWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new WriteLine("Hello World!");
}
}
@@ -0,0 +1,34 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityOutputs;
public class LoopingWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var readCurrentValue = new Inline<string>(context => context.GetVariable<string>("CurrentValue")!);
builder.Root = new ForEach<string>(
[
"Item 1",
"Item 2",
"Item 3"
])
{
Body = new Sequence
{
Activities =
[
readCurrentValue,
new WriteLine(context =>
{
var currentValue = context.GetVariable<string>("CurrentValue");
var activityResult = context.GetActivityExecutionContext().GetResult(readCurrentValue);
return $"Current value: {currentValue}, Activity result: {activityResult}";
})
]
}
};
}
}
@@ -0,0 +1,27 @@
using Elsa.Extensions;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityOutputs;
public class SumActivity : CodeActivity<int>
{
public SumActivity(Variable<int> a, Variable<int> b)
{
A = new(a);
B = new(b);
}
public Input<int> A { get; set; }
public Input<int> B { get; set; }
protected override void Execute(ActivityExecutionContext context)
{
var input1 = A.Get(context);
var input2 = B.Get(context);
var result = input1 + input2;
// All three work.
Result.Set(context, result);
}
}
@@ -0,0 +1,29 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityOutputs;
public class SumWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var a = new Variable<int>("A", 0);
var b = new Variable<int>("B", 0);
var sumActivity = new SumActivity(a, b);
workflow.Root = new Sequence
{
Variables = { a, b },
Activities =
{
new Inline(context => a.Set(context, 4)),
new Inline(context => b.Set(context, 6)),
sumActivity,
new WriteLine(context => $"The result of {a.Get(context)} and {b.Get(context)} is {sumActivity.GetResult<int>(context)}."),
new WriteLine(context => $"The last result is {context.GetLastResult<int>()}."),
}
};
}
}
@@ -0,0 +1,44 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ActivityOutputs;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Activity outputs can be accessed from other activities.")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<SumWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[]
{
"The result of 4 and 6 is 10.", "The last result is 10."
}, lines);
}
[Fact(DisplayName = "The last activity output is returned.")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<LoopingWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[]
{
"Current value: Item 1, Activity result: Item 1",
"Current value: Item 2, Activity result: Item 2",
"Current value: Item 3, Activity result: Item 3"
}, lines);
}
}
@@ -0,0 +1,50 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Options;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Blocking;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Subsequent activity does not get scheduled when previous activity created a bookmark")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BlockingSequentialWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1" }, lines);
}
[Fact(DisplayName = "Subsequent activities are scheduled when resuming workflow using bookmark")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<BlockingSequentialWorkflow>();
// Start workflow.
var result = await _workflowRunner.RunAsync(workflow);
var bookmark = result.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Resume");
// Resume workflow.
var runOptions = new RunWorkflowOptions { BookmarkId = bookmark!.Id };
await _workflowRunner.RunAsync(workflow, result.WorkflowState, runOptions);
// Verify expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1", "Line 2", "Line 3" }, lines);
}
}
@@ -0,0 +1,21 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Blocking;
class BlockingSequentialWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Line 1"),
new Event("Resume"){ Id = "Resume"},
new WriteLine("Line 2"),
new WriteLine("Line 3")
}
};
}
}
@@ -0,0 +1,88 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Scenarios.BlockingAndBreaking.Workflows;
using Elsa.Workflows.Options;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.BlockingAndBreaking;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Fork completes after all branches complete.")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<WaitAllForkWorkflow>();
// Start workflow.
var result1 = await _workflowRunner.RunAsync(workflow);
// Resume the first branch.
var bookmark = result1.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Branch 1");
var runOptions = new RunWorkflowOptions { BookmarkId = bookmark!.Id };
var result2 = await _workflowRunner.RunAsync(workflow, result1.WorkflowState, runOptions);
// Resume the second branch.
bookmark = result2.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Branch 2");
runOptions = new RunWorkflowOptions { BookmarkId = bookmark!.Id };
var result3 = await _workflowRunner.RunAsync(workflow, result2.WorkflowState, runOptions);
// Verify expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Branch 1", "Branch 2", "Branch 1 - Resumed", "Branch 2 - Resumed", "End" }, lines);
}
[Fact(DisplayName = "Fork completes after any branch completes.")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<WaitAnyForkWorkflow>();
// Start workflow.
var result1 = await _workflowRunner.RunAsync(workflow);
// Resume the first branch.
var bookmark = result1.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Branch 1");
var runOptions = new RunWorkflowOptions { BookmarkId = bookmark!.Id };
var result2 = await _workflowRunner.RunAsync(workflow, result1.WorkflowState, runOptions);
// Verify expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Branch 1", "Branch 2", "Branch 1 - Resumed", "End" }, lines);
}
[Fact(DisplayName = "Break breaks out of While loop.")]
public async Task Test3()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<BreakWhileFromForkWorkflow>();
// Start workflow.
var result1 = await _workflowRunner.RunAsync(workflow);
// Resume the first branch.
var bookmark = result1.WorkflowState.Bookmarks.FirstOrDefault(x => x.ActivityId == "Branch 1");
var runOptions = new RunWorkflowOptions { BookmarkId = bookmark!.Id };
var result2 = await _workflowRunner.RunAsync(workflow, result1.WorkflowState, runOptions);
// There should be no bookmarks left, since the while loop was broken out of.
Assert.Empty(result2.WorkflowState.Bookmarks);
// Verify expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Branch 1", "Branch 2", "Branch 1 - Resumed", "End" }, lines);
}
}
@@ -0,0 +1,61 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.BlockingAndBreaking.Workflows;
public class BreakWhileFromForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", 0);
workflow.WithVariable(currentValue);
workflow.Root = new Sequence
{
Activities =
{
new WriteLine("Start"),
new While(() => true)
{
Body = new Sequence
{
Activities =
{
new Fork
{
JoinMode = ForkJoinMode.WaitAll,
Branches =
{
new Sequence
{
Activities =
{
new WriteLine("Branch 1"),
new Event("Branch 1") { Id = "Branch 1" },
new WriteLine("Branch 1 - Resumed"),
// This should break the while loop, not matter how high up in the tree it is.
new Break()
}
},
new Sequence
{
Activities =
{
new WriteLine("Branch 2"),
new Event("Branch 2") { Id = "Branch 2" },
new WriteLine("Branch 2 - Resumed"),
}
}
}
}
}
}
},
new WriteLine("End")
}
};
}
}
@@ -0,0 +1,50 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.BlockingAndBreaking.Workflows;
public class WaitAllForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", 0);
workflow.WithVariable(currentValue);
workflow.Root =
new Sequence
{
Activities =
{
new WriteLine("Start"),
new Fork
{
JoinMode = ForkJoinMode.WaitAll,
Branches =
{
new Sequence
{
Activities =
{
new WriteLine("Branch 1"),
new Event("Branch 1") { Id = "Branch 1" },
new WriteLine("Branch 1 - Resumed"),
}
},
new Sequence
{
Activities =
{
new WriteLine("Branch 2"),
new Event("Branch 2") { Id = "Branch 2" },
new WriteLine("Branch 2 - Resumed"),
}
}
}
},
new WriteLine("End")
}
};
}
}
@@ -0,0 +1,50 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.BlockingAndBreaking.Workflows;
public class WaitAnyForkWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var currentValue = new Variable<int?>("CurrentValue", 0);
workflow.WithVariable(currentValue);
workflow.Root =
new Sequence
{
Activities =
{
new WriteLine("Start"),
new Fork
{
JoinMode = ForkJoinMode.WaitAny,
Branches =
{
new Sequence
{
Activities =
{
new WriteLine("Branch 1"),
new Event("Branch 1") { Id = "Branch 1" },
new WriteLine("Branch 1 - Resumed"),
}
},
new Sequence
{
Activities =
{
new WriteLine("Branch 2"),
new Event("Branch 2") { Id = "Branch 2" },
new WriteLine("Branch 2 - Resumed"),
}
}
}
},
new WriteLine("End")
}
};
}
}
@@ -0,0 +1,33 @@
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CanExecute.Activities;
public class CustomActivity : CodeActivity
{
public CustomActivity(int magicNumber)
{
MagicNumber = new (magicNumber);
}
public CustomActivity(Func<ExpressionExecutionContext, int> magicNumber)
{
MagicNumber = new (magicNumber);
}
public Input<int> MagicNumber { get; set; }
protected override bool CanExecute(ActivityExecutionContext context)
{
var magicNumber = MagicNumber.Get(context);
return magicNumber == 42;
}
protected override void Execute(ActivityExecutionContext context)
{
var provider = context.GetService<IStandardOutStreamProvider>() ?? new StandardOutStreamProvider(Console.Out);
var textWriter = provider.GetTextWriter();
textWriter.WriteLine("Welcome to the world of Might and Magic!");
}
}
@@ -0,0 +1,43 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Scenarios.CanExecute.Activities;
using Elsa.Workflows.IntegrationTests.Scenarios.CanExecute.Workflows;
using Elsa.Workflows.Options;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CanExecute;
public class CanExecuteTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
private readonly IWorkflowRunner _workflowRunner;
public CanExecuteTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.AddActivitiesFrom<CustomActivity>()
.Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Theory(DisplayName = "Activities are executed only when they report that they can execute.")]
[InlineData(12, "Magic number is 12")]
[InlineData(42, "Magic number is 42\nWelcome to the world of Might and Magic!\nDone")]
public async Task Test1(int magicNumber, string expectedLines)
{
await _services.PopulateRegistriesAsync();
var runOptions = new RunWorkflowOptions
{
Input = new Dictionary<string, object>
{
["MagicNumber"] = magicNumber
}
};
await _workflowRunner.RunAsync<MagicWorkflow>(runOptions);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(expectedLines.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries), lines);
}
}
@@ -0,0 +1,24 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.IntegrationTests.Scenarios.CanExecute.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CanExecute.Workflows;
public class MagicWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var magicNumberVariable = builder.WithVariable<int>();
builder.Root = new Sequence
{
Activities =
{
new SetVariable<int>(magicNumberVariable, context => context.GetInput<int>("MagicNumber")),
new WriteLine(context => $"Magic number is {magicNumberVariable.Get(context)}"),
new CustomActivity(context => magicNumberVariable.Get(context)),
new WriteLine("Done")
}
};
}
}
@@ -0,0 +1,38 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Composites;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureElsa(elsa => elsa.UseJavaScript())
.Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "The main workflow can capture the result of the composite activity")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<SumWorkflow>();
// Start workflow.
await _workflowRunner.RunAsync(workflow);
// Verify expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Sum: 3" }, lines);
}
}
@@ -0,0 +1,72 @@
using Elsa.Extensions;
using Elsa.JavaScript.Activities;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Composites;
/// <summary>
/// A composite activity that sums two inputs and returns the result.
/// </summary>
public class Sum : Composite<int>
{
private readonly RunJavaScript _runJavaScript;
public Input<int> A { get; set; } = null!;
public Input<int> B { get; set; } = null!;
public Sum()
{
_runJavaScript = new()
{
Script = new("getA() + getB();"),
};
Root = new Sequence
{
Activities =
{
_runJavaScript
}
};
}
protected override void ConfigureActivities(ActivityExecutionContext context)
{
// Copy input into variables so that JS activity can access them.
var a = A.Get(context);
var b = B.Get(context);
Variables.Add(new Variable<int>("A", a));
Variables.Add(new Variable<int>("B", b));
// If the call site set a result variable, assign it to the JavaScript activity's result.
if (Result != null)
_runJavaScript.Result = new(Result.MemoryBlockReference)!;
}
}
/// <summary>
/// A workflow that uses the <see cref="Sum"/> composite activity.
/// </summary>
public class SumWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var sum = workflow.WithVariable<int>("Sum", 0);
workflow.Root = new Sequence
{
Activities =
{
new Sum
{
A = new(1),
B = new(2),
Result = new(sum)
},
new WriteLine(context => $"Sum: {sum.Get(context)}")
}
};
}
}
@@ -0,0 +1,36 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CompositesAndOutcomes;
public class ExplicitJoinWaitAnyTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public ExplicitJoinWaitAnyTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.WithWorkflowsFromDirectory("Scenarios", "CompositesAndOutcomes", "Workflows")
.Build();
}
[Fact(DisplayName = "Complete activity must not cascade.")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Execute.
var workflowState = await _services.RunWorkflowUntilEndAsync("tester");
// Assert expected output.
var lines = _capturingTextWriter.Lines.ToList();
var expectedLinesArray = new[] { "Start", "End" };
Assert.Equal(expectedLinesArray, lines);
// Assert expected workflow status.
Assert.Equal(WorkflowStatus.Finished, workflowState.Status);
}
}
@@ -0,0 +1,71 @@
{
"id": "3457460b3fd74daa8f898564cf96fe48",
"definitionId": "99c1c6cf2fec47b3b4916a01425ec989",
"name": "Completor",
"createdAt": "2023-09-14T13:49:38.017111+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [
"Success",
"Failure"
],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true,
"activityCategory": "Testing"
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Gn-5gEC2-UCmmzWTgvYCmw",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"outcomes": {
"typeName": "Object",
"expression": {
"type": "Literal",
"value": "Success"
},
"memoryReference": {
"id": "tyDvLNGiYkCbUabhgzcSdw:input-0"
}
},
"id": "tyDvLNGiYkCbUabhgzcSdw",
"name": "Complete1",
"type": "Elsa.Complete",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -186.5,
"y": -263
},
"size": {
"width": 136.8125,
"height": 50
}
}
}
}
],
"connections": []
}
}
@@ -0,0 +1,62 @@
{
"id": "9b6b4d4a6a8741c58ccbe3911eed7821",
"definitionId": "bc7f148b2a5b4f55811537a0d762e137",
"name": "Consumer",
"createdAt": "2023-09-14T13:50:13.31251+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true,
"activityCategory": "Testing"
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "v-ii0Vp0yUCPWReLNUiosg",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"workflowDefinitionId": "99c1c6cf2fec47b3b4916a01425ec989",
"workflowDefinitionVersionId": "3457460b3fd74daa8f898564cf96fe48",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "3457460b3fd74daa8f898564cf96fe48",
"id": "ctusr1Uv40uFqIws9Ymt9A",
"name": "Completor1",
"type": "Completor",
"version": 2,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 20,
"y": -260
},
"size": {
"width": 106.859375,
"height": 116.015625
}
}
}
}
],
"connections": []
}
}
@@ -0,0 +1,145 @@
{
"id": "74144d614208497380a7bae783b78626",
"definitionId": "tester",
"name": "Tester",
"createdAt": "2023-09-14T13:50:38.720025+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "k0HQeDE_gk-OxcsjGj3tbQ",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "35MeNlWlAUKXd5JFDJtJ3w:input-0"
}
},
"id": "35MeNlWlAUKXd5JFDJtJ3w",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -198,
"y": -246.9921875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "End"
},
"memoryReference": {
"id": "CvdSrKs5DESezhkLjb0YIw:input-0"
}
},
"id": "CvdSrKs5DESezhkLjb0YIw",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 253,
"y": -246.9921875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"workflowDefinitionId": "bc7f148b2a5b4f55811537a0d762e137",
"workflowDefinitionVersionId": "9b6b4d4a6a8741c58ccbe3911eed7821",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "9b6b4d4a6a8741c58ccbe3911eed7821",
"id": "QfkYnRZDxEO0rtBTIbTXzg",
"name": "Consumer1",
"type": "Consumer",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 20,
"y": -280
},
"size": {
"width": 105.5625,
"height": 116.015625
}
}
}
}
],
"connections": [
{
"source": {
"activity": "35MeNlWlAUKXd5JFDJtJ3w",
"port": "Done"
},
"target": {
"activity": "QfkYnRZDxEO0rtBTIbTXzg",
"port": "In"
}
},
{
"source": {
"activity": "QfkYnRZDxEO0rtBTIbTXzg",
"port": "Done"
},
"target": {
"activity": "CvdSrKs5DESezhkLjb0YIw",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,38 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CompositesPassingData;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureElsa(elsa => elsa.UseJavaScript())
.Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "The main workflow can capture the result of the composite activity")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<AddTextMainWorkflow>();
// Start workflow.
await _workflowRunner.RunAsync(workflow);
// Verify expected output.
var line = _capturingTextWriter.Lines.ToList().Last();
Assert.Equal("hi there obi wan", line);
}
}
@@ -0,0 +1,61 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Management.Activities.SetOutput;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.CompositesPassingData;
/// <summary>
/// A composite activity that adds text to a string
/// </summary>
public class AddTextSubWorkflow : Composite
{
public Input<string> A { get; set; } = default!;
public Output<string> B { get; set; } = default!;
public AddTextSubWorkflow()
{
var setOutput = new SetOutput()
{
OutputName = new Input<string>("B"),
OutputValue = new Input<object?>(context => "hi there " + A.Get(context))
};
Root = new Sequence
{
Activities =
{
setOutput
}
};
}
}
/// <summary>
/// A workflow that uses the <see cref="AddTextSubWorkflow"/> composite activity.
/// </summary>
public class AddTextMainWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var outVariable = new Variable<string>("out", "test");
var subWorkflow = new AddTextSubWorkflow()
{
A = new("obi wan"),
B = new (outVariable)
};
workflow.Variables.Add(outVariable);
workflow.Root = new Sequence
{
Activities =
{
subWorkflow,
new WriteLine(context => outVariable.Get(context))
}
};
}
}
@@ -0,0 +1,33 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.DependencyWorkflows;
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.WithWorkflowsFromDirectory("Scenarios", "DependencyWorkflows", "Workflows")
.Build();
}
[Fact(DisplayName = "Workflows provided from JSON files that depend on other workflows are executed correctly.")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Run the "matter" workflow.
await _services.RunWorkflowUntilEndAsync("matter-workflow");
// Assert.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Atom", "Atom", "Atom", "Atom", "Atom", "Atom", "Atom", "Atom", "Atom" }, lines);
}
}
@@ -0,0 +1,61 @@
{
"id": "82b53ccf2a4e4ced966b85fb3cc13eed",
"definitionId": "40a40a0cdf5448acbc899391b2c50458",
"name": "AtomActivity",
"createdAt": "2023-06-01T14:00:29.374132+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"usableAsActivity": true,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": [],
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"start": "Atom",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Atom"
},
"memoryReference": {
"id": "65ee0198-2051-491d-9035-6d05e73fbd08"
}
},
"id": "Atom",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2394,
"y": 2472
}
},
"displayText": "Atom"
}
}
],
"connections": []
}
}
@@ -0,0 +1,105 @@
{
"id": "matter-workflow-v1",
"definitionId": "matter-workflow",
"name": "Matter Workflow",
"createdAt": "2023-06-01T14:06:25.895316+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isLatest": true,
"isPublished": true,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": [],
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"start": "MoleculeActivity2",
"activities": [
{
"workflowDefinitionId": "86e355d944764f15872f8cfdcc1e9cec",
"workflowDefinitionVersionId": "fec9de7279a440f693a5901507140a77",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "fec9de7279a440f693a5901507140a77",
"id": "MoleculeActivity1",
"type": "MoleculeActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2347,
"y": 2407
}
}
}
},
{
"workflowDefinitionId": "86e355d944764f15872f8cfdcc1e9cec",
"workflowDefinitionVersionId": "fec9de7279a440f693a5901507140a77",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "fec9de7279a440f693a5901507140a77",
"id": "MoleculeActivity3",
"type": "MoleculeActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2560,
"y": 2560
}
}
}
},
{
"workflowDefinitionId": "86e355d944764f15872f8cfdcc1e9cec",
"workflowDefinitionVersionId": "fec9de7279a440f693a5901507140a77",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "fec9de7279a440f693a5901507140a77",
"id": "MoleculeActivity2",
"type": "MoleculeActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2140,
"y": 2560
}
}
}
}
],
"connections": [
{
"source": "MoleculeActivity2",
"target": "MoleculeActivity1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "MoleculeActivity1",
"target": "MoleculeActivity3",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,109 @@
{
"id": "fec9de7279a440f693a5901507140a77",
"definitionId": "86e355d944764f15872f8cfdcc1e9cec",
"name": "MoleculeActivity",
"createdAt": "2023-06-01T14:05:32.409643+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": [],
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"start": "AtomActivity1",
"activities": [
{
"workflowDefinitionId": "40a40a0cdf5448acbc899391b2c50458",
"workflowDefinitionVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"id": "AtomActivity1",
"type": "AtomActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2228,
"y": 2418
}
}
}
},
{
"workflowDefinitionId": "40a40a0cdf5448acbc899391b2c50458",
"workflowDefinitionVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"id": "AtomActivity2",
"type": "AtomActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2458,
"y": 2487
}
}
}
},
{
"workflowDefinitionId": "40a40a0cdf5448acbc899391b2c50458",
"workflowDefinitionVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "82b53ccf2a4e4ced966b85fb3cc13eed",
"id": "AtomActivity3",
"type": "AtomActivity",
"version": 1,
"customProperties": {
"CanStartWorkflow": false,
"RunAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 2660,
"y": 2560
}
}
}
}
],
"connections": [
{
"source": "AtomActivity1",
"target": "AtomActivity2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "AtomActivity2",
"target": "AtomActivity3",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,71 @@
using Elsa.Common.Models;
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
using Elsa.Workflows.Management.Entities;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.DependencyWorkflowsPublishing;
/// <summary>
/// Contains tests for the "DependencyWorkflowsPublishing" scenario.
/// </summary>
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
private readonly IWorkflowDefinitionPublisher _workflowDefinitionPublisher;
private readonly IActivitySerializer _activitySerializer;
private readonly IActivityVisitor _activityVisitor;
/// <summary>
/// Initializes a new instance of the <see cref="Tests"/> class.
/// </summary>
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.WithWorkflowsFromDirectory("Scenarios", "DependencyWorkflowsPublishing", "Workflows")
.Build();
_workflowDefinitionPublisher = _services.GetRequiredService<IWorkflowDefinitionPublisher>();
_activitySerializer = _services.GetRequiredService<IActivitySerializer>();
_activityVisitor = _services.GetRequiredService<IActivityVisitor>();
}
/// <summary>
/// When a dependency workflow is published, all consuming workflows are updated to point to the new version of the dependency.
/// </summary>
[Fact(DisplayName = "When a dependency workflow is published, all consuming workflows are updated to point to the new version of the dependency.")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var childDefinitionV1 = await _services.GetWorkflowDefinitionAsync("child", VersionOptions.Latest);
var parentDefinition = await _services.GetWorkflowDefinitionAsync("parent", VersionOptions.Latest);
var childActivityV1 = await GetChildActivityAsync(parentDefinition);
// Assert initial state.
Assert.Equal(1, childDefinitionV1.Version);
Assert.False(parentDefinition.IsPublished);
Assert.Equal(1, childActivityV1.Version);
// Create a new draft for the child workflow and publish it.
var childDefinitionV2 = (await _workflowDefinitionPublisher.GetDraftAsync(childDefinitionV1.DefinitionId, VersionOptions.Published))!;
await _workflowDefinitionPublisher.PublishAsync(childDefinitionV2);
// Assert that the parent workflow now points to the new version of the child workflow.
parentDefinition = await _services.GetWorkflowDefinitionAsync("parent", VersionOptions.Latest);
var childActivityV2 = await GetChildActivityAsync(parentDefinition);
Assert.Equal(2, childActivityV2.Version);
}
private async Task<WorkflowDefinitionActivity> GetChildActivityAsync(WorkflowDefinition parent)
{
var root = _activitySerializer.Deserialize(parent.StringData!);
var graph = await _activityVisitor.VisitAsync(root);
var flattenedList = graph.Flatten().ToList();
return (WorkflowDefinitionActivity)flattenedList.Single(x => x.Activity is WorkflowDefinitionActivity { WorkflowDefinitionId: "child" }).Activity;
}
}
@@ -0,0 +1,37 @@
{
"id": "child-v1",
"definitionId": "child",
"name": "Child",
"createdAt": "2024-02-07T14:57:47.115341+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "c22918340b6cb7cd",
"nodeId": "Workflow1:c22918340b6cb7cd",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [],
"connections": []
}
}
@@ -0,0 +1,64 @@
{
"id": "ffd312ccf7a2af4f",
"definitionId": "parent",
"name": "Parent",
"createdAt": "2024-02-07T14:58:12.954939+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": false,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "3535b9598a0962f0",
"nodeId": "Workflow1:3535b9598a0962f0",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"workflowDefinitionId": "child",
"workflowDefinitionVersionId": "child-v1",
"latestAvailablePublishedVersion": 1,
"latestAvailablePublishedVersionId": "child-v1",
"id": "35525d3b78b8e518",
"nodeId": "Workflow1:3535b9598a0962f0:35525d3b78b8e518",
"name": "Child1",
"type": "Child",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -95.17782592773438,
"y": -137.3333282470703
},
"size": {
"width": 67.78125,
"height": 50
}
}
}
}
],
"connections": []
}
}
@@ -0,0 +1,42 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ExplicitJoins;
public class ExplicitJoinWaitAnyTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public ExplicitJoinWaitAnyTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.Build();
}
[Theory(DisplayName = "Workflows with explicit joins complete the workflow.")]
[InlineData("join-any-1.json", "Start; End")]
[InlineData("join-all-1.json", "Start; Line 1; Line 2; End")]
[InlineData("join-all-2.json", "Start; Line 1; Line 2; End")]
public async Task Test1(string filename, string expectedLines)
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var fileName = $"Scenarios/ExplicitJoins/Workflows/{filename}";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(fileName);
// Execute.
var workflowState = await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Assert expected output.
var lines = _capturingTextWriter.Lines.ToList();
var expectedLinesArray = expectedLines.Split(";", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList();
Assert.Equal(expectedLinesArray, lines);
// Assert expected workflow status.
Assert.Equal(WorkflowStatus.Finished, workflowState.Status);
}
}
@@ -0,0 +1,248 @@
{
"id": "9948e541b0e540c384c6eb9fdcf1d544",
"definitionId": "296428cc22bd4f309e858f4b461dbb3d",
"name": "Join 1",
"createdAt": "2023-09-12T18:12:54.370149+00:00",
"version": 2,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "sL581qWiIEOpRncv5Ez33A",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAll"
},
"memoryReference": {
"id": "aA4pfJOzyEqmLOY3U3vCoQ:input-0"
}
},
"id": "aA4pfJOzyEqmLOY3U3vCoQ",
"name": "FlowJoin1",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 198,
"y": -266
},
"size": {
"width": 136.859375,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "FRXh0PlHekejZiWANAplUg:input-0"
}
},
"id": "FRXh0PlHekejZiWANAplUg",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -287,
"y": -266
},
"size": {
"width": 102.21875,
"height": 50
}
},
"displayText": "Start"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Line 1"
},
"memoryReference": {
"id": "GaVrCWqBDkS6YrvydxKvFA:input-0"
}
},
"id": "GaVrCWqBDkS6YrvydxKvFA",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -40,
"y": -316
},
"size": {
"width": 110.65625,
"height": 50
}
},
"displayText": "Line 1"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Line 2"
},
"memoryReference": {
"id": "9Z7bt98j00uRMt9TS2FD0g:input-0"
}
},
"id": "9Z7bt98j00uRMt9TS2FD0g",
"name": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -40,
"y": -207
},
"size": {
"width": 110.65625,
"height": 50
}
},
"displayText": "Line 2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "End"
},
"memoryReference": {
"id": "MplDrS_bUE-8td-XBUvWmA:input-0"
}
},
"id": "MplDrS_bUE-8td-XBUvWmA",
"name": "WriteLine4",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 430,
"y": -266
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "FRXh0PlHekejZiWANAplUg",
"port": "Done"
},
"target": {
"activity": "GaVrCWqBDkS6YrvydxKvFA",
"port": "In"
}
},
{
"source": {
"activity": "FRXh0PlHekejZiWANAplUg",
"port": "Done"
},
"target": {
"activity": "9Z7bt98j00uRMt9TS2FD0g",
"port": "In"
}
},
{
"source": {
"activity": "9Z7bt98j00uRMt9TS2FD0g",
"port": "Done"
},
"target": {
"activity": "aA4pfJOzyEqmLOY3U3vCoQ",
"port": "In"
}
},
{
"source": {
"activity": "GaVrCWqBDkS6YrvydxKvFA",
"port": "Done"
},
"target": {
"activity": "aA4pfJOzyEqmLOY3U3vCoQ",
"port": "In"
}
},
{
"source": {
"activity": "aA4pfJOzyEqmLOY3U3vCoQ",
"port": "Done"
},
"target": {
"activity": "MplDrS_bUE-8td-XBUvWmA",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,287 @@
{
"id": "14984c048db34cceaf5e15ece80f9703",
"definitionId": "64f8f2c852f04dc28f60707034d7a906",
"name": "Join 2",
"createdAt": "2023-09-12T18:12:43.392272+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "B2jpPDriPEe-zSOhVuAGeQ",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "2yPLgUudQEGPaschdLdkGg:input-0"
}
},
"id": "2yPLgUudQEGPaschdLdkGg",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -358.5,
"y": -229
},
"size": {
"width": 139.296875,
"height": 50
}
},
"displayText": "Start"
}
},
{
"body": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "zaAIlUtQDEaPqeITeRJ4_w",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:46",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Line 2"
},
"memoryReference": {
"id": "g3jVTdvU3EK7V5JqRqfqjQ:input-0"
}
},
"id": "g3jVTdvU3EK7V5JqRqfqjQ",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -279.5,
"y": -294
},
"size": {
"width": 139.296875,
"height": 50
}
},
"displayText": "Line 2"
}
}
],
"connections": []
},
"id": "eHAEEQ-Iu0GwM3mRxZin0Q",
"name": "FlowNode1",
"type": "Elsa.FlowNode",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -120,
"y": -179
},
"size": {
"width": 211.15625,
"height": 120
}
},
"displayText": "Line 2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Line 1"
},
"memoryReference": {
"id": "7RQ2sbVHo0SxsyK0dvTKEA:input-0"
}
},
"id": "7RQ2sbVHo0SxsyK0dvTKEA",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -120,
"y": -279
},
"size": {
"width": 139.296875,
"height": 50
}
},
"displayText": "Line 1"
}
},
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAll"
},
"memoryReference": {
"id": "dVUuwMm5XUiYpiYoGo5JKA:input-0"
}
},
"id": "dVUuwMm5XUiYpiYoGo5JKA",
"name": "FlowJoin1",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 180.5,
"y": -229
},
"size": {
"width": 136.859375,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "End"
},
"memoryReference": {
"id": "NpZ_yT_irUuE9ASEfYLxbw:input-0"
}
},
"id": "NpZ_yT_irUuE9ASEfYLxbw",
"name": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 400,
"y": -229
},
"size": {
"width": 139.296875,
"height": 50
}
},
"displayText": "End"
}
}
],
"connections": [
{
"source": {
"activity": "2yPLgUudQEGPaschdLdkGg",
"port": "Done"
},
"target": {
"activity": "7RQ2sbVHo0SxsyK0dvTKEA",
"port": "In"
}
},
{
"source": {
"activity": "2yPLgUudQEGPaschdLdkGg",
"port": "Done"
},
"target": {
"activity": "eHAEEQ-Iu0GwM3mRxZin0Q",
"port": "In"
}
},
{
"source": {
"activity": "7RQ2sbVHo0SxsyK0dvTKEA",
"port": "Done"
},
"target": {
"activity": "dVUuwMm5XUiYpiYoGo5JKA",
"port": "In"
}
},
{
"source": {
"activity": "eHAEEQ-Iu0GwM3mRxZin0Q",
"port": "Done"
},
"target": {
"activity": "dVUuwMm5XUiYpiYoGo5JKA",
"port": "In"
}
},
{
"source": {
"activity": "dVUuwMm5XUiYpiYoGo5JKA",
"port": "Done"
},
"target": {
"activity": "NpZ_yT_irUuE9ASEfYLxbw",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,245 @@
{
"id": "2484d1b14e2348f7a916ecef042536e6",
"definitionId": "7204b870946b4968aa786a2c91431242",
"name": "Flow Join Any",
"createdAt": "2023-09-08T19:07:08.435872+00:00",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": false,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "F44h770hvkSaOWI_jjbM_A",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"NotFoundConnectionsKey": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAny"
},
"memoryReference": {
"id": "ImdIbHXtHkiLlrExFlW51A:input-0"
}
},
"id": "ImdIbHXtHkiLlrExFlW51A",
"name": "FlowJoin1",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -180,
"y": -320
},
"size": {
"width": 136.859375,
"height": 50
}
}
}
},
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAny"
},
"memoryReference": {
"id": "cxnS1whoCESvwwPyBnno_Q:input-0"
}
},
"id": "cxnS1whoCESvwwPyBnno_Q",
"name": "FlowJoin2",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -180,
"y": -80
},
"size": {
"width": 136.859375,
"height": 50
}
}
}
},
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAny"
},
"memoryReference": {
"id": "H0apwXYdoESn2TJ5T_3xAg:input-0"
}
},
"id": "H0apwXYdoESn2TJ5T_3xAg",
"name": "FlowJoin3",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 40,
"y": -194
},
"size": {
"width": 136.859375,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "Kn9d-EVn2EiaYJqHd8VcmQ:input-0"
}
},
"id": "Kn9d-EVn2EiaYJqHd8VcmQ",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -393.5,
"y": -194
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "End"
},
"memoryReference": {
"id": "haHoM3A5EU6aYu_8dCYaYg:input-0"
}
},
"id": "haHoM3A5EU6aYu_8dCYaYg",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 310.5,
"y": -194
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "Kn9d-EVn2EiaYJqHd8VcmQ",
"port": "Done"
},
"target": {
"activity": "ImdIbHXtHkiLlrExFlW51A",
"port": "In"
}
},
{
"source": {
"activity": "Kn9d-EVn2EiaYJqHd8VcmQ",
"port": "Done"
},
"target": {
"activity": "cxnS1whoCESvwwPyBnno_Q",
"port": "In"
}
},
{
"source": {
"activity": "ImdIbHXtHkiLlrExFlW51A",
"port": "Done"
},
"target": {
"activity": "H0apwXYdoESn2TJ5T_3xAg",
"port": "In"
}
},
{
"source": {
"activity": "cxnS1whoCESvwwPyBnno_Q",
"port": "Done"
},
"target": {
"activity": "H0apwXYdoESn2TJ5T_3xAg",
"port": "In"
}
},
{
"source": {
"activity": "H0apwXYdoESn2TJ5T_3xAg",
"port": "Done"
},
"target": {
"activity": "haHoM3A5EU6aYu_8dCYaYg",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,41 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartCompletion;
/// <summary>
/// Tests for the flowchart completion feature using various workflow setups.
/// </summary>
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
}
[Theory(DisplayName = "Workflows should complete successfully.")]
[InlineData("workflow1.json")]
[InlineData("workflow2.json")]
[InlineData("workflow3.json")]
[InlineData("workflow4.json")]
[InlineData("workflow5.json")]
[InlineData("workflow6.json")]
public async Task Test1(string workflowFileName)
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var fileName = $"Scenarios/FlowchartCompletion/Workflows/{workflowFileName}";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(fileName);
// Execute.
var workflowState = await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Assert that the workflow has completed.
Assert.Equal(WorkflowStatus.Finished, workflowState.Status);
}
}
@@ -0,0 +1,127 @@
{
"id": "f7f7563b32de444797363df61e5109d3",
"definitionId": "d91eff09eaad43efb9d58332f1f22c82",
"name": "Flows",
"createdAt": "2023-04-18T13:32:26.294066+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": false,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine1"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2020,
"y": 2320
}
},
"displayText": "WriteLine1"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine2"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2260,
"y": 2260
}
},
"displayText": "WriteLine2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine3"
},
"memoryReference": {
"id": "WriteLine3:input-1"
}
},
"id": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2260,
"y": 2380
}
},
"displayText": "WriteLine3"
}
}
],
"connections": [
{
"source": "WriteLine1",
"target": "WriteLine2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine1",
"target": "WriteLine3",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,361 @@
{
"id": "dc6576d848aa4f46812356467575705f",
"definitionId": "991a4f39f7074a9988465230cfdb8d81",
"name": "Braided flow",
"createdAt": "2023-04-18T13:45:03.454228+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": false,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine7"
},
"memoryReference": {
"id": "WriteLine7:input-1"
}
},
"id": "WriteLine7",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2340,
"y": 2220
}
},
"displayText": "WriteLine7"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine9"
},
"memoryReference": {
"id": "WriteLine9:input-1"
}
},
"id": "WriteLine9",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2560,
"y": 2320
}
},
"displayText": "WriteLine9"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine1"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1600,
"y": 2320
}
},
"displayText": "WriteLine1"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine2"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2220
}
},
"displayText": "WriteLine2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine3"
},
"memoryReference": {
"id": "WriteLine3:input-1"
}
},
"id": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2400
}
},
"displayText": "WriteLine3"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine4"
},
"memoryReference": {
"id": "WriteLine4:input-1"
}
},
"id": "WriteLine4",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2160
}
},
"displayText": "WriteLine4"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine5"
},
"memoryReference": {
"id": "WriteLine5:input-1"
}
},
"id": "WriteLine5",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2320
}
},
"displayText": "WriteLine5"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine6"
},
"memoryReference": {
"id": "WriteLine6:input-1"
}
},
"id": "WriteLine6",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2480
}
},
"displayText": "WriteLine6"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine8"
},
"memoryReference": {
"id": "WriteLine8:input-1"
}
},
"id": "WriteLine8",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2340,
"y": 2400
}
},
"displayText": "WriteLine8"
}
}
],
"connections": [
{
"source": "WriteLine5",
"target": "WriteLine7",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine8",
"target": "WriteLine9",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine7",
"target": "WriteLine9",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine1",
"target": "WriteLine2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine4",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "WriteLine5",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine1",
"target": "WriteLine3",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine4",
"target": "WriteLine7",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine5",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine6",
"target": "WriteLine8",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "WriteLine6",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine5",
"target": "WriteLine8",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,469 @@
{
"id": "01146119d31041c195148662710d95c7",
"definitionId": "abb565fd716f4eb7be0510ab8ff36329",
"name": "Braided delayed flow",
"createdAt": "2023-04-18T14:06:58.250625+00:00",
"version": 4,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": false,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine1"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1600,
"y": 2320
}
},
"displayText": "WriteLine1"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine2"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2220
}
},
"displayText": "WriteLine2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine3"
},
"memoryReference": {
"id": "WriteLine3:input-1"
}
},
"id": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2400
}
},
"displayText": "WriteLine3"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine4"
},
"memoryReference": {
"id": "WriteLine4:input-1"
}
},
"id": "WriteLine4",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2160
}
},
"displayText": "WriteLine4"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine5"
},
"memoryReference": {
"id": "WriteLine5:input-1"
}
},
"id": "WriteLine5",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2320
}
},
"displayText": "WriteLine5"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine6"
},
"memoryReference": {
"id": "WriteLine6:input-1"
}
},
"id": "WriteLine6",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2480
}
},
"displayText": "WriteLine6"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine8"
},
"memoryReference": {
"id": "WriteLine8:input-1"
}
},
"id": "WriteLine8",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2460,
"y": 2400
}
},
"displayText": "WriteLine8"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine7"
},
"memoryReference": {
"id": "WriteLine7:input-1"
}
},
"id": "WriteLine7",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2460,
"y": 2240
}
},
"displayText": "WriteLine7"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine9"
},
"memoryReference": {
"id": "WriteLine9:input-1"
}
},
"id": "WriteLine9",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2680,
"y": 2300
}
},
"displayText": "WriteLine9"
}
},
{
"timeSpan": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay1:input-1"
}
},
"strategy": {
"typeName": "Elsa.Scheduling.Models.DelayBlockingStrategy, Elsa.Scheduling",
"expression": {
"type": "Literal",
"value": "NonBlocking"
},
"memoryReference": {
"id": "Delay1:input-2"
}
},
"autoBlockingThreshold": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay1:input-3"
}
},
"id": "Delay1",
"type": "Elsa.Delay",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2260,
"y": 2240
}
}
}
},
{
"timeSpan": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:10"
},
"memoryReference": {
"id": "Delay2:input-1"
}
},
"strategy": {
"typeName": "Elsa.Scheduling.Models.DelayBlockingStrategy, Elsa.Scheduling",
"expression": {
"type": "Literal",
"value": "NonBlocking"
},
"memoryReference": {
"id": "Delay2:input-2"
}
},
"autoBlockingThreshold": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay2:input-3"
}
},
"id": "Delay2",
"type": "Elsa.Delay",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2280,
"y": 2440
}
}
}
}
],
"connections": [
{
"source": "WriteLine1",
"target": "WriteLine2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine4",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "WriteLine5",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine1",
"target": "WriteLine3",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine5",
"target": "WriteLine7",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine5",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine6",
"target": "Delay2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "WriteLine6",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine8",
"target": "WriteLine9",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "Delay2",
"target": "WriteLine8",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine5",
"target": "WriteLine8",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "Delay1",
"target": "WriteLine7",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine7",
"target": "WriteLine9",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine4",
"target": "Delay1",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,415 @@
{
"id": "44200e995aaf429d9975bd7177e271d0",
"definitionId": "7d0420cacaad4ae09c589a0355bbef44",
"name": "Braided conditional delayed flow",
"createdAt": "2023-04-18T14:37:02.902303+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": false,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine1"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1600,
"y": 2320
}
},
"displayText": "WriteLine1"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine2"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2220
}
},
"displayText": "WriteLine2"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine3"
},
"memoryReference": {
"id": "WriteLine3:input-1"
}
},
"id": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 1820,
"y": 2400
}
},
"displayText": "WriteLine3"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine4"
},
"memoryReference": {
"id": "WriteLine4:input-1"
}
},
"id": "WriteLine4",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2160
}
},
"displayText": "WriteLine4"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine6"
},
"memoryReference": {
"id": "WriteLine6:input-1"
}
},
"id": "WriteLine6",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2480
}
},
"displayText": "WriteLine6"
}
},
{
"condition": {
"typeName": "Boolean",
"expression": {
"type": "Literal",
"value": "True"
},
"memoryReference": {
"id": "FlowDecision1:input-1"
}
},
"id": "FlowDecision1",
"type": "Elsa.FlowDecision",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2320
}
}
}
},
{
"timeSpan": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay1:input-1"
}
},
"strategy": {
"typeName": "Elsa.Scheduling.Models.DelayBlockingStrategy, Elsa.Scheduling",
"expression": {
"type": "Literal",
"value": "NonBlocking"
},
"memoryReference": {
"id": "Delay1:input-2"
}
},
"autoBlockingThreshold": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay1:input-3"
}
},
"id": "Delay1",
"type": "Elsa.Delay",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2320,
"y": 2220
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine7"
},
"memoryReference": {
"id": "WriteLine7:input-1"
}
},
"id": "WriteLine7",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2520,
"y": 2220
}
},
"displayText": "WriteLine7"
}
},
{
"timeSpan": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:10"
},
"memoryReference": {
"id": "Delay2:input-1"
}
},
"strategy": {
"typeName": "Elsa.Scheduling.Models.DelayBlockingStrategy, Elsa.Scheduling",
"expression": {
"type": "Literal",
"value": "NonBlocking"
},
"memoryReference": {
"id": "Delay2:input-2"
}
},
"autoBlockingThreshold": {
"typeName": "TimeSpan",
"expression": {
"type": "Literal",
"value": "00:00:05"
},
"memoryReference": {
"id": "Delay2:input-3"
}
},
"id": "Delay2",
"type": "Elsa.Delay",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2320,
"y": 2420
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "WriteLine8"
},
"memoryReference": {
"id": "WriteLine8:input-1"
}
},
"id": "WriteLine8",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2520,
"y": 2420
}
},
"displayText": "WriteLine8"
}
}
],
"connections": [
{
"source": "WriteLine1",
"target": "WriteLine2",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine4",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "FlowDecision1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine1",
"target": "WriteLine3",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine3",
"target": "WriteLine6",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "FlowDecision1",
"target": "Delay1",
"sourcePort": "True",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "FlowDecision1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "Delay1",
"target": "WriteLine7",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "Delay2",
"target": "WriteLine8",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "FlowDecision1",
"target": "Delay2",
"sourcePort": "False",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,207 @@
{
"id": "3365d4c0198e459f834b799cbfe91154",
"definitionId": "4aa1e566da264ab6b5495bedb734096d",
"name": "Forking, Containment and Blocking",
"description": "A combination of a fork, a containment and a blocking activity.",
"version": 1,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "uEnFWEdaWkadgIMHT2umLA",
"metadata": {},
"customProperties": {
"NotFoundConnectionsKey": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"id": "MLDV_altcUO5MwP7p5xJWw",
"name": "Start1",
"type": "Elsa.Start",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -480,
"y": -566.046875
},
"size": {
"width": 102.21875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Intermediary"
},
"memoryReference": {
"id": "EIh3Q0Um1U-QSCh1b3GBjQ:input-0"
}
},
"id": "EIh3Q0Um1U-QSCh1b3GBjQ",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -228.5,
"y": -476.046875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": null,
"id": "0Z7Velr5CEW_r7YYoeEjBQ",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 69.34375,
"y": -476.046875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"body": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "NJ5fh2bUWU-TxWeXsbNaSg",
"metadata": {},
"customProperties": {
"NotFoundConnectionsKey": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"eventName": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Event4"
},
"memoryReference": {
"id": "8KVHRw-WpUizyxOq0C6Nvg:input-0"
}
},
"result": null,
"id": "8KVHRw-WpUizyxOq0C6Nvg",
"name": "Event1",
"type": "Elsa.Event",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -840,
"y": -500
},
"size": {
"width": 106.828125,
"height": 50
}
}
}
}
],
"connections": []
},
"id": "HG4wfBhvlEW_149HwNyzHw",
"name": "FlowNode1",
"type": "Elsa.FlowNode",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -228.5,
"y": -704.109375
},
"size": {
"width": 211.15625,
"height": 120
}
}
}
}
],
"connections": [
{
"source": {
"activity": "MLDV_altcUO5MwP7p5xJWw",
"port": "Done"
},
"target": {
"activity": "HG4wfBhvlEW_149HwNyzHw",
"port": "In"
}
},
{
"source": {
"activity": "MLDV_altcUO5MwP7p5xJWw",
"port": "Done"
},
"target": {
"activity": "EIh3Q0Um1U-QSCh1b3GBjQ",
"port": "In"
}
},
{
"source": {
"activity": "EIh3Q0Um1U-QSCh1b3GBjQ",
"port": "Done"
},
"target": {
"activity": "0Z7Velr5CEW_r7YYoeEjBQ",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,198 @@
{
"id": "e5190066597846cc926f8ae644d706f4",
"definitionId": "b07725956291433abee7ff229b415194",
"name": "Parallel For Each",
"createdAt": "2023-09-10T16:32:58.422674+00:00",
"version": 3,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "oplDmRa_zEqH8PCdTcFh8A",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"NotFoundConnectionsKey": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "NTW8X70bTEaBtlYyU44PcA:input-0"
}
},
"id": "NTW8X70bTEaBtlYyU44PcA",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -352,
"y": -286.9921875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "End"
},
"memoryReference": {
"id": "6Bf011ZOvUWOfQ1rKJEDFA:input-0"
}
},
"id": "6Bf011ZOvUWOfQ1rKJEDFA",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 169,
"y": -286.9921875
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"items": {
"typeName": "Object[]",
"expression": {
"type": "JavaScript",
"value": "[\u0022Apple\u0022, \u0022Banana\u0022, \u0022Cherry\u0022]"
},
"memoryReference": {
"id": "By4rVs_-2kSDENFO-bniRg:input-0"
}
},
"body": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "ylZLDh7FQ0WmnbdnRJHutg",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"NotFoundConnectionsKey": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "JavaScript",
"value": "\u0060Current fruit: ${getCurrentValue()}\u0060"
},
"memoryReference": {
"id": "fht2JVY0G0KURaRYB9jqlw:input-0"
}
},
"id": "fht2JVY0G0KURaRYB9jqlw",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -459.5,
"y": -374
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
}
],
"connections": []
},
"id": "By4rVs_-2kSDENFO-bniRg",
"name": "ParallelForEach1",
"type": "Elsa.ParallelForEach",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -100,
"y": -320
},
"size": {
"width": 149.203125,
"height": 116.015625
}
}
}
}
],
"connections": [
{
"source": {
"activity": "NTW8X70bTEaBtlYyU44PcA",
"port": "Done"
},
"target": {
"activity": "By4rVs_-2kSDENFO-bniRg",
"port": "In"
}
},
{
"source": {
"activity": "By4rVs_-2kSDENFO-bniRg",
"port": "Done"
},
"target": {
"activity": "6Bf011ZOvUWOfQ1rKJEDFA",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,9 @@
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity.Activities;
public class CustomActivity : CodeActivity
{
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
await context.CompleteActivityAsync();
}
}
@@ -0,0 +1,315 @@
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.IntegrationTests.Scenarios.FlowchartNextActivity.Workflows;
using Elsa.Workflows.Memory;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity;
public class FlowchartNextActivityTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
private readonly IWorkflowRunner _workflowRunner;
public FlowchartNextActivityTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.AddActivitiesFrom<FlowchartNextActivityTests>()
.Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Flowchart only schedules next activity connected to outcome of previous activity.")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<FlowchartWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1" }, lines);
}
[Fact(DisplayName = "Flowchart with backward connections and a dangling activity")]
public async Task BackwardConnectionTest()
{
var workflow = new TestWorkflow(workflowBuilder =>
{
var loopVariable = new Variable<int>("LoopCount", 0);
var start = new Start();
var dangling = new WriteLine("dangling");
var writeLineDecision = new FlowSwitch()
{
Cases = {
new FlowSwitchCase("LessThanThree", new Expression("JavaScript", "getVariable('LoopCount') < 3")),
new FlowSwitchCase("LessThanOne", new Expression("JavaScript", "getVariable('LoopCount') < 1")),
},
Mode = new(SwitchMode.MatchAny)
};
var a = new WriteLine("A");
var b = new WriteLine("B");
var c = new WriteLine("C");
var incrementLoop = new SetVariable()
{
Variable = loopVariable,
Value = new Models.Input<object?>(new Expression("JavaScript", "getVariable('LoopCount') + 1"))
};
var loopbackDecision = new FlowSwitch()
{
Cases = {
new FlowSwitchCase("EqualOne", new Expression("JavaScript", "getVariable('LoopCount') == 1")),
new FlowSwitchCase("LessThanFour", new Expression("JavaScript", "getVariable('LoopCount') < 4")),
},
Mode = new(SwitchMode.MatchFirst)
};
var d = new WriteLine("D");
var e = new WriteLine("E");
var f = new WriteLine("F");
var end = new End();
workflowBuilder.Root = new Flowchart
{
Variables =
{
loopVariable
},
Activities =
{
start,
dangling,
writeLineDecision,
a,
b,
c,
incrementLoop,
loopbackDecision,
d,
e,
f,
end
},
Connections =
{
new(start, writeLineDecision),
new(dangling, writeLineDecision),
new(new Endpoint(writeLineDecision, "LessThanThree"), new Endpoint(a)),
new(new Endpoint(writeLineDecision, "LessThanThree"), new Endpoint(b)),
new(new Endpoint(writeLineDecision, "LessThanOne"), new Endpoint(c)),
new(new Endpoint(writeLineDecision, "Default"), new Endpoint(incrementLoop)),
new(a, incrementLoop),
new(b, incrementLoop),
new(c, incrementLoop),
new(incrementLoop, loopbackDecision),
new(new Endpoint(loopbackDecision, "EqualOne"), new Endpoint(d)),
new(d, incrementLoop),
new(new Endpoint(loopbackDecision, "LessThanFour"), new Endpoint(e)),
new(e, writeLineDecision),
new(new Endpoint(loopbackDecision, "Default"), new Endpoint(f)),
new(f, end),
}
};
});
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
Assert.Equal(new[] { "A", "B", "C", "D", "E", "A", "B", "E", "F" }, lines);
}
[Fact(DisplayName = "Flowchart with an invalid backward connection")]
public async Task InvalidBackwardConnectionTest()
{
var workflow = new TestWorkflow(workflowBuilder =>
{
var start = new Start() { Id = "Start" };
var a = new WriteLine("A") { Id = "WriteLineA" };
var b = new WriteLine("B") { Id = "WriteLineB" };
var c = new WriteLine("C") { Id = "WriteLineC" };
var d = new WriteLine("D") { Id = "WriteLineD" };
var e = new WriteLine("E") { Id = "WriteLineE" };
workflowBuilder.Root = new Flowchart
{
Activities =
{
start,
a,
b,
c,
d,
e,
},
Connections =
{
new(start, a),
new(a, b),
new(b, c),
new(b, d),
new(c, e),
new(d, e),
new(e, c),
}
};
});
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(WorkflowSubStatus.Faulted, result.WorkflowState.SubStatus);
Assert.Equal(1, result.WorkflowState.Incidents.Count());
Assert.Equal("Invalid backward connection: Every path from the source ('WriteLineE') must go through the target ('WriteLineC') when tracing back to the start.", result.WorkflowState.Incidents.First().Message);
Assert.Equal(new[] { "A", "B", "C", "D", "E" }, lines);
}
[Theory(DisplayName = "Flowchart with a Join activity executed multiple times")]
[InlineData(FlowJoinMode.WaitAll)]
[InlineData(FlowJoinMode.WaitAny)]
public async Task WaitAnyLoopTest(FlowJoinMode joinMode)
{
var workflow = new TestWorkflow(workflowBuilder =>
{
var loopVariable = new Variable<int>("LoopCount", 0);
var start = new Start();
var a = new WriteLine("A");
var b = new WriteLine("B");
var c = new WriteLine("C");
var d = new WriteLine("D");
var join = new FlowJoin()
{
Mode = new(joinMode)
};
var incrementLoop = new SetVariable()
{
Variable = loopVariable,
Value = new Models.Input<object?>(new Expression("JavaScript", "getVariable('LoopCount') + 1"))
};
var loopbackDecision = new FlowSwitch()
{
Cases = {
new FlowSwitchCase("LessThanThree", new Expression("JavaScript", "getVariable('LoopCount') < 3")),
},
Mode = new(SwitchMode.MatchFirst)
};
var end = new End();
workflowBuilder.Root = new Flowchart
{
Variables =
{
loopVariable
},
Activities =
{
start,
a,
b,
c,
d,
join,
incrementLoop,
loopbackDecision,
end
},
Connections =
{
new(start, a),
new(a, b),
new(a, c),
new(a, d),
new(b, join),
new(c, join),
new(d, join),
new(join, incrementLoop),
new(incrementLoop,loopbackDecision),
new(new Endpoint(loopbackDecision, "LessThanThree"), new Endpoint(a)),
new(new Endpoint(loopbackDecision, "Default"), new Endpoint(end)),
}
};
});
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
Assert.Equal(new[] { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B", "C", "D"}, lines);
}
[Theory(DisplayName = "Flowchart with a Join activity executed multiple times, bug 6479")]
[InlineData(FlowJoinMode.WaitAll)]
[InlineData(FlowJoinMode.WaitAny)]
public async Task WaitLoopBug6479Test(FlowJoinMode joinMode)
{
var workflow = new TestWorkflow(workflowBuilder =>
{
var loopVariable = new Variable<int>("LoopCount", 0);
var start = new Start();
var loopbackSwitch = new FlowSwitch()
{
Cases = {
new FlowSwitchCase("DoLoopback", new Expression("JavaScript", "getVariable('LoopCount') < 3")),
},
Mode = new(SwitchMode.MatchFirst)
};
var a = new WriteLine("A");
var incrementLoop = new SetVariable()
{
Variable = loopVariable,
Value = new Models.Input<object?>(new Expression("JavaScript", "getVariable('LoopCount') + 1"))
};
var join = new FlowJoin()
{
Mode = new(joinMode)
};
var b = new WriteLine("B");
var end = new End();
workflowBuilder.Root = new Flowchart
{
Variables =
{
loopVariable
},
Activities =
{
start,
loopbackSwitch,
a,
incrementLoop,
join,
b,
end
},
Connections =
{
new(start, loopbackSwitch),
new(new Endpoint(loopbackSwitch, "DoLoopback"), new Endpoint(a)),
new(new Endpoint(loopbackSwitch, "DoLoopback"), new Endpoint(incrementLoop)),
new(new Endpoint(loopbackSwitch, "Default"), new Endpoint(b)),
new(a, join),
new(incrementLoop, join),
new(join, loopbackSwitch),
new(b, end),
}
};
});
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
Assert.Equal(new[] { "A", "A", "A", "B" }, lines);
}
}
@@ -0,0 +1,31 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
using Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity.Workflows;
public class FlowchartWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var customActivity = new CustomActivity();
var writeLine1 = new WriteLine("Line 1");
var writeLine2 = new WriteLine("Line 2");
builder.Root = new Flowchart
{
Activities =
{
customActivity,
writeLine1,
writeLine2
},
Connections =
{
new Connection(new Endpoint(customActivity, "Done"), new Endpoint(writeLine1)),
new Connection(new Endpoint(customActivity, "Fake"), new Endpoint(writeLine2)),
}
};
}
}
@@ -0,0 +1,40 @@
using Elsa.Expressions.Models;
using Elsa.JavaScript.Activities;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Memory;
namespace Elsa.Workflows.IntegrationTests.Scenarios.HttpRequestWithLiquid;
/// <summary>
/// A workflow that use javascript to get some data, use them with some liquid expressions
/// </summary>
public class JavascriptAndLiquidWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var products = new Variable<object>("Products", null!).WithStorageDriver<WorkflowInstanceStorageDriver>();
var product = new Variable<object>("Product", null!).WithStorageDriver<WorkflowInstanceStorageDriver>();
builder.Root = new Sequence
{
Variables = { products, product },
Activities =
{
new RunJavaScript
{
Script = new(@"setProducts([{""id"":1, ""price"":12.99}, {""id"":2, ""price"":10}, {""id"":3, ""price"":1}])")
},
new WriteLine(new Expression("Liquid", "First product id: {{ Variables.Products[0].id }}")),
new WriteLine(new Expression("Liquid", "First product price rounded: {{ Variables.Products[0].price | round }}")),
new WriteLine(new Expression("Liquid", "First product as json: {{ Variables.Products[0] | json }}")),
new WriteLine(new Expression("Liquid", "Second product id: {{ Variables.Products[1].id }}")),
new RunJavaScript
{
Script = new(@"variables.Product = {""id"":2, ""price"":10}")
},
new WriteLine(new Expression("Liquid", "Single product id: {{ Variables.Product.id }}")),
new WriteLine(new Expression("Liquid", "Single product as json: {{ Variables.Product | json }}")),
}
};
}
}
@@ -0,0 +1,48 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Elsa.Workflows.Models;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.HttpRequestWithLiquid;
public sealed class Tests : IDisposable
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureElsa(configure => configure.UseHttp())
.Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact]
public async Task GetProducts()
{
await _services.PopulateRegistriesAsync();
RunWorkflowResult result = await _workflowRunner.RunAsync(new JavascriptAndLiquidWorkflow());
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
Assert.Empty(result.WorkflowState.Incidents);
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Contains("First product id: 1", lines);
Assert.Contains("First product price rounded: 13", lines);
Assert.Contains("First product as json: {\"id\":1,\"price\":12.99}", lines);
Assert.Contains("Second product id: 2", lines);
Assert.Contains("Single product id: 2", lines);
Assert.Contains("Single product as json: {\"id\":2,\"price\":10}", lines);
}
public void Dispose()
{
_capturingTextWriter.Dispose();
GC.SuppressFinalize(this);
}
}
@@ -0,0 +1,36 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins;
public class BraidedWorkflowTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public BraidedWorkflowTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Braided workflows are executed correctly")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<BraidedWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "WriteLine1", "WriteLine2", "WriteLine3", "WriteLine4", "WriteLine5", "WriteLine6", "WriteLine7" }, lines);
}
[Fact(DisplayName = "Braided workflows complete the workflow")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync<BraidedWorkflow>();
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
}
}
@@ -0,0 +1,36 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins;
public class ImplicitWorkflowTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public ImplicitWorkflowTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Implicit loop workflows are executed correctly")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<ImplicitLoopWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Retry", "End" }, lines);
}
[Fact(DisplayName = "Implicit loop workflows complete the workflow")]
public async Task Test2()
{
await _services.PopulateRegistriesAsync();
var result = await _workflowRunner.RunAsync<ImplicitLoopWorkflow>();
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
}
}
@@ -0,0 +1,42 @@
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Filters;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins;
public class JoinRunsOnceTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public JoinRunsOnceTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
}
[Fact(DisplayName = "The Join activity executes only once, not twice")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync($"Scenarios/ImplicitJoins/Workflows/join.json");
// Execute.
var state = await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Assert.
var journal = await _services.GetRequiredService<IWorkflowExecutionLogStore>().FindManyAsync(new WorkflowExecutionLogRecordFilter
{
WorkflowInstanceId = state.Id,
ActivityId = "802725996be1b582",
EventName = "Completed"
}, PageArgs.All);
Assert.Single(journal.Items);
}
}
@@ -0,0 +1,42 @@
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Filters;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins;
public class ParallelJoinCompletesTests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public ParallelJoinCompletesTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
}
[Fact(DisplayName = "The ParallelForEach activity completes when its Body contains a Join activity")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync("Scenarios/ImplicitJoins/Workflows/parallel-join.json");
// Execute.
var state = await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Assert.
var journal = await _services.GetRequiredService<IWorkflowExecutionLogStore>().FindManyAsync(new()
{
WorkflowInstanceId = state.Id,
ActivityId = "70fc1183cd5800f2",
EventName = "Completed"
}, PageArgs.All);
Assert.Single(journal.Items);
}
}
@@ -0,0 +1,51 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins.Workflows;
public class BraidedWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var writeLine1 = new WriteLine("WriteLine1");
var writeLine2 = new WriteLine("WriteLine2");
var writeLine3 = new WriteLine("WriteLine3");
var writeLine4 = new WriteLine("WriteLine4");
var writeLine5 = new WriteLine("WriteLine5");
var writeLine6 = new WriteLine("WriteLine6");
var writeLine7 = new WriteLine("WriteLine7");
workflow.Root = new Flowchart
{
Start = writeLine1,
Activities =
{
writeLine1,
writeLine2,
writeLine3,
writeLine4,
writeLine5,
writeLine6,
writeLine7,
},
Connections =
{
new Connection(writeLine1, writeLine2),
new Connection(writeLine1, writeLine3),
new Connection(writeLine2, writeLine4),
new Connection(writeLine2, writeLine5),
new Connection(writeLine3, writeLine5),
new Connection(writeLine3, writeLine6),
new Connection(writeLine4, writeLine7),
new Connection(writeLine5, writeLine7),
new Connection(writeLine6, writeLine7),
}
};
}
}
@@ -0,0 +1,41 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImplicitJoins.Workflows;
public class ImplicitLoopWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var counterVariable = builder.WithVariable(0);
var start = new WriteLine("Start");
var incrementCounter = new SetVariable<int>(counterVariable, context => counterVariable.Get(context) + 1);
var counterGreaterThanOne = new FlowDecision(context => counterVariable.Get(context) > 1);
var retry = new WriteLine("Retry");
var end = new WriteLine("End");
builder.Root = new Flowchart
{
Start = start,
Activities =
{
start,
incrementCounter,
counterGreaterThanOne,
retry,
end
},
Connections =
{
new Connection(start, incrementCounter),
new Connection(incrementCounter, counterGreaterThanOne),
new Connection(new Endpoint(counterGreaterThanOne, "False"), new Endpoint(retry)),
new Connection(new Endpoint(counterGreaterThanOne, "True"), new Endpoint(end)),
new Connection(retry, incrementCounter),
}
};
}
}
@@ -0,0 +1,215 @@
{
"id": "825e9d347e2f3094",
"definitionId": "f64e42304661f407",
"name": "Joins",
"createdAt": "2023-12-13T21:41:53.628415+00:00",
"version": 3,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": false,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "8a03bf79d15fd48a",
"nodeId": "Workflow1:8a03bf79d15fd48a",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"id": "c7624645ff77c7d0",
"nodeId": "Workflow1:8a03bf79d15fd48a:c7624645ff77c7d0",
"name": "Start1",
"type": "Elsa.Start",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -400.5,
"y": 212
},
"size": {
"width": 102.21875,
"height": 50
}
}
}
},
{
"text": null,
"id": "281e9ea6d278b231",
"nodeId": "Workflow1:8a03bf79d15fd48a:281e9ea6d278b231",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -160.5,
"y": 305
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAny"
},
"memoryReference": {
"id": "802725996be1b582:input-0"
}
},
"id": "802725996be1b582",
"nodeId": "Workflow1:8a03bf79d15fd48a:802725996be1b582",
"name": "FlowJoin1",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 80,
"y": 200
},
"size": {
"width": 98.265625,
"height": 50
}
}
}
},
{
"text": null,
"id": "bfc88f064cf394ed",
"nodeId": "Workflow1:8a03bf79d15fd48a:bfc88f064cf394ed",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -160,
"y": 120
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"id": "5467123c7973d011",
"nodeId": "Workflow1:8a03bf79d15fd48a:5467123c7973d011",
"name": "End1",
"type": "Elsa.End",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 274.5,
"y": 200
},
"size": {
"width": 94.40625,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "c7624645ff77c7d0",
"port": "Done"
},
"target": {
"activity": "bfc88f064cf394ed",
"port": "In"
}
},
{
"source": {
"activity": "bfc88f064cf394ed",
"port": "Done"
},
"target": {
"activity": "802725996be1b582",
"port": "In"
}
},
{
"source": {
"activity": "c7624645ff77c7d0",
"port": "Done"
},
"target": {
"activity": "281e9ea6d278b231",
"port": "In"
}
},
{
"source": {
"activity": "281e9ea6d278b231",
"port": "Done"
},
"target": {
"activity": "802725996be1b582",
"port": "In"
}
},
{
"source": {
"activity": "802725996be1b582",
"port": "Done"
},
"target": {
"activity": "5467123c7973d011",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,241 @@
{
"id": "2effd8f5f4205a5b",
"definitionId": "2da8d4cb0fcfbacd",
"name": "Parallel \u002B Join",
"createdAt": "2023-12-13T21:42:12.634042+00:00",
"version": 2,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": false,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "d3350290ed96e720",
"nodeId": "Workflow1:d3350290ed96e720",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"items": {
"typeName": "Object[]",
"expression": {
"type": "JavaScript",
"value": "[1,2,3]"
},
"memoryReference": {
"id": "70fc1183cd5800f2:input-0"
}
},
"body": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "b07170f2d68980cd",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2:b07170f2d68980cd",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:45",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"condition": {
"typeName": "Boolean",
"expression": {
"type": "JavaScript",
"value": "false"
},
"memoryReference": {
"id": "33665f061d96b957:input-0"
}
},
"id": "33665f061d96b957",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2:b07170f2d68980cd:33665f061d96b957",
"name": "FlowDecision1",
"type": "Elsa.FlowDecision",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -329.140625,
"y": -78.5
},
"size": {
"width": 129.515625,
"height": 50
}
}
}
},
{
"text": null,
"id": "e27177eb5965bd6b",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2:b07170f2d68980cd:e27177eb5965bd6b",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -40,
"y": -160
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": null,
"id": "343d233a75d8d8e9",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2:b07170f2d68980cd:343d233a75d8d8e9",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 371.859375,
"y": -78.5
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"mode": {
"typeName": "Elsa.Workflows.Core.Activities.Flowchart.Models.FlowJoinMode, Elsa.Workflows.Core",
"expression": {
"type": "Literal",
"value": "WaitAny"
},
"memoryReference": {
"id": "1f8625e278b74aa3:input-0"
}
},
"id": "1f8625e278b74aa3",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2:b07170f2d68980cd:1f8625e278b74aa3",
"name": "FlowJoin1",
"type": "Elsa.FlowJoin",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 188.859375,
"y": -78.5
},
"size": {
"width": 98.265625,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "33665f061d96b957",
"port": "True"
},
"target": {
"activity": "e27177eb5965bd6b",
"port": "In"
}
},
{
"source": {
"activity": "e27177eb5965bd6b",
"port": "Done"
},
"target": {
"activity": "1f8625e278b74aa3",
"port": "In"
}
},
{
"source": {
"activity": "1f8625e278b74aa3",
"port": "Done"
},
"target": {
"activity": "343d233a75d8d8e9",
"port": "In"
}
},
{
"source": {
"activity": "33665f061d96b957",
"port": "False"
},
"target": {
"activity": "1f8625e278b74aa3",
"port": "In"
}
}
]
},
"id": "70fc1183cd5800f2",
"nodeId": "Workflow1:d3350290ed96e720:70fc1183cd5800f2",
"name": "ParallelForEach1",
"type": "Elsa.ParallelForEach",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -260,
"y": 140
},
"size": {
"width": 185.203125,
"height": 116.015625
}
}
}
}
],
"connections": []
}
}
@@ -0,0 +1,45 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ImportAndExecute;
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.Build();
}
[Theory(DisplayName = "Workflow imported from file should execute successfully.")]
[MemberData(memberName: nameof(GetSpecimen))]
public async Task Test1(string workflowFileName, string[] expectedOutput)
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync($"Scenarios/ImportAndExecute/Workflows/{workflowFileName}");
// Execute.
await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Assert.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(expectedOutput, lines);
}
public static TheoryData<string, string[]> GetSpecimen()
{
return new TheoryData<string, string[]>
{
{ "writeline.json", new[] { "Dummy Text" } },
{ "implicit-loop.json", new[] { "Do something", "Retry", "Do something", "Done" } }
};
}
}
@@ -0,0 +1,243 @@
{
"id": "0975a7e7cff7421ea03d1cc0247db02b",
"definitionId": "68e6bca3d86c4a5a8dfb825f1d5c0875",
"name": "Looping construct",
"createdAt": "2023-04-18T19:04:50.945222+00:00",
"version": 1,
"variables": [
{
"id": "b1198ecccf3f435abe462dc37012cda9",
"name": "Counter",
"typeName": "Int32",
"isArray": false,
"value": "0",
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
}
],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": true,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "Start1",
"activities": [
{
"variable": {
"id": "b1198ecccf3f435abe462dc37012cda9",
"name": "Counter",
"typeName": "Int32",
"value": "0",
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
},
"value": {
"typeName": "Object",
"expression": {
"type": "JavaScript",
"value": "getCounter() \u002B 1"
},
"memoryReference": {
"id": "SetVariable1:input-1"
}
},
"id": "SetVariable1",
"type": "Elsa.SetVariable",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2620,
"y": 2280
}
},
"displayText": "Increment counter"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Done"
},
"memoryReference": {
"id": "WriteLine3:input-1"
}
},
"id": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 3220,
"y": 2400
}
},
"displayText": "Done"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Retry"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2460,
"y": 2500
}
},
"displayText": "Retry"
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Do something"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2320,
"y": 2280
}
},
"displayText": "Do something"
}
},
{
"condition": {
"typeName": "Boolean",
"expression": {
"type": "JavaScript",
"value": "getCounter() \u003E 1"
},
"memoryReference": {
"id": "FlowDecision1:input-1"
}
},
"id": "FlowDecision1",
"type": "Elsa.FlowDecision",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2940,
"y": 2400
}
},
"displayText": "Counter \u003E 1"
}
},
{
"id": "Start1",
"type": "Elsa.Start",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2060,
"y": 2380
}
}
}
}
],
"connections": [
{
"source": "WriteLine1",
"target": "SetVariable1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "FlowDecision1",
"target": "WriteLine3",
"sourcePort": "True",
"targetPort": "In"
},
{
"source": "FlowDecision1",
"target": "WriteLine2",
"sourcePort": "False",
"targetPort": "In"
},
{
"source": "Start1",
"target": "WriteLine1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "WriteLine2",
"target": "WriteLine1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "SetVariable1",
"target": "FlowDecision1",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
@@ -0,0 +1,55 @@
{
"id": "3a793167cf6948c899d2f40c3c05cf0b",
"definitionId": "11e2b350dae8485498c16d1ce3843829",
"name": "Dummy workflow",
"createdAt": "2023-04-04T08:35:22.841043+00:00",
"version": 1,
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"metadata": {},
"isLatest": true,
"isPublished": true,
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "Flowchart1",
"metadata": {},
"applicationProperties": {
"NotFoundConnectionsKey": []
},
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Dummy Text"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"canStartWorkflow": false,
"runAsynchronously": false,
"customProperties": {},
"source": null,
"line": null,
"metadata": {
"designer": {
"position": {
"x": 2256,
"y": 2462
}
}
}
}
],
"connections": []
}
}
@@ -0,0 +1,35 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.IncidentStrategies;
using Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Statics;
using Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Workflows;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Incidents;
public class IncidentStrategyTests
{
private readonly IServiceProvider _services;
private readonly CapturingTextWriter _capturingTextWriter = new();
public IncidentStrategyTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.AddWorkflow<FaultyWorkflow>()
.Build();
}
[Theory(DisplayName = "Workflows with a Fault strategy do not continue after a fault")]
[InlineData(typeof(FaultStrategy), new[] { "Start", "Step 1a", "Step 2a" }, WorkflowSubStatus.Faulted)]
[InlineData(typeof(ContinueWithIncidentsStrategy), new[] { "Start", "Step 1a", "Step 2a", "Step 1b" }, WorkflowSubStatus.Suspended)]
public async Task Test0(Type incidentStrategyType, string[] expectedOutput, WorkflowSubStatus expectedSubStatus)
{
TestSettings.IncidentStrategyType = incidentStrategyType;
await _services.PopulateRegistriesAsync();
var workflowState = await _services.RunWorkflowUntilEndAsync<FaultyWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(expectedOutput, lines);
Assert.Equal(expectedSubStatus, workflowState.SubStatus);
Assert.Equal(1, workflowState.Incidents.Count);
}
}
@@ -0,0 +1,8 @@
using Elsa.Workflows.IncidentStrategies;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Statics;
public static class TestSettings
{
public static Type IncidentStrategyType { get; set; } = typeof(FaultStrategy);
}
@@ -0,0 +1,51 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
using Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Statics;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Workflows;
public class FaultyWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var start = new WriteLine("Start");
var step1A = new WriteLine("Step 1a");
var event1 = new Event("Event 1");
var step1B = new WriteLine("Step 1b");
var step2A = new WriteLine("Step 2a");
var event2 = new Event("Event 2");
var step2B = new WriteLine("Step 2b");
var fault = Fault.Create("Whoops!", "Test", "Test");
builder.WorkflowOptions.IncidentStrategyType = TestSettings.IncidentStrategyType;
builder.Root = new Flowchart
{
Activities =
{
start,
step1A,
event1,
step1B,
step2A,
event2,
step2B,
fault
},
Connections = new[]
{
new Connection(start, step1A),
new Connection(step1A, event1),
new Connection(event1, step1B),
new Connection(start, step2A),
new Connection(step2A, fault),
new Connection(fault, event2),
new Connection(event2, step2B),
}
};
}
}
@@ -0,0 +1,99 @@
using System.Text;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Contracts;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JavaScriptFunctions;
public class BytesAndStringEncodingTests
{
private readonly IJavaScriptEvaluator _evaluator;
private readonly ExpressionExecutionContext _expressionContext;
public BytesAndStringEncodingTests(ITestOutputHelper testOutputHelper)
{
var testOutputHelper1 = testOutputHelper ?? throw new ArgumentNullException(nameof(testOutputHelper));
var services = new TestApplicationBuilder(testOutputHelper1).Build();
_evaluator = services.GetRequiredService<IJavaScriptEvaluator>();
_expressionContext = new ExpressionExecutionContext(services, new MemoryRegister());
}
[Fact]
public async Task ByteArrayToString_ConvertsTo_String()
{
const string data = "Hello World!";
var bytes = Encoding.UTF8.GetBytes(data);
var script = "bytesToString(getData())";
_expressionContext.SetVariable("Data", bytes);
var result = (string)(await _evaluator.EvaluateAsync(script, typeof(string), _expressionContext))!;
Assert.Equal(data, result);
}
[Fact]
public async Task ByteArrayFromString_ConvertsTo_ByteArray()
{
const string data = "Hello World!";
var script = "bytesFromString(getData())";
_expressionContext.SetVariable("Data", data);
var result = (byte[])(await _evaluator.EvaluateAsync(script, typeof(byte[]), _expressionContext))!;
var bytes = Encoding.UTF8.GetBytes(data);
Assert.Equal(bytes, result);
}
[Fact]
public async Task ByteArrayToBase64_ConvertsTo_Base64()
{
const string data = "Hello World!";
var bytes = Encoding.UTF8.GetBytes(data);
var base64 = Convert.ToBase64String(bytes);
var script = "bytesToBase64(getData())";
_expressionContext.SetVariable("Data", bytes);
var result = (string)(await _evaluator.EvaluateAsync(script, typeof(string), _expressionContext))!;
Assert.Equal(base64, result);
}
[Fact]
public async Task ByteArrayFromBase64_ConvertsTo_ByteArray()
{
const string data = "Hello World!";
var bytes = Encoding.UTF8.GetBytes(data);
var base64 = Convert.ToBase64String(bytes);
var script = "bytesFromBase64(getData())";
_expressionContext.SetVariable("Data", base64);
var result = (byte[])(await _evaluator.EvaluateAsync(script, typeof(byte[]), _expressionContext))!;
Assert.Equal(bytes, result);
}
[Fact]
public async Task StringToBase64_ConvertsTo_Base64()
{
const string data = "Hello World!";
var bytes = Encoding.UTF8.GetBytes(data);
var base64 = Convert.ToBase64String(bytes);
var script = "stringToBase64(getData())";
_expressionContext.SetVariable("Data", data);
var result = (string)(await _evaluator.EvaluateAsync(script, typeof(string), _expressionContext))!;
Assert.Equal(base64, result);
}
[Fact]
public async Task StringFromBase64_ConvertsTo_String()
{
const string data = "Hello World!";
var bytes = Encoding.UTF8.GetBytes(data);
var base64 = Convert.ToBase64String(bytes);
var script = "stringFromBase64(getData())";
_expressionContext.SetVariable("Data", base64);
var result = (string)(await _evaluator.EvaluateAsync(script, typeof(string), _expressionContext))!;
Assert.Equal(data, result);
}
}
@@ -0,0 +1,120 @@
using System.Dynamic;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Contracts;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JavaScriptListsAndArrays;
public class Tests
{
private readonly IJavaScriptEvaluator _evaluator;
private readonly ExpressionExecutionContext _expressionContext;
public Tests(ITestOutputHelper testOutputHelper)
{
var testOutputHelper1 = testOutputHelper ?? throw new ArgumentNullException(nameof(testOutputHelper));
var services = new TestApplicationBuilder(testOutputHelper1).Build();
_evaluator = services.GetRequiredService<IJavaScriptEvaluator>();
_expressionContext = new ExpressionExecutionContext(services, new MemoryRegister());
}
[Fact(DisplayName = "Workflow inputs containing .NET lists on dynamic objects are converted to arrays for use in JavaScript.")]
public async Task Test1()
{
dynamic dynamicObject = new ExpandoObject();
dynamicObject.List = new List<string>
{
"a",
"b",
"c"
};
var script = "getObj().List.filter(x => x === 'b').length === 1";
_expressionContext.SetVariable("obj", (object)dynamicObject);
var result = await _evaluator.EvaluateAsync(script, typeof(bool), _expressionContext);
Assert.True((bool)result!);
}
[Fact(DisplayName = "Can access list properties as arrays")]
public async Task Test2()
{
var person = new ExpandoObject();
var magicNumbers = new[]
{
42, 43, 44
};
var order1 = new ExpandoObject();
order1.TryAdd("magicNumbers", magicNumbers);
person.TryAdd("orders", new[]
{
order1
});
person.TryAdd("name", "John");
person.TryAdd("age", 12);
var languages = new List<object>
{
"English",
"French"
};
person.TryAdd("languages", languages);
var obj = new ExpandoObject();
obj.TryAdd("persons", new List<object>
{
person
});
var name = await _evaluator.EvaluateAsync("o.persons.filter(x => x.age == 12)[0].name", typeof(string), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
var language = await _evaluator.EvaluateAsync("o.persons[0].languages.filter(x => x == 'English')[0]", typeof(string), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
var magicNumber = await _evaluator.EvaluateAsync("o.persons[0].orders[0].magicNumbers[1]", typeof(int), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
Assert.Equal("John", name);
Assert.Equal("English", language);
Assert.Equal(43, magicNumber);
}
[Theory(DisplayName = "Can sort array and list properties as mutable arrays")]
[MemberData(nameof(ArraySortEnumerableData))]
// See also:
// - https://github.com/sebastienros/jint/issues/1942
// - https://github.com/elsa-workflows/elsa-core/issues/5912
public async Task Test5(IEnumerable<double> collection)
{
dynamic dynamicObject = new ExpandoObject();
dynamicObject.Items = collection;
var script = """
const model = variables.Model;
model.Items.sort((a, b) => a - b);
return model;
""";
_expressionContext.SetVariable("Model", (object)dynamicObject);
dynamicObject = (ExpandoObject)(await _evaluator.EvaluateAsync(script, typeof(ExpandoObject), _expressionContext))!;
var items = ((object[])dynamicObject.Items).Cast<double>().ToArray();
Assert.Equal([2, 4, 8], items);
}
public static IEnumerable<object[]> ArraySortEnumerableData = new object[][]
{
[
new double[]
{
8, 4, 2
}
],
[
new List<double>
{
8,
4,
2
}
]
};
}
@@ -0,0 +1,35 @@
using System.Dynamic;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JavaScriptNativeVariables;
public class Data
{
public static ExpandoObject CreateCustomerModel()
{
dynamic customer = new ExpandoObject();
customer.Name = "John Doe";
customer.Age = 42;
customer.Orders = new[]
{
new
{
Id = 1,
Product = "Apple",
Price = 10
},
new
{
Id = 3,
Product = "Orange",
Price = 30
},
new
{
Id = 2,
Product = "Banana",
Price = 20
}
};
return customer;
}
}
@@ -0,0 +1,38 @@
using Elsa.Extensions;
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JavaScriptNativeVariables;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.ConfigureElsa(elsa => elsa.UseJavaScript())
.Build();
_workflowBuilderFactory = _services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "The JavaScript activity can access and modify native variables")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
var workflow = await _workflowBuilderFactory.CreateBuilder().BuildWorkflowAsync<JavaScriptWorkflow>();
await _workflowRunner.RunAsync(workflow);
var line1 = _capturingTextWriter.Lines.ToList().ElementAt(0);
var line2 = _capturingTextWriter.Lines.ToList().ElementAt(1);
var line3 = _capturingTextWriter.Lines.ToList().ElementAt(2);
Assert.Equal("Jane Doe", line1);
Assert.Equal("Apple, Banana, Orange", line2);
Assert.Equal("jane.doe@acme.com", line3);
}
}
@@ -0,0 +1,33 @@
using Elsa.JavaScript.Activities;
using Elsa.JavaScript.Models;
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JavaScriptNativeVariables;
public class JavaScriptWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
workflow.WithDefinitionId(Guid.NewGuid().ToString());
var customerVariable = workflow.WithVariable("customer", Data.CreateCustomerModel());
workflow.Root = new Sequence
{
Activities =
{
new RunJavaScript
{
Script = new("""
const customer = variables.customer;
customer.Name = 'Jane Doe';
customer.Orders.sort((a, b) => a.Product.localeCompare(b.Product));
variables.customer = {...customer, Email: 'jane.doe@acme.com'};
""")
},
new WriteLine(JavaScriptExpression.Create("variables.customer.Name")),
new WriteLine(JavaScriptExpression.Create("variables.customer.Orders.map(x => x.Product).join(', ')")),
new WriteLine(JavaScriptExpression.Create("variables.customer.Email"))
}
};
}
}
@@ -0,0 +1,44 @@
using Elsa.Testing.Shared;
using Elsa.Workflows.State;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectSerialization;
/// <summary>
/// Tests for serializing and deserializing JSON objects containing reserved keywords such as $id.
/// </summary>
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
private readonly IWorkflowStateSerializer _workflowStateSerializer;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowStateSerializer = _services.GetRequiredService<IWorkflowStateSerializer>();
}
[Fact(DisplayName = "User-objects containing $id don't break deserialization into ExpandoObject.")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import workflow.
var workflowFileName = "Scenarios/JsonObjectSerialization/Workflows/instance-serialization.json";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(workflowFileName);
// Execute.
var workflowState = await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId);
// Serialize workflow state.
var serializedWorkflowState = _workflowStateSerializer.Serialize(workflowState);
// Attempt to deserialize workflow state.
_workflowStateSerializer.Deserialize<WorkflowState>(serializedWorkflowState);
// If we reach this point, the test has passed. Otherwise, an exception would have been thrown.
}
}
@@ -0,0 +1,140 @@
{
"id": "b78e443339b5bf1e",
"definitionId": "861e51cdbe07931",
"name": "Instance Serialization",
"description": "This workflow stores a user-JSON object containing $id as a field, which should not affect the deserialization process form JSON back into ExpandoObject.",
"createdAt": "2023-11-23T15:18:12.805077+00:00",
"version": 13,
"toolVersion": "3.0.0.0",
"variables": [
{
"id": "d4f37df0c6781767",
"name": "Variable1",
"typeName": "Object",
"isArray": false,
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
},
{
"id": "4e256dab27d33eac",
"name": "Variable2",
"typeName": "Object",
"isArray": false,
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
}
],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": false,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "35cdc2a3529d9b55",
"nodeId": "Workflow1:35cdc2a3529d9b55",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:47",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"variable": {
"id": "d4f37df0c6781767",
"name": "Variable1",
"typeName": "Object",
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
},
"value": {
"typeName": "Object",
"expression": {
"type": "JavaScript",
"value": "return {\n \u0022inputSchema\u0022: {\n \u0022$schema\u0022: \u0022http://json-schema.org/draft-07/schema#\u0022,\n \u0022title\u0022: \u0022Inform front-office afd 2.0 input schema\u0022,\n \u0022description\u0022: \u0022Schema of the AFD 2.0 message that is sent to the front-office\u0022,\n \u0022$id\u0022: \u0022#/definitions/inform-front-office-input-afd\u0022,\n \u0022type\u0022: \u0022object\u0022\n }\n}"
},
"memoryReference": {
"id": "d4f32ee0ad659ba5:input-0"
}
},
"id": "d4f32ee0ad659ba5",
"nodeId": "Workflow1:35cdc2a3529d9b55:d4f32ee0ad659ba5",
"name": "SetVariable1",
"type": "Elsa.SetVariable",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -456.5988464355469,
"y": -104.93463134765625
},
"size": {
"width": 153.828125,
"height": 50
}
}
}
},
{
"variable": {
"id": "4e256dab27d33eac",
"name": "Variable2",
"typeName": "Object",
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
},
"value": {
"typeName": "Object",
"expression": {
"type": "JavaScript",
"value": "return getVariable1()"
},
"memoryReference": {
"id": "31c60a181a1a66d8:input-0"
}
},
"id": "31c60a181a1a66d8",
"nodeId": "Workflow1:35cdc2a3529d9b55:31c60a181a1a66d8",
"name": "SetVariable2",
"type": "Elsa.SetVariable",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -210.69132232666016,
"y": -104.93463134765625
},
"size": {
"width": 153.828125,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "d4f32ee0ad659ba5",
"port": "Done"
},
"target": {
"activity": "31c60a181a1a66d8",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,20 @@
using System.Text.Json.Nodes;
using Elsa.Extensions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectToObjectRemainsJsonObject.Activities;
public class JsonObjectProducer : CodeActivity<JsonObject>
{
protected override void Execute(ActivityExecutionContext context)
{
var value = new JsonObject
{
["Foo"] = new JsonObject
{
["Bar"] = "Baz"
}
};
context.SetResult(value);
}
}
@@ -0,0 +1,15 @@
using Elsa.Extensions;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectToObjectRemainsJsonObject.Activities;
public class ObjectProcessor : CodeActivity<object>
{
public Input<object> Value { get; set; } = default!;
protected override void Execute(ActivityExecutionContext context)
{
var value = Value.Get(context);
context.SetResult(value);
}
}
@@ -0,0 +1,28 @@
using Elsa.Testing.Shared;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectToObjectRemainsJsonObject;
public class JsonObjectJintTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public JsonObjectJintTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_services.GetRequiredService<IWorkflowBuilderFactory>();
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "The produced JsonObject remains JsonObject and does not become JsonElement (which does not support index notation)")]
public async Task Test1()
{
await _services.PopulateRegistriesAsync();
await _workflowRunner.RunAsync<Workflows.TestWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Baz" }, lines);
}
}
@@ -0,0 +1,32 @@
using System.Text.Json.Nodes;
using Elsa.Expressions.Models;
using Elsa.JavaScript.Models;
using Elsa.Workflows.Activities;
using Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectToObjectRemainsJsonObject.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.JsonObjectToObjectRemainsJsonObject.Workflows;
public class TestWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var item = builder.WithVariable<JsonObject>("item", default!);
builder.Root = new Sequence
{
Activities =
{
new JsonObjectProducer
{
Result = new(item)
},
new ObjectProcessor
{
Value = new(JavaScriptExpression.Create("getItem()"), new MemoryBlockReference()),
Result = new(item)
},
new WriteLine(JavaScriptExpression.Create("getItem()['Foo']['Bar']"))
}
};
}
}
@@ -0,0 +1,38 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ParentChildCompletion;
/// <summary>
/// Tests for mapping an activity's output directly to the workflow's output definition.
/// </summary>
public class Tests
{
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).Build();
}
[Fact(DisplayName = "Parent workflow invoking child workflow as activity completes successfully.")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import child workflow.
var childWorkflowFilename = "Scenarios/ParentChildCompletion/Workflows/child.json";
await _services.ImportWorkflowDefinitionAsync(childWorkflowFilename);
// Import parent workflow.
var parentWorkflowFilename = "Scenarios/ParentChildCompletion/Workflows/parent.json";
var parentWorkflowDefinition = await _services.ImportWorkflowDefinitionAsync(parentWorkflowFilename);
// Execute.
var workflowState = await _services.RunWorkflowUntilEndAsync(parentWorkflowDefinition.DefinitionId);
// Assert expected status
Assert.Equal(WorkflowStatus.Finished, workflowState.Status);
}
}
@@ -0,0 +1,303 @@
{
"id": "c8109a84c584c83c",
"definitionId": "3dcdbf22d29d6e60",
"name": "Subworkflow test",
"createdAt": "2023-11-10T13:03:21.734094+00:00",
"version": 4,
"toolVersion": "3.0.0.0",
"variables": [
{
"id": "c0dbe71ceb21ee4f",
"name": "Variable1",
"typeName": "Object",
"isArray": false,
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
}
],
"inputs": [],
"outputs": [],
"outcomes": [
"Success",
"Failure"
],
"customProperties": {},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"usableAsActivity": true,
"autoUpdateConsumingWorkflows": true
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "2492a1125f14bc2d",
"nodeId": "Workflow1:2492a1125f14bc2d",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:47",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "test"
},
"memoryReference": {
"id": "a3625dd2e8405265:input-0"
}
},
"id": "a3625dd2e8405265",
"nodeId": "Workflow1:2492a1125f14bc2d:a3625dd2e8405265",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": true,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -174,
"y": 302
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "1"
},
"memoryReference": {
"id": "31d29bf86b2b62cb:input-0"
}
},
"id": "31d29bf86b2b62cb",
"nodeId": "Workflow1:2492a1125f14bc2d:31d29bf86b2b62cb",
"name": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 132.6484375,
"y": 292
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "11"
},
"memoryReference": {
"id": "57576570e00a4c70:input-0"
}
},
"id": "57576570e00a4c70",
"nodeId": "Workflow1:2492a1125f14bc2d:57576570e00a4c70",
"name": "WriteLine5",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 449.6484375,
"y": 280
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "22"
},
"memoryReference": {
"id": "caa57f31b8d0e0d1:input-0"
}
},
"id": "caa57f31b8d0e0d1",
"nodeId": "Workflow1:2492a1125f14bc2d:caa57f31b8d0e0d1",
"name": "WriteLine4",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 449.6484375,
"y": 563
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"script": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "return {\u0022prop1\u0022:\u0022ceva\u0022}"
},
"memoryReference": {
"id": "fb20dbd8777282b2:input-0"
}
},
"possibleOutcomes": null,
"result": {
"typeName": "Object",
"memoryReference": {
"id": "c0dbe71ceb21ee4f"
}
},
"id": "fb20dbd8777282b2",
"nodeId": "Workflow1:2492a1125f14bc2d:fb20dbd8777282b2",
"name": "RunJavaScript1",
"type": "Elsa.RunJavaScript",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 114.453125,
"y": 563
},
"size": {
"width": 175.6875,
"height": 50
}
}
}
},
{
"outcomes": {
"typeName": "Object",
"expression": {
"type": "Literal",
"value": "Success"
},
"memoryReference": {
"id": "8654ab45535f4069:input-0"
}
},
"id": "8654ab45535f4069",
"nodeId": "Workflow1:2492a1125f14bc2d:8654ab45535f4069",
"name": "Complete1",
"type": "Elsa.Complete",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 738.7582397460938,
"y": 690.54443359375
},
"size": {
"width": 136.8125,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "a3625dd2e8405265",
"port": "Done"
},
"target": {
"activity": "31d29bf86b2b62cb",
"port": "In"
}
},
{
"source": {
"activity": "31d29bf86b2b62cb",
"port": "Done"
},
"target": {
"activity": "57576570e00a4c70",
"port": "In"
}
},
{
"source": {
"activity": "fb20dbd8777282b2",
"port": "Done"
},
"target": {
"activity": "caa57f31b8d0e0d1",
"port": "In"
}
},
{
"source": {
"activity": "a3625dd2e8405265",
"port": "Done"
},
"target": {
"activity": "fb20dbd8777282b2",
"port": "In"
}
},
{
"source": {
"activity": "caa57f31b8d0e0d1",
"port": "Done"
},
"target": {
"activity": "8654ab45535f4069",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,194 @@
{
"id": "34991d27edc12ae5",
"definitionId": "89d6fe82a11fbed6",
"name": "Mainworkflow test",
"createdAt": "2023-11-10T21:01:49.123204+00:00",
"version": 2,
"toolVersion": "3.0.0.0",
"variables": [],
"inputs": [],
"outputs": [],
"outcomes": [],
"customProperties": {
"Elsa:WorkflowContextProviderTypes": []
},
"isReadonly": false,
"isLatest": true,
"isPublished": true,
"options": {
"autoUpdateConsumingWorkflows": false
},
"root": {
"type": "Elsa.Flowchart",
"version": 1,
"id": "f9071ee83c88d765",
"nodeId": "Workflow1:f9071ee83c88d765",
"metadata": {},
"customProperties": {
"source": "FlowchartJsonConverter.cs:47",
"notFoundConnections": [],
"canStartWorkflow": false,
"runAsynchronously": false
},
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Success"
},
"memoryReference": {
"id": "77069142b8b9da15:input-0"
}
},
"id": "77069142b8b9da15",
"nodeId": "Workflow1:f9071ee83c88d765:77069142b8b9da15",
"name": "WriteLine2",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 231.8203125,
"y": 200
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Failure"
},
"memoryReference": {
"id": "34e3dcda423f3c1c:input-0"
}
},
"id": "34e3dcda423f3c1c",
"nodeId": "Workflow1:f9071ee83c88d765:34e3dcda423f3c1c",
"name": "WriteLine3",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": 231.8203125,
"y": 393
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "Start"
},
"memoryReference": {
"id": "8b7bf9cd03c066fa:input-0"
}
},
"id": "8b7bf9cd03c066fa",
"nodeId": "Workflow1:f9071ee83c88d765:8b7bf9cd03c066fa",
"name": "WriteLine1",
"type": "Elsa.WriteLine",
"version": 1,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -340,
"y": 309
},
"size": {
"width": 139.296875,
"height": 50
}
}
}
},
{
"workflowDefinitionId": "b8b45fd4dbffad5d",
"workflowDefinitionVersionId": "24dd11bc73a881d5",
"latestAvailablePublishedVersion": 7,
"latestAvailablePublishedVersionId": "24dd11bc73a881d5",
"id": "abcd6d3d391bfe10",
"nodeId": "Workflow1:f9071ee83c88d765:abcd6d3d391bfe10",
"name": "SubworkflowTest1",
"type": "SubworkflowTest",
"version": 7,
"customProperties": {
"canStartWorkflow": false,
"runAsynchronously": false
},
"metadata": {
"designer": {
"position": {
"x": -54,
"y": 309
},
"size": {
"width": 157.09375,
"height": 50
}
}
}
}
],
"connections": [
{
"source": {
"activity": "8b7bf9cd03c066fa",
"port": "Done"
},
"target": {
"activity": "abcd6d3d391bfe10",
"port": "In"
}
},
{
"source": {
"activity": "abcd6d3d391bfe10",
"port": "Success"
},
"target": {
"activity": "77069142b8b9da15",
"port": "In"
}
},
{
"source": {
"activity": "abcd6d3d391bfe10",
"port": "Failure"
},
"target": {
"activity": "34e3dcda423f3c1c",
"port": "In"
}
}
]
}
}
@@ -0,0 +1,64 @@
using Elsa.Testing.Shared;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.ParentChildInputs;
/// <summary>
/// Tests for the flowchart completion feature using various workflow setups.
/// </summary>
public class Tests
{
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IServiceProvider _services;
public Tests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
}
[Fact(DisplayName = "Child activity receives workflow input from parent even if same name.")]
public async Task Test1()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import child workflow.
var childFileName = $"Scenarios/ParentChildInputs/Workflows/child.json";
await _services.ImportWorkflowDefinitionAsync(childFileName);
// Import parent workflow.
var parentFileName = $"Scenarios/ParentChildInputs/Workflows/parent1.json";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(parentFileName);
// Execute.
var input = new Dictionary<string, object> { ["Input1"] = "Foo" };
await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId, input);
// Assert expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Parent: Foo", "Child: Bar" }, lines);
}
[Fact(DisplayName = "Child activity receives input from parent event if same name and does not use global workflow input.")]
public async Task Test2()
{
// Populate registries.
await _services.PopulateRegistriesAsync();
// Import child workflow.
var childFileName = $"Scenarios/ParentChildInputs/Workflows/child.json";
await _services.ImportWorkflowDefinitionAsync(childFileName);
// Import parent workflow.
var parentFileName = $"Scenarios/ParentChildInputs/Workflows/parent2.json";
var workflowDefinition = await _services.ImportWorkflowDefinitionAsync(parentFileName);
// Execute.
var input = new Dictionary<string, object> { ["Input1"] = "Foo" };
await _services.RunWorkflowUntilEndAsync(workflowDefinition.DefinitionId, input);
// Assert expected output.
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Parent: Foo", "Child: Foo" }, lines);
}
}

Some files were not shown because too many files have changed in this diff Show More