// 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);
}
}