Skip to content

Refactor dashboard automation tests and improve configuration handling #4238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ public async Task<DistributedApplication> ConfigureAsync<TEntryPoint>(
string[]? args = null,
Action<IDistributedApplicationTestingBuilder>? configureBuilder = null) where TEntryPoint : class
{
if (App is not null)
{
return App;
}

var builder = await DistributedApplicationTestingBuilder.CreateAsync<TEntryPoint>(
args: args ?? [],
configureBuilder: static (options, _) =>
{
options.DisableDashboard = false;
options.AllowUnsecuredTransport = true;
});

builder.Configuration["ASPIRE_ALLOW_UNSECURED_TRANSPORT"] = "true";

configureBuilder?.Invoke(builder);

App = await builder.BuildAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,21 @@ await InteractWithPageAsync(async page =>
// Login to the dashboard
await page.LoginAndWaitForRunningResourcesAsync(DashboardLoginToken);

var apiEllipsisButton = FluentDataGridSelector.Grid.Body.Row(3).Cell(6)
.Descendant("fluent-button:nth-of-type(3)");
await page.ClickAsync(apiEllipsisButton);
await page.ClickAsync(FluentDataGridSelector.Grid.Body.Row(3).Cell(6)
.Descendant("fluent-button:nth-of-type(3)"));

await page.HighlightElementAsync("fluent-anchored-region");

await page.SaveExploreScreenshotAsync("resource-actions.png");

await page.ClickAsync(DashboardSelectors.ResourcePage.ViewDetailsOption);
await page.ClickAsync(DashboardSelectors.ResourcePage.SplitPanel);

await page.AdjustSplitPanelsGridTemplateAsync();
await page.ClickAndDragShadowRootElementAsync(
DashboardSelectors.SplitPanels, DashboardSelectors.MedianId, (0, 20));
await page.RedactElementTextAsync(DashboardSelectors.ResourcePage.ResourceDetailsProjectPath);

await page.ClickAsync(apiEllipsisButton);
var apiEllipsisButton = FluentDataGridSelector.Grid.Body.Row(3).Cell(4)
.Descendant("fluent-button");

await page.ClickAsync(apiEllipsisButton, new() { Force = true });
await page.HoverAsync(DashboardSelectors.ResourcePage.ViewDetailsOption);
await page.HighlightElementAsync(DashboardSelectors.ResourcePage.ViewDetailsOption);

Expand Down Expand Up @@ -258,7 +256,7 @@ await InteractWithPageAsync(async page =>
[Fact, Trait("Capture", "resource-errors")]
public async Task CaptureResourcesWithErrorsImages()
{
await ConfigureAsync<SampleAppHost>([ "API_THROWS_EXCEPTION=true" ]);
await ConfigureAsync<SampleAppHost>(["API_THROWS_EXCEPTION=true"]);

await InteractWithPageAsync(async page =>
{
Expand Down Expand Up @@ -447,13 +445,13 @@ await InteractWithPageAsync(async page =>

// Delay beyond output cache, and invalidate then reload.
await Task.Delay(2_250);
await webPage.ReloadAsync();
await webPage.ReloadAsync(new() { WaitUntil = WaitUntilState.NetworkIdle });

await page.BringToFrontAsync();

await page.ClickAsync(DashboardSelectors.Nav.Traces);

await Task.Delay(1000);
await Task.Delay(1_000);

await page.SaveExploreScreenshotAsync("traces.png");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ internal static class ResourcePage
public const string StartResource = """fluent-button[title="Start resource"]""";
public const string SplitPanel = """fluent-button[title="Split horizontal"]""";

public const string ResourceDetailsProjectPath = """fluent-accordion-item [title^="C:"]:last-of-type""";
public const string ResourceDetailsProjectPath = """fluent-accordion-item [title^="C:"]:last-of-type, fluent-accordion-item [title^="E:"]:last-of-type""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ await page.EvaluateAsync($$"""

public static async Task LoginAsync(this IPage page, string token)
{
var response = await page.GotoAsync($"/login?t={token}");
var response = await page.GotoAsync(
$"/login?t={token}", new() { WaitUntil = WaitUntilState.NetworkIdle });

Assert.NotNull(response);
Assert.True(response.Ok, $"Failed to navigate to login page: {response.Status}");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Aspire.Dashboard.ScreenCapture;

namespace Aspire.Dashboard.ScreenCapture;

[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public class FluentDataGridSelector(string initialSelector)
{
private readonly StringBuilder _selector = new(initialSelector);
Expand Down Expand Up @@ -46,4 +48,6 @@ public FluentDataGridSelector Descendant(string selector)

public static implicit operator string(FluentDataGridSelector fluentDataGridSelector) =>
fluentDataGridSelector.ToString();

private string GetDebuggerDisplay() => ToString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
global using System.Text.Json;

global using Aspire.Dashboard.ScreenCapture.Extensions;

global using Aspire.Hosting;
global using Aspire.Hosting.ApplicationModel;
global using Aspire.Hosting.Testing;

global using Microsoft.AspNetCore.Hosting.Server;
global using Microsoft.AspNetCore.Hosting.Server.Features;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Playwright;

global using Xunit;

global using SampleAppHost = Projects.AspireSample_AppHost;
global using SampleAppHost = Projects.AspireSample_AppHost;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class PlaywrightFixture : IAsyncLifetime

public async Task InitializeAsync()
{
Assertions.SetDefaultExpectTimeout(10_000);
Assertions.SetDefaultExpectTimeout(30_000);

_playwright = await Playwright.CreateAsync();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,38 @@ public class PlaywrightTestsBase<TDashboardServerFixture>(AppHostTestFixture app
{
public AppHostTestFixture AppHostTestFixture { get; } = appHostTestFixture;
public PlaywrightFixture PlaywrightFixture { get; } = appHostTestFixture.PlaywrightFixture;
public string? DashboardUrl { get; private set; }
public string? DashboardUrl { get; internal set; }
public string DashboardLoginToken { get; private set; } = "";

private IBrowserContext? _context;

public Task<DistributedApplication> ConfigureAsync<TEntryPoint>(
public async Task<DistributedApplication> ConfigureAsync<TEntryPoint>(
string[]? args = null,
Action<IDistributedApplicationTestingBuilder>? configureBuilder = null) where TEntryPoint : class =>
AppHostTestFixture.ConfigureAsync<TEntryPoint>(args, builder =>
Action<IDistributedApplicationTestingBuilder>? configureBuilder = null) where TEntryPoint : class
{
var app = await AppHostTestFixture.ConfigureAsync<TEntryPoint>(args, builder =>
{
var aspNetCoreUrls = builder.Configuration["ASPNETCORE_URLS"];
var urls = aspNetCoreUrls is not null ? aspNetCoreUrls.Split(";") : [];

DashboardUrl = urls.FirstOrDefault();
DashboardLoginToken = builder.Configuration["AppHost:BrowserToken"] ?? "";

builder.Eventing.Subscribe<ResourceEndpointsAllocatedEvent>((@event, _) =>
{
if (@event.Resource.TryGetUrls(out var urls))
{
DashboardUrl = urls.FirstOrDefault()?.ToString() ?? "";
}

return Task.CompletedTask;
});

configureBuilder?.Invoke(builder);
});

var hostUrl = app.GetEndpoint("aspire-dashboard");
DashboardUrl = hostUrl.ToString();

return app;
}

public async Task InteractWithPageAsync(Func<IPage, Task> test, ViewportSize? size = null)
{
var page = await CreateNewPageAsync(size);
Expand All @@ -33,6 +46,12 @@ public async Task InteractWithPageAsync(Func<IPage, Task> test, ViewportSize? si
{
await test(page);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error during test interaction: {ex.Message}");

throw;
}
finally
{
await page.CloseAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

app.MapGet("/weatherforecast", () =>
{
if (builder.Configuration.GetValue<bool>("THROW_EXCEPTION"))
if (builder.Configuration.GetValue("THROW_EXCEPTION", false))
{
throw new ApplicationException("Error processing request");
}
Expand Down
Binary file modified docs/fundamentals/dashboard/media/explore/dashboard-help.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/fundamentals/dashboard/media/explore/project-graphs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/fundamentals/dashboard/media/explore/projects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading