|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Collections.Immutable;
using Aspire.Dashboard.Components.Controls;
using Aspire.Dashboard.Components.Tests.Shared;
using Aspire.Dashboard.Model;
using Aspire.Tests.Shared.DashboardModel;
using Bunit;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.FluentUI.AspNetCore.Components;
using Xunit;
namespace Aspire.Dashboard.Components.Tests.Controls;
[UseCulture("en-US")]
public class ResourceDetailsTests : DashboardTestContext
{
[Fact]
public async Task ClickMaskAllSwitch_UpdatedResource_MaskChanged()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource1 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource1);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel> (resource1.Name, resource1)]));
});
// Assert
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
});
var actionsButton = cut.Find(".resource-details-actions");
await actionsButton.ClickAsync(new MouseEventArgs());
var maskAllSwitch = cut.Find(".mask-all-switch");
// HACK. Calling OnClick on the element isn't triggering the event correctly. Instead, call OnClick on the component.
var item = cut.FindComponents<FluentMenuItem>().Single(s => s.Instance.Class == maskAllSwitch.Attributes["class"]!.Value);
await cut.InvokeAsync(() => item.Instance.OnClick.InvokeAsync(new MouseEventArgs()));
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.False(e.IsValueMasked);
});
var resource2 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar3", "value!", fromSpec: true)
}.ToImmutableArray());
cut.SetParametersAndRender(builder =>
{
builder.Add(p => p.Resource, resource2);
});
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar3", e.Name);
Assert.False(e.IsValueMasked);
});
}
[Fact]
public async Task ClickMaskAllSwitch_NewResource_MaskChanged()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource1 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource1);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel> (resource1.Name, resource1)]));
});
// Assert
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
});
var actionsButton = cut.Find(".resource-details-actions");
await actionsButton.ClickAsync(new MouseEventArgs());
var maskAllSwitch = cut.Find(".mask-all-switch");
// HACK. Calling OnClick on the element isn't triggering the event correctly. Instead, call OnClick on the component.
var item = cut.FindComponents<FluentMenuItem>().Single(s => s.Instance.Class == maskAllSwitch.Attributes["class"]!.Value);
await cut.InvokeAsync(() => item.Instance.OnClick.InvokeAsync(new MouseEventArgs()));
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.False(e.IsValueMasked);
});
var resource2 = ModelTestHelpers.CreateResource(
"app2",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar3", "value!", fromSpec: true)
}.ToImmutableArray());
cut.SetParametersAndRender(builder =>
{
builder.Add(p => p.Resource, resource2);
});
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar3", e.Name);
Assert.True(e.IsValueMasked);
});
}
[Fact]
public async Task ClickMaskEnvVarSwitch_UpdatedResource_MaskChanged()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource1 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource1);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel> (resource1.Name, resource1)]));
});
// Assert
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
});
var maskValueButton = cut.Find(".env-var-properties .grid-value-mask-button");
await maskValueButton.ClickAsync(new MouseEventArgs());
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.True(e.IsValueMasked);
});
var resource2 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar3", "value!", fromSpec: true)
}.ToImmutableArray());
cut.SetParametersAndRender(builder =>
{
builder.Add(p => p.Resource, resource2);
});
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar3", e.Name);
Assert.True(e.IsValueMasked);
});
}
[Fact]
public async Task ClickMaskEnvVarSwitch_NewResource_MaskChanged()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource1 = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource1);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel> (resource1.Name, resource1)]));
});
// Assert
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.Equal("value!", e.Value);
Assert.True(e.IsValueMasked);
});
var maskValueButton = cut.Find(".env-var-properties .grid-value-mask-button");
await maskValueButton.ClickAsync(new MouseEventArgs());
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.False(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.True(e.IsValueMasked);
});
var resource2 = ModelTestHelpers.CreateResource(
"app2",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("envvar1", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar2", "value!", fromSpec: true),
new EnvironmentVariableViewModel("envvar3", "value!", fromSpec: true)
}.ToImmutableArray());
cut.SetParametersAndRender(builder =>
{
builder.Add(p => p.Resource, resource2);
});
Assert.Collection(cut.Instance.FilteredEnvironmentVariables,
e =>
{
Assert.Equal("envvar1", e.Name);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar2", e.Name);
Assert.True(e.IsValueMasked);
},
e =>
{
Assert.Equal("envvar3", e.Name);
Assert.True(e.IsValueMasked);
});
}
[Fact]
public void FilteredEnvironmentVariables_SortedByName()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource = ModelTestHelpers.CreateResource(
"app1",
environment: new List<EnvironmentVariableViewModel>
{
new EnvironmentVariableViewModel("ZEBRA", "value1", fromSpec: true),
new EnvironmentVariableViewModel("alpha", "value2", fromSpec: true),
new EnvironmentVariableViewModel("Beta", "value3", fromSpec: true),
new EnvironmentVariableViewModel("GAMMA", "value4", fromSpec: true),
new EnvironmentVariableViewModel("delta", "value5", fromSpec: true)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel>(resource.Name, resource)]));
});
// Assert - verify environment variables are sorted alphabetically (case-insensitive)
var envVars = cut.Instance.FilteredEnvironmentVariables.ToList();
Assert.Equal(5, envVars.Count);
Assert.Collection(envVars,
e => Assert.Equal("alpha", e.Name),
e => Assert.Equal("Beta", e.Name),
e => Assert.Equal("delta", e.Name),
e => Assert.Equal("GAMMA", e.Name),
e => Assert.Equal("ZEBRA", e.Name));
}
[Fact]
public void FilteredVolumes_SortedBySource()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource = ModelTestHelpers.CreateResource(
"app1",
volumes: new List<VolumeViewModel>
{
new VolumeViewModel(0, "zebra-volume", "/target1", "bind", false),
new VolumeViewModel(1, "Alpha-Volume", "/target2", "volume", false),
new VolumeViewModel(2, "beta-volume", "/target3", "bind", true),
new VolumeViewModel(3, "GAMMA-Volume", "/target4", "volume", false),
new VolumeViewModel(4, "delta-volume", "/target5", "bind", false)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel>(resource.Name, resource)]));
});
// Assert - verify volumes are sorted alphabetically by source (case-insensitive)
var volumes = cut.Instance.FilteredVolumes.ToList();
Assert.Equal(5, volumes.Count);
Assert.Collection(volumes,
v => Assert.Equal("Alpha-Volume", v.Source),
v => Assert.Equal("beta-volume", v.Source),
v => Assert.Equal("delta-volume", v.Source),
v => Assert.Equal("GAMMA-Volume", v.Source),
v => Assert.Equal("zebra-volume", v.Source));
}
[Fact]
public void FilteredHealthReports_SortedByName()
{
// Arrange
ResourceSetupHelpers.SetupResourceDetails(this);
var resource = ModelTestHelpers.CreateResource(
"app1",
healthReports: new List<HealthReportViewModel>
{
new HealthReportViewModel("zebra-check", HealthStatus.Healthy, "OK", null),
new HealthReportViewModel("Alpha-Check", HealthStatus.Healthy, "OK", null),
new HealthReportViewModel("beta-check", HealthStatus.Degraded, "Warning", null),
new HealthReportViewModel("GAMMA-Check", HealthStatus.Healthy, "OK", null),
new HealthReportViewModel("delta-check", HealthStatus.Unhealthy, "Failed", null)
}.ToImmutableArray());
// Act
var cut = RenderComponent<ResourceDetails>(builder =>
{
builder.Add(p => p.ShowSpecOnlyToggle, true);
builder.Add(p => p.Resource, resource);
builder.Add(p => p.ResourceByName, new ConcurrentDictionary<string, ResourceViewModel>([new KeyValuePair<string, ResourceViewModel>(resource.Name, resource)]));
});
// Assert - verify health reports are sorted alphabetically by name (case-insensitive)
var healthReports = cut.Instance.FilteredHealthReports.ToList();
Assert.Equal(5, healthReports.Count);
Assert.Collection(healthReports,
h => Assert.Equal("Alpha-Check", h.Name),
h => Assert.Equal("beta-check", h.Name),
h => Assert.Equal("delta-check", h.Name),
h => Assert.Equal("GAMMA-Check", h.Name),
h => Assert.Equal("zebra-check", h.Name));
}
}
|