File: Interactions\InteractionsProviderTests.cs
Web Access
Project: src\tests\Aspire.Dashboard.Components.Tests\Aspire.Dashboard.Components.Tests.csproj (Aspire.Dashboard.Components.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Threading.Channels;
using Aspire.Dashboard.Components.Tests.Shared;
using Aspire.Dashboard.Model;
using Aspire.DashboardService.Proto.V1;
using Bunit;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.FluentUI.AspNetCore.Components;
using Xunit;
 
namespace Aspire.Dashboard.Components.Tests.Interactions;
 
[UseCulture("en-US")]
public partial class InteractionsProviderTests : DashboardTestContext
{
    private readonly ITestOutputHelper _testOutputHelper;
 
    public InteractionsProviderTests(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }
 
    [Fact]
    public async Task Initialize_DashboardClientNotEnabled_ProviderDisabledAsync()
    {
        // Arrange
        var dashboardClient = new TestDashboardClient(isEnabled: false);
 
        SetupInteractionProviderServices(dashboardClient);
 
        // Act
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        // Assert
        cut.WaitForAssertion(() =>
        {
            Assert.False(instance._enabled);
        });
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    [Fact]
    public async Task Initialize_DashboardClientEnabled_ProviderEnabledAsync()
    {
        // Arrange
        var interactionsChannel = Channel.CreateUnbounded<WatchInteractionsResponseUpdate>();
 
        var dashboardClient = new TestDashboardClient(isEnabled: true, interactionChannelProvider: () => interactionsChannel);
 
        SetupInteractionProviderServices(dashboardClient);
 
        // Act
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        // Assert
        cut.WaitForAssertion(() =>
        {
            Assert.True(instance._enabled);
        });
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    [Fact]
    public async Task ReceiveData_MessageBoxOpen_OpenDialog()
    {
        // Arrange
        var interactionsChannel = Channel.CreateUnbounded<WatchInteractionsResponseUpdate>();
 
        var dialogReference = new DialogReference("abc", null!);
        var dashboardClient = new TestDashboardClient(isEnabled: true, interactionChannelProvider: () => interactionsChannel);
        var dialogService = new TestDialogService(onShowDialog: (data, parameters) => Task.FromResult<IDialogReference>(dialogReference));
 
        SetupInteractionProviderServices(dashboardClient: dashboardClient, dialogService: dialogService);
 
        // Act 1
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        await interactionsChannel.Writer.WriteAsync(new WatchInteractionsResponseUpdate
        {
            InteractionId = 1,
            MessageBox = new InteractionMessageBox()
        });
 
        // Assert 1
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() =>
        {
            var reference = instance._interactionDialogReference;
            if (reference == null)
            {
                return false;
            }
 
            return dialogReference == reference.Dialog && reference.InteractionId == 1;
        }, "Wait for dialog reference created.");
 
        // Act 2
        dialogReference.Dismiss(DialogResult.Ok(true));
 
        // Assert 2
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() => instance._interactionDialogReference == null, "Wait for dialog reference dismissed.");
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    [Fact]
    public async Task ReceiveData_MessageBoxOpenAndCompletion_OpenAndCloseDialog()
    {
        // Arrange
        var interactionsChannel = Channel.CreateUnbounded<WatchInteractionsResponseUpdate>();
 
        var dialogReference = new DialogReference("abc", null!);
        var dashboardClient = new TestDashboardClient(isEnabled: true, interactionChannelProvider: () => interactionsChannel);
        var dialogService = new TestDialogService(onShowDialog: (data, parameters) => Task.FromResult<IDialogReference>(dialogReference));
 
        SetupInteractionProviderServices(dashboardClient: dashboardClient, dialogService: dialogService);
 
        // Act 1
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        await interactionsChannel.Writer.WriteAsync(new WatchInteractionsResponseUpdate
        {
            InteractionId = 1,
            MessageBox = new InteractionMessageBox()
        });
 
        // Assert 1
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() =>
        {
            var reference = instance._interactionDialogReference;
            if (reference == null)
            {
                return false;
            }
 
            return dialogReference == reference.Dialog && reference.InteractionId == 1;
        }, "Wait for dialog reference created.");
 
        // Act 2
        await interactionsChannel.Writer.WriteAsync(new WatchInteractionsResponseUpdate
        {
            InteractionId = 1,
            Complete = new InteractionComplete()
        });
 
        // Assert 2
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() => instance._interactionDialogReference == null, "Wait for dialog reference dismissed.");
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    [Fact]
    public async Task ReceiveData_InputDialogOpenAndCancel_OpenDialogAndSendCompletion()
    {
        // Arrange
        var interactionsChannel = Channel.CreateUnbounded<WatchInteractionsResponseUpdate>();
        var sendInteractionUpdatesChannel = Channel.CreateUnbounded<WatchInteractionsRequestUpdate>();
 
        DialogParameters? dialogParameters = null;
        var dialogReference = new DialogReference("abc", null!);
        var dashboardClient = new TestDashboardClient(isEnabled: true,
            interactionChannelProvider: () => interactionsChannel,
            sendInteractionUpdateChannel: sendInteractionUpdatesChannel);
        var dialogService = new TestDialogService(onShowDialog: (data, parameters) =>
        {
            dialogParameters = parameters;
            return Task.FromResult<IDialogReference>(dialogReference);
        });
 
        SetupInteractionProviderServices(dashboardClient: dashboardClient, dialogService: dialogService);
 
        // Act 1
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        await interactionsChannel.Writer.WriteAsync(new WatchInteractionsResponseUpdate
        {
            InteractionId = 1,
            InputsDialog = new InteractionInputsDialog()
        });
 
        // Assert 1
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() =>
        {
            var reference = instance._interactionDialogReference;
            if (reference == null)
            {
                return false;
            }
 
            return dialogReference == reference.Dialog && reference.InteractionId == 1;
        }, "Wait for dialog reference created.");
 
        // Act 2
        Assert.NotNull(dialogParameters);
 
        await cut.InvokeAsync(() => dialogParameters.OnDialogResult.InvokeAsync(DialogResult.Cancel())).DefaultTimeout();
 
        var update = await sendInteractionUpdatesChannel.Reader.ReadAsync();
 
        Assert.Equal(1, update.InteractionId);
        Assert.Equal(WatchInteractionsRequestUpdate.KindOneofCase.Complete, update.KindCase);
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    [Fact]
    public async Task ReceiveData_InputDialogOpenAndSubmit_OpenDialogAndSendCompletion()
    {
        // Arrange
        var interactionsChannel = Channel.CreateUnbounded<WatchInteractionsResponseUpdate>();
        var sendInteractionUpdatesChannel = Channel.CreateUnbounded<WatchInteractionsRequestUpdate>();
 
        InteractionsInputsDialogViewModel? vm = null;
        DialogParameters? dialogParameters = null;
        var dialogReference = new DialogReference("abc", null!);
        var dashboardClient = new TestDashboardClient(isEnabled: true,
            interactionChannelProvider: () => interactionsChannel,
            sendInteractionUpdateChannel: sendInteractionUpdatesChannel);
        var dialogService = new TestDialogService(onShowDialog: (data, parameters) =>
        {
            vm = (InteractionsInputsDialogViewModel)data;
            dialogParameters = parameters;
            return Task.FromResult<IDialogReference>(dialogReference);
        });
 
        SetupInteractionProviderServices(dashboardClient: dashboardClient, dialogService: dialogService);
 
        // Act 1
        var cut = RenderComponent<Components.Interactions.InteractionsProvider>();
 
        var instance = cut.Instance;
 
        var response = new WatchInteractionsResponseUpdate
        {
            InteractionId = 1,
            InputsDialog = new InteractionInputsDialog()
        };
        await interactionsChannel.Writer.WriteAsync(response);
 
        // Assert 1
        await AsyncTestHelpers.AssertIsTrueRetryAsync(() =>
        {
            var reference = instance._interactionDialogReference;
            if (reference == null)
            {
                return false;
            }
 
            return dialogReference == reference.Dialog && reference.InteractionId == 1;
        }, "Wait for dialog reference created.");
 
        // Act 2
        Assert.NotNull(dialogParameters);
        Assert.NotNull(vm);
 
        await vm.OnSubmitCallback(response).DefaultTimeout();
 
        var update = await sendInteractionUpdatesChannel.Reader.ReadAsync();
 
        Assert.Equal(1, update.InteractionId);
        Assert.Equal(WatchInteractionsRequestUpdate.KindOneofCase.InputsDialog, update.KindCase);
 
        await instance.DisposeAsync().DefaultTimeout();
    }
 
    private void SetupInteractionProviderServices(TestDashboardClient? dashboardClient = null, TestDialogService? dialogService = null)
    {
        var loggerFactory = IntegrationTestHelpers.CreateLoggerFactory(_testOutputHelper);
 
        Services.AddLocalization();
        Services.AddSingleton<ILoggerFactory>(loggerFactory);
 
        Services.AddSingleton<IDialogService>(dialogService ?? new TestDialogService());
        Services.AddSingleton<IMessageService, MessageService>();
        Services.AddSingleton<IDashboardClient>(dashboardClient ?? new TestDashboardClient());
    }
}