File: Integration\Playwright\OverviewLayoutTests.cs
Web Access
Project: src\tests\Aspire.Dashboard.Playwright.Tests\Aspire.Dashboard.Playwright.Tests.csproj (Aspire.Dashboard.Playwright.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.Collections.Concurrent;
using Aspire.Dashboard.Resources;
using Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
using Aspire.Dashboard.Utils;
using Aspire.TestUtilities;
using Microsoft.Playwright;
using Xunit;
using ResourceStrings = Aspire.Dashboard.Resources.Resources;
 
namespace Aspire.Dashboard.Tests.Integration.Playwright;
 
[RequiresFeature(TestFeature.Playwright)]
public class OverviewLayoutTests(ResourcesTests.ResourcesDashboardServerFixture fixture)
    : PlaywrightTestsBase<ResourcesTests.ResourcesDashboardServerFixture>(fixture)
{
    [Theory]
    [InlineData(390, 844, "Light")]
    [InlineData(390, 844, "Dark")]
    [InlineData(768, 1024, "Light")]
    [InlineData(768, 1024, "Dark")]
    [InlineData(1440, 900, "Light")]
    [InlineData(1440, 900, "Dark")]
    [OuterloopTest("Resource-intensive Playwright browser test")]
    public async Task RemovedCards_UsesSummaryAndPreservesScopeDetailsAndKeyboard(int width, int height, string theme)
    {
        await RunTestAsync(async page =>
        {
            var errors = new ConcurrentQueue<string>();
            page.PageError += (_, error) => errors.Enqueue(error);
            page.Console += (_, message) =>
            {
                if (message.Type == "error")
                {
                    errors.Enqueue(message.Text);
                }
            };
            await page.SetViewportSizeAsync(width, height);
            await page.EmulateMediaAsync(new() { ReducedMotion = ReducedMotion.Reduce });
            await page.Context.AddCookiesAsync([new Cookie { Name = "currentTheme", Value = theme, Url = DashboardServerFixture.DashboardApp.FrontendSingleEndPointAccessor().GetResolvedAddress() }]);
            await page.GotoAsync("/resources");
            await Assertions.Expect(page.Locator(".home-hero")).ToBeVisibleAsync();
            await page.EvaluateAsync("key => localStorage.setItem(key, JSON.stringify('Cards'))", BrowserStorageKeys.GroupOverview);
            await page.ReloadAsync();
            await Assertions.Expect(page.Locator(".home-hero")).ToBeVisibleAsync();
            await page.WaitForFunctionAsync("key => JSON.parse(localStorage.getItem(key)) === 'Summary'", BrowserStorageKeys.GroupOverview);
            await Assertions.Expect(page.Locator(".home-hero-headline")).ToHaveTextAsync("All 3 resources are running");
            var layout = page.GetByRole(AriaRole.Group, new() { Name = ResourceStrings.LayoutLabel, Exact = true });
            await Assertions.Expect(layout.GetByRole(AriaRole.Button)).ToHaveCountAsync(2);
            var summary = layout.GetByRole(AriaRole.Button, new() { Name = Dialogs.SettingsDialogGroupOverviewSummary, Exact = true });
            var graph = layout.GetByRole(AriaRole.Button, new() { Name = ResourceStrings.GraphPageTitle, Exact = true });
            await Assertions.Expect(summary).ToHaveAttributeAsync("aria-pressed", "true");
            await Assertions.Expect(page.Locator(".resource-card-grid, .home-resource-table")).ToHaveCountAsync(0);
            await AssertFitsAsync(layout);
 
            await graph.FocusAsync();
            await page.Keyboard.PressAsync("Enter");
            await Assertions.Expect(page.Locator(".home-resource-graph-layout")).ToBeVisibleAsync();
            await Assertions.Expect(graph).ToHaveAttributeAsync("aria-pressed", "true");
            await summary.FocusAsync();
            await page.Keyboard.PressAsync("Space");
            await Assertions.Expect(page.Locator(".home-hero")).ToBeVisibleAsync();
            await Assertions.Expect(summary).ToBeFocusedAsync();
 
            await page.GotoAsync("/graph");
            await Assertions.Expect(page.Locator(".home-resource-graph-layout")).ToBeVisibleAsync();
            await summary.ClickAsync();
            await Assertions.Expect(page.Locator(".home-hero")).ToBeVisibleAsync();
 
            await page.GotoAsync("/resources?resource=apigateway&resource=TestResource");
            await Assertions.Expect(page.Locator(".home-hero-headline")).ToHaveTextAsync("All 2 resources are running");
            await Assertions.Expect(page.Locator(".home-activity")).ToBeVisibleAsync();
            await Assertions.Expect(page.Locator(".home-insights")).ToHaveCountAsync(1);
            await Assertions.Expect(page.Locator(".home-problems")).ToHaveCountAsync(1);
            await page.GotoAsync("/resources/TestResource");
            await Assertions.Expect(page.Locator(".overview-grid")).ToBeVisibleAsync();
            await Assertions.Expect(page.Locator(".home-hero")).ToHaveCountAsync(0);
            await page.GoBackAsync();
            await Assertions.Expect(page.Locator(".home-hero-headline")).ToHaveTextAsync("All 2 resources are running");
 
            await page.GotoAsync("/resources");
            await Assertions.Expect(page.Locator(".home-hero-headline")).ToHaveTextAsync("All 3 resources are running");
            if (width <= 768)
            {
                await page.Locator(".resource-picker").ClickAsync();
            }
            await page.Locator("#resourcePaneGroupingButton").ClickAsync();
            await page.GetByRole(AriaRole.Menuitemradio, new() { Name = Layout.ResourcePaneGroupingResourceType, Exact = true }).ClickAsync();
            var group = page.Locator(".resource-tag-group").Filter(new() { Has = page.GetByText("Project", new() { Exact = true }) });
            await group.Locator(".resource-tag-link").ClickAsync();
            await Assertions.Expect(page.Locator(".home-hero-headline")).ToHaveTextAsync("All 2 resources are running");
 
            await OpenSettingsAsync(page);
            var options = page.GetByRole(AriaRole.Group, new() { Name = Dialogs.SettingsDialogGroupOverview, Exact = true });
            await Assertions.Expect(options.GetByRole(AriaRole.Button)).ToHaveCountAsync(2);
            await Assertions.Expect(options.GetByRole(AriaRole.Button, new() { Name = Dialogs.SettingsDialogGroupOverviewSummary, Exact = true })).ToHaveAttributeAsync("aria-pressed", "true");
            await AssertFitsAsync(options);
            await page.GetByRole(AriaRole.Button, new() { Name = Dialogs.DialogCloseButtonText, Exact = true }).Last.ClickAsync();
            Assert.Empty(errors);
        });
    }
 
    private static async Task AssertFitsAsync(ILocator control)
    {
        var fits = await control.EvaluateAsync<bool>("el => { const r = el.getBoundingClientRect(); return r.left >= 0 && r.right <= innerWidth && el.scrollWidth <= el.clientWidth + 1; }");
        Assert.True(fits);
    }
 
    private static async Task OpenSettingsAsync(IPage page)
    {
        var settings = page.GetByRole(AriaRole.Button, new() { Name = Layout.MainLayoutLaunchSettings, Exact = true });
        if (await settings.IsVisibleAsync())
        {
            await settings.ClickAsync();
        }
        else
        {
            await page.Locator(".navigation-button").ClickAsync();
            await page.GetByRole(AriaRole.Menuitem, new() { Name = Layout.MainLayoutLaunchSettings, Exact = true }).ClickAsync();
        }
        await Assertions.Expect(page.GetByRole(AriaRole.Dialog)).ToBeVisibleAsync();
    }
}