| File: Integration\Playwright\AspireMenuLayoutTests.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.Otlp.Model; using Aspire.Dashboard.Otlp.Storage; using Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure; using Aspire.TestUtilities; using Google.Protobuf.Collections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Playwright; using OpenTelemetry.Proto.Logs.V1; using Xunit; using static Aspire.Tests.Shared.Telemetry.TelemetryTestHelpers; namespace Aspire.Dashboard.Tests.Integration.Playwright; [RequiresFeature(TestFeature.Playwright)] public sealed class AspireMenuLayoutTests : PlaywrightTestsBase<DashboardServerFixture> { public AspireMenuLayoutTests(DashboardServerFixture dashboardServerFixture) : base(dashboardServerFixture) { } [Fact] [OuterloopTest("Resource-intensive Playwright browser test")] public async Task StructuredLogActions_TriggerRemainsVisibleAndMenuItemsSharePopup() { var repository = DashboardServerFixture.DashboardApp.Services.GetRequiredService<ITelemetryRepositoryWriter>(); await repository.AddLogsAsync(new AddContext(), new RepeatedField<ResourceLogs> { new ResourceLogs { Resource = CreateResource(name: "menu-resource", instanceId: "menu-resource"), ScopeLogs = { new ScopeLogs { Scope = CreateScope(), LogRecords = { CreateLogRecord(message: "menu test") } } } } }); await RunTestAsync(async page => { await page.SetViewportSizeAsync(1024, 400); await page.GotoAsync("/structuredlogs"); var trigger = page.Locator(".grid-action-container > fluent-button[aria-label='Actions']").First; await Assertions.Expect(trigger).ToBeVisibleAsync(); var cell = trigger.Locator("xpath=ancestor::td"); var triggerBefore = await trigger.BoundingBoxAsync(); var cellBefore = await cell.BoundingBoxAsync(); Assert.NotNull(triggerBefore); Assert.NotNull(cellBefore); await trigger.ClickAsync(); await Assertions.Expect(trigger).ToHaveAttributeAsync("aria-expanded", "true"); var menuList = trigger.Locator("xpath=following-sibling::fluent-menu/fluent-menu-list"); await Assertions.Expect(menuList).ToBeVisibleAsync(); var values = await trigger.EvaluateAsync<double[]>( """ trigger => { const cell = trigger.closest('td'); const container = trigger.parentElement; const menuList = container.querySelector('fluent-menu-list'); const items = [...menuList.querySelectorAll(':scope > fluent-menu-item')]; const triggerBounds = trigger.getBoundingClientRect(); const cellBounds = cell.getBoundingClientRect(); const containerBounds = container.getBoundingClientRect(); const itemBounds = items.map(item => item.getBoundingClientRect()); const menuListBounds = menuList.getBoundingClientRect(); return [ parseFloat(getComputedStyle(trigger).minWidth), triggerBounds.top - cellBounds.top, cellBounds.bottom - triggerBounds.bottom, containerBounds.height - cellBounds.height, items.length, new Set(itemBounds.map(bounds => Math.round(bounds.left))).size, Math.max(...itemBounds.map(bounds => bounds.right)) - Math.min(...itemBounds.map(bounds => bounds.right)), menuListBounds.left, window.innerWidth - menuListBounds.right, menuListBounds.top, window.innerHeight - menuListBounds.bottom ]; } """); Assert.Equal(32, values[0]); Assert.True(values[1] >= 0); Assert.True(values[2] >= 0); Assert.InRange(Math.Abs(values[3]), 0, 1); Assert.True(values[4] >= 2); Assert.Equal(1, values[5]); Assert.InRange(values[6], 0, 1); Assert.True(values[7] >= 0, $"Menu overflowed the left viewport edge by {-values[7]}px."); Assert.True(values[8] >= 0, $"Menu overflowed the right viewport edge by {-values[8]}px."); Assert.True(values[9] >= 0, $"Menu overflowed the top viewport edge by {-values[9]}px."); Assert.True(values[10] >= 0, $"Menu overflowed the bottom viewport edge by {-values[10]}px."); }); } }