Skip to content

Commit a218e5e

Browse files
authored
Add browser token authentication Playwright tests (#5514)
1 parent b9bd67c commit a218e5e

File tree

7 files changed

+151
-24
lines changed

7 files changed

+151
-24
lines changed

src/Aspire.Dashboard/Components/Pages/Login.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</div>
3232
<div class="token-entry-footer">
3333
<a href="@NavigationManager.Uri" id="helpLink">@Loc[nameof(Dashboard.Resources.Login.WhereIsMyTokenLinkText)]</a>
34-
<FluentButton Appearance="Appearance.Accent" Type="ButtonType.Submit">@Loc[nameof(Dashboard.Resources.Login.LogInButtonText)]</FluentButton>
34+
<FluentButton Appearance="Appearance.Accent" Name="submit-token" Type="ButtonType.Submit">@Loc[nameof(Dashboard.Resources.Login.LogInButtonText)]</FluentButton>
3535
<FluentTooltip Anchor="helpLink" MaxWidth="650px">
3636
<div class="token-help-container">
3737
<div>

tests/Aspire.Dashboard.Tests/Integration/Playwright/AppBarTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Aspire.Dashboard.Resources;
5+
using Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
56
using Aspire.Workload.Tests;
67
using Microsoft.Playwright;
78
using Xunit;
89

910
namespace Aspire.Dashboard.Tests.Integration.Playwright;
1011

1112
[ActiveIssue("https://github.com/dotnet/aspire/issues/4623", typeof(PlaywrightProvider), nameof(PlaywrightProvider.DoesNotHavePlaywrightSupport))]
12-
public class AppBarTests : PlaywrightTestsBase
13+
public class AppBarTests : PlaywrightTestsBase<DashboardServerFixture>
1314
{
14-
public AppBarTests(DashboardServerFixture dashboardServerFixture, PlaywrightFixture playwrightFixture)
15-
: base(dashboardServerFixture, playwrightFixture)
15+
public AppBarTests(DashboardServerFixture dashboardServerFixture)
16+
: base(dashboardServerFixture)
1617
{
1718
}
1819

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Aspire.Dashboard.Configuration;
5+
using Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
6+
using Aspire.Hosting;
7+
using Aspire.Workload.Tests;
8+
using Microsoft.Playwright;
9+
using Xunit;
10+
11+
namespace Aspire.Dashboard.Tests.Integration.Playwright;
12+
13+
[ActiveIssue("https://github.com/dotnet/aspire/issues/4623", typeof(PlaywrightProvider), nameof(PlaywrightProvider.DoesNotHavePlaywrightSupport))]
14+
public class BrowserTokenAuthenticationTests : PlaywrightTestsBase<BrowserTokenAuthenticationTests.BrowserTokenDashboardServerFixture>
15+
{
16+
public class BrowserTokenDashboardServerFixture : DashboardServerFixture
17+
{
18+
public BrowserTokenDashboardServerFixture()
19+
{
20+
Configuration[DashboardConfigNames.DashboardFrontendAuthModeName.ConfigKey] = nameof(FrontendAuthMode.BrowserToken);
21+
Configuration[DashboardConfigNames.DashboardFrontendBrowserTokenName.ConfigKey] = "VALID_TOKEN";
22+
}
23+
}
24+
25+
public BrowserTokenAuthenticationTests(BrowserTokenDashboardServerFixture dashboardServerFixture)
26+
: base(dashboardServerFixture)
27+
{
28+
}
29+
30+
[Fact]
31+
public async Task BrowserToken_LoginPage_Success_RedirectToResources()
32+
{
33+
// Arrange
34+
await RunTestAsync(async page =>
35+
{
36+
// Act
37+
var response = await page.GotoAsync("/");
38+
var uri = new Uri(response!.Url);
39+
40+
Assert.Equal("/login?returnUrl=%2F", uri.PathAndQuery);
41+
42+
var tokenTextBox = page.GetByRole(AriaRole.Textbox);
43+
await tokenTextBox.FillAsync("VALID_TOKEN");
44+
45+
var submitButton = page.GetByRole(AriaRole.Button);
46+
await submitButton.ClickAsync();
47+
48+
// Assert
49+
await Assertions
50+
.Expect(page.GetByText(MockDashboardClient.TestResource1.DisplayName))
51+
.ToBeVisibleAsync();
52+
});
53+
}
54+
55+
[Fact]
56+
public async Task BrowserToken_LoginPage_Failure_DisplayFailureMessage()
57+
{
58+
// Arrange
59+
await RunTestAsync(async page =>
60+
{
61+
// Act
62+
var response = await page.GotoAsync("/");
63+
var uri = new Uri(response!.Url);
64+
65+
Assert.Equal("/login?returnUrl=%2F", uri.PathAndQuery);
66+
67+
var tokenTextBox = page.GetByRole(AriaRole.Textbox);
68+
await tokenTextBox.FillAsync("INVALID_TOKEN");
69+
70+
var submitButton = page.GetByRole(AriaRole.Button);
71+
await submitButton.ClickAsync();
72+
73+
// Assert
74+
await Assertions
75+
.Expect(page.GetByText("Invalid token"))
76+
.ToBeVisibleAsync();
77+
});
78+
}
79+
80+
[Fact]
81+
public async Task BrowserToken_QueryStringToken_Success_RestrictToResources()
82+
{
83+
// Arrange
84+
await RunTestAsync(async page =>
85+
{
86+
// Act
87+
await page.GotoAsync("/login?t=VALID_TOKEN");
88+
89+
// Assert
90+
await Assertions
91+
.Expect(page.GetByText(MockDashboardClient.TestResource1.DisplayName))
92+
.ToBeVisibleAsync();
93+
});
94+
}
95+
96+
[Fact]
97+
public async Task BrowserToken_QueryStringToken_Failure_DisplayLoginPage()
98+
{
99+
// Arrange
100+
await RunTestAsync(async page =>
101+
{
102+
// Act
103+
await page.GotoAsync("/login?t=INVALID_TOKEN");
104+
105+
var submitButton = page.GetByRole(AriaRole.Button);
106+
var name = await submitButton.GetAttributeAsync("name");
107+
108+
// Assert
109+
Assert.Equal("submit-token", name);
110+
});
111+
}
112+
}

tests/Aspire.Dashboard.Tests/Integration/Playwright/DashboardServerFixture.cs renamed to tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/DashboardServerFixture.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Reflection;
@@ -11,28 +11,40 @@
1111
using Microsoft.Extensions.DependencyInjection.Extensions;
1212
using Xunit;
1313

14-
namespace Aspire.Dashboard.Tests.Integration.Playwright;
14+
namespace Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
1515

1616
public class DashboardServerFixture : IAsyncLifetime
1717
{
18+
public Dictionary<string, string?> Configuration { get; }
19+
1820
public DashboardWebApplication DashboardApp { get; private set; } = null!;
1921

20-
public Task InitializeAsync()
22+
// Can't have multiple fixtures when one is generic. Workaround by nesting playwright fixture.
23+
public PlaywrightFixture PlaywrightFixture { get; }
24+
25+
public DashboardServerFixture()
2126
{
22-
const string aspireDashboardAssemblyName = "Aspire.Dashboard";
23-
var currentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name!;
24-
var currentAssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
25-
var aspireAssemblyDirectory = currentAssemblyDirectory.Replace(currentAssemblyName, aspireDashboardAssemblyName);
27+
PlaywrightFixture = new PlaywrightFixture();
2628

27-
var initialData = new Dictionary<string, string?>
29+
Configuration = new Dictionary<string, string?>
2830
{
2931
[DashboardConfigNames.DashboardFrontendUrlName.ConfigKey] = "http://127.0.0.1:0",
3032
[DashboardConfigNames.DashboardOtlpHttpUrlName.ConfigKey] = "http://127.0.0.1:0",
3133
[DashboardConfigNames.DashboardOtlpAuthModeName.ConfigKey] = nameof(OtlpAuthMode.Unsecured),
3234
[DashboardConfigNames.DashboardFrontendAuthModeName.ConfigKey] = nameof(FrontendAuthMode.Unsecured)
3335
};
36+
}
37+
38+
public async Task InitializeAsync()
39+
{
40+
await PlaywrightFixture.InitializeAsync();
41+
42+
const string aspireDashboardAssemblyName = "Aspire.Dashboard";
43+
var currentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name!;
44+
var currentAssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
45+
var aspireAssemblyDirectory = currentAssemblyDirectory.Replace(currentAssemblyName, aspireDashboardAssemblyName);
3446

35-
var config = new ConfigurationManager().AddInMemoryCollection(initialData).Build();
47+
var config = new ConfigurationManager().AddInMemoryCollection(Configuration).Build();
3648

3749
// Add services to the container.
3850
DashboardApp = new DashboardWebApplication(
@@ -53,11 +65,12 @@ public Task InitializeAsync()
5365
builder.Services.AddSingleton<IDashboardClient, MockDashboardClient>();
5466
});
5567

56-
return DashboardApp.StartAsync();
68+
await DashboardApp.StartAsync();
5769
}
5870

59-
public Task DisposeAsync()
71+
public async Task DisposeAsync()
6072
{
61-
return DashboardApp.DisposeAsync().AsTask();
73+
await DashboardApp.DisposeAsync();
74+
await PlaywrightFixture.DisposeAsync();
6275
}
6376
}

tests/Aspire.Dashboard.Tests/Integration/Playwright/MockDashboardClient.cs renamed to tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Aspire.Dashboard.Model;
77
using Google.Protobuf.WellKnownTypes;
88

9-
namespace Aspire.Dashboard.Tests.Integration.Playwright;
9+
namespace Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
1010

1111
public sealed class MockDashboardClient : IDashboardClient
1212
{
@@ -18,7 +18,7 @@ public sealed class MockDashboardClient : IDashboardClient
1818
CreationTimeStamp = DateTime.Now,
1919
Environment = ImmutableArray<EnvironmentVariableViewModel>.Empty,
2020
ResourceType = KnownResourceTypes.Project,
21-
Properties = new []
21+
Properties = new[]
2222
{
2323
new KeyValuePair<string, Value>(KnownProperties.Project.Path, new Value()
2424
{

tests/Aspire.Dashboard.Tests/Integration/Playwright/PlaywrightFixture.cs renamed to tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/PlaywrightFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using Microsoft.Playwright;
66
using Xunit;
77

8-
namespace Aspire.Dashboard.Tests.Integration.Playwright;
8+
namespace Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
99

1010
public class PlaywrightFixture : IAsyncLifetime
1111
{

tests/Aspire.Dashboard.Tests/Integration/Playwright/PlaywrightTestsBase.cs renamed to tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/PlaywrightTestsBase.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Microsoft.Playwright;
55
using Xunit;
66

7-
namespace Aspire.Dashboard.Tests.Integration.Playwright;
7+
namespace Aspire.Dashboard.Tests.Integration.Playwright.Infrastructure;
88

9-
public class PlaywrightTestsBase : IClassFixture<DashboardServerFixture>, IClassFixture<PlaywrightFixture>, IAsyncDisposable
9+
public class PlaywrightTestsBase<TDashboardServerFixture> : IClassFixture<TDashboardServerFixture>, IAsyncDisposable
10+
where TDashboardServerFixture : DashboardServerFixture
1011
{
1112
public DashboardServerFixture DashboardServerFixture { get; }
1213
public PlaywrightFixture PlaywrightFixture { get; }
1314

1415
private IBrowserContext? _context;
1516

16-
public PlaywrightTestsBase(DashboardServerFixture dashboardServerFixture, PlaywrightFixture playwrightFixture)
17+
public PlaywrightTestsBase(DashboardServerFixture dashboardServerFixture)
1718
{
1819
DashboardServerFixture = dashboardServerFixture;
19-
PlaywrightFixture = playwrightFixture;
20+
PlaywrightFixture = dashboardServerFixture.PlaywrightFixture;
2021
}
2122

2223
public async Task RunTestAsync(Func<IPage, Task> test)

0 commit comments

Comments
 (0)