File: Model\InputViewModelTests.cs
Web Access
Project: src\tests\Aspire.Dashboard.Tests\Aspire.Dashboard.Tests.csproj (Aspire.Dashboard.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Dashboard.Model.Interaction;
using Aspire.DashboardService.Proto.V1;
using Xunit;
 
namespace Aspire.Dashboard.Tests.Model;
 
public class InputViewModelTests
{
    [Fact]
    public void InputViewModel_ChoiceWithoutPlaceholder_DefaultsToFirstOption()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice,
        };
        input.Options.Add("red", "Red");
        input.Options.Add("blue", "Blue");
        input.Options.Add("green", "Green");
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert
        Assert.Equal("red", viewModel.Value);
    }
 
    [Fact]
    public void InputViewModel_ChoiceWithPlaceholder_DoesNotDefaultToFirstOption()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice,
            Placeholder = "Select a color"
        };
        input.Options.Add("red", "Red");
        input.Options.Add("blue", "Blue");
        input.Options.Add("green", "Green");
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert - proto strings default to empty string, not null
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
    }
 
    [Fact]
    public void InputViewModel_ChoiceWithExistingValue_KeepsExistingValue()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice,
            Value = "blue"
        };
        input.Options.Add("red", "Red");
        input.Options.Add("blue", "Blue");
        input.Options.Add("green", "Green");
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert
        Assert.Equal("blue", viewModel.Value);
    }
 
    [Fact]
    public void InputViewModel_ChoiceWithEmptyOptions_DoesNotSetValue()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice
        };
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert - proto strings default to empty string, not null
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
    }
 
    [Fact]
    public void InputViewModel_NonChoiceInput_DoesNotSetValue()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Enter Text",
            InputType = InputType.Text
        };
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert - proto strings default to empty string, not null
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
    }
 
    [Fact]
    public void SetInput_ChoiceWithoutPlaceholder_DefaultsToFirstOption()
    {
        // Arrange
        var initialInput = new InteractionInput
        {
            Label = "Enter Text",
            InputType = InputType.Text
        };
        var viewModel = new InputViewModel(initialInput);
 
        var choiceInput = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice
        };
        choiceInput.Options.Add("red", "Red");
        choiceInput.Options.Add("blue", "Blue");
 
        // Act
        viewModel.SetInput(choiceInput);
 
        // Assert
        Assert.Equal("red", viewModel.Value);
    }
 
    [Fact]
    public void SetInput_ChoiceWithPlaceholder_DoesNotDefaultToFirstOption()
    {
        // Arrange
        var initialInput = new InteractionInput
        {
            Label = "Enter Text",
            InputType = InputType.Text
        };
        var viewModel = new InputViewModel(initialInput);
 
        var choiceInput = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice,
            Placeholder = "Select a color"
        };
        choiceInput.Options.Add("red", "Red");
        choiceInput.Options.Add("blue", "Blue");
 
        // Act
        viewModel.SetInput(choiceInput);
 
        // Assert - proto strings default to empty string, not null
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
    }
 
    [Fact]
    public void InputViewModel_ChoiceWithAllowCustomChoice_DoesNotDefaultToFirstOption()
    {
        // Arrange
        var input = new InteractionInput
        {
            Label = "Choose Color",
            InputType = InputType.Choice,
            AllowCustomChoice = true
        };
        input.Options.Add("red", "Red");
        input.Options.Add("blue", "Blue");
        input.Options.Add("green", "Green");
 
        // Act
        var viewModel = new InputViewModel(input);
 
        // Assert - When AllowCustomChoice is true, value should not default
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
    }
 
    [Fact]
    public void ChoiceVersion_IncrementedWhenOptionsChange()
    {
        var input = new InteractionInput
        {
            Label = "Choose",
            InputType = InputType.Choice,
            Placeholder = "Select"
        };
        input.Options.Add("a", "A");
 
        var viewModel = new InputViewModel(input);
        var initialVersion = viewModel.ChoiceVersion;
 
        var updatedInput = new InteractionInput
        {
            Label = "Choose",
            InputType = InputType.Choice,
            Placeholder = "Select"
        };
        updatedInput.Options.Add("b", "B");
        updatedInput.Options.Add("c", "C");
 
        viewModel.SetInput(updatedInput);
 
        Assert.Equal(initialVersion + 1, viewModel.ChoiceVersion);
    }
 
    [Fact]
    public void ChoiceVersion_NotIncrementedForNonChoiceInput()
    {
        var input = new InteractionInput
        {
            Label = "Name",
            InputType = InputType.Text
        };
 
        var viewModel = new InputViewModel(input);
        var initialVersion = viewModel.ChoiceVersion;
 
        var updatedInput = new InteractionInput
        {
            Label = "Name",
            InputType = InputType.Text,
            Value = "test"
        };
 
        viewModel.SetInput(updatedInput);
 
        Assert.Equal(initialVersion, viewModel.ChoiceVersion);
    }
 
    [Fact]
    public void ChoiceVersion_NotIncrementedWhenOptionsUnchanged()
    {
        var input = new InteractionInput
        {
            Label = "Choose",
            InputType = InputType.Choice,
            Placeholder = "Select"
        };
        input.Options.Add("a", "A");
        input.Options.Add("b", "B");
 
        var viewModel = new InputViewModel(input);
        var initialVersion = viewModel.ChoiceVersion;
 
        var sameInput = new InteractionInput
        {
            Label = "Choose",
            InputType = InputType.Choice,
            Placeholder = "Select"
        };
        sameInput.Options.Add("a", "A");
        sameInput.Options.Add("b", "B");
 
        viewModel.SetInput(sameInput);
 
        Assert.Equal(initialVersion, viewModel.ChoiceVersion);
    }
 
    [Fact]
    public void SetInput_DisabledInputUsesIncomingValue()
    {
        var input = new InteractionInput
        {
            Label = "Location",
            InputType = InputType.Choice,
            Placeholder = "Select a location",
            Disabled = true
        };
        var viewModel = new InputViewModel(input);
 
        var updatedInput = new InteractionInput
        {
            Label = "Location",
            InputType = InputType.Choice,
            Placeholder = "Select a location",
            Disabled = true,
            Value = "westus"
        };
        updatedInput.Options.Add("westus", "West US");
 
        viewModel.SetInput(updatedInput);
 
        Assert.Equal("westus", viewModel.Value);
    }
 
    [Fact]
    public void SetInput_DisabledToEnabledInputUsesIncomingValue()
    {
        var input = new InteractionInput
        {
            Label = "Subscription",
            InputType = InputType.Choice,
            Placeholder = "Select subscription ID",
            Disabled = true
        };
        var viewModel = new InputViewModel(input);
 
        var updatedInput = new InteractionInput
        {
            Label = "Subscription",
            InputType = InputType.Choice,
            Placeholder = "Select subscription ID",
            Value = "12345678-1234-1234-1234-123456789012"
        };
        updatedInput.Options.Add("12345678-1234-1234-1234-123456789012", "Test Subscription");
 
        viewModel.SetInput(updatedInput);
 
        Assert.False(viewModel.InputDisabled);
        Assert.Equal("12345678-1234-1234-1234-123456789012", viewModel.Value);
    }
 
    [Fact]
    public void SetInput_EnabledToDisabledInputUsesIncomingValue()
    {
        var input = new InteractionInput
        {
            Label = "Location",
            InputType = InputType.Choice,
            Value = "local"
        };
        var viewModel = new InputViewModel(input);
 
        var updatedInput = new InteractionInput
        {
            Label = "Location",
            InputType = InputType.Choice,
            Disabled = true,
            Value = "server"
        };
 
        viewModel.SetInput(updatedInput);
 
        Assert.True(viewModel.InputDisabled);
        Assert.Equal("server", viewModel.Value);
    }
 
    [Fact]
    public void SetInput_LoadingCompletionUsesIncomingValue()
    {
        var input = new InteractionInput
        {
            Label = "Subscription",
            InputType = InputType.Choice,
            Loading = true,
            Value = "local"
        };
        var viewModel = new InputViewModel(input);
 
        var updatedInput = new InteractionInput
        {
            Label = "Subscription",
            InputType = InputType.Choice,
            Value = "server"
        };
        updatedInput.Options.Add("server", "Server");
 
        viewModel.SetInput(updatedInput);
 
        Assert.False(viewModel.InputDisabled);
        Assert.Equal("server", viewModel.Value);
    }
 
    [Fact]
    public void SetInput_EnabledInputPreservesLocalValue()
    {
        var input = new InteractionInput
        {
            Label = "Name",
            InputType = InputType.Text,
            Value = "local"
        };
        var viewModel = new InputViewModel(input);
 
        var updatedInput = new InteractionInput
        {
            Label = "Name",
            InputType = InputType.Text,
            Value = "server"
        };
 
        viewModel.SetInput(updatedInput);
 
        Assert.Equal("local", viewModel.Value);
    }
 
    [Fact]
    public void InputViewModel_File_DefaultsToEmptyValue()
    {
        var input = new InteractionInput
        {
            Label = "Select File",
            InputType = InputType.File
        };
 
        var viewModel = new InputViewModel(input);
 
        Assert.True(string.IsNullOrEmpty(viewModel.Value));
        Assert.Empty(viewModel.FileReferences);
    }
 
    [Fact]
    public void InputViewModel_File_SetFileReferencesSerializesToValue()
    {
        var input = new InteractionInput
        {
            Label = "Select File",
            InputType = InputType.File
        };
        var viewModel = new InputViewModel(input);
 
        viewModel.SetFileReferences([
            new FileReferenceViewModel { Id = "abc123", Name = "readme.txt" }
        ]);
 
        Assert.Single(viewModel.FileReferences);
        Assert.Equal("readme.txt", viewModel.FileReferences[0].Name);
        Assert.Contains("abc123", viewModel.Value);
        Assert.Contains("readme.txt", viewModel.Value);
    }
 
    [Fact]
    public void InputViewModel_File_SetInputPreservesFileReferencesWhenValueIsPreserved()
    {
        var initialInput = new InteractionInput
        {
            Label = "Select File",
            InputType = InputType.File,
            Value = "[{\"Id\":\"id1\",\"Name\":\"local-file.txt\"}]"
        };
        var viewModel = new InputViewModel(initialInput);
        viewModel.SetFileReferences([
            new FileReferenceViewModel { Id = "id1", Name = "local-file.txt" }
        ]);
 
        var newInput = new InteractionInput
        {
            Label = "Select Another File",
            InputType = InputType.File,
            Value = string.Empty,
        };
 
        viewModel.SetInput(newInput);
 
        Assert.Equal("[{\"Id\":\"id1\",\"Name\":\"local-file.txt\"}]", viewModel.Value);
        Assert.Single(viewModel.FileReferences);
        Assert.Equal("local-file.txt", viewModel.FileReferences[0].Name);
    }
}