-
-
Notifications
You must be signed in to change notification settings - Fork 229
test(blazor): Add Playwright E2E tests for navigation breadcrumbs #4908
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
base: feat/blazor-wasm-nav-breadcrumbs
Are you sure you want to change the base?
Changes from all commits
a8fb947
be0139e
ae6ee06
8573ca9
09d007c
ae89086
6a3029d
07c6608
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <Router AppAssembly="@typeof(App).Assembly"> | ||
| <Found Context="routeData"> | ||
| <RouteView RouteData="@routeData" DefaultLayout="@typeof(Shared.MainLayout)" /> | ||
| </Found> | ||
| <NotFound> | ||
| <p>Not found</p> | ||
| </NotFound> | ||
| </Router> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @page "/" | ||
|
|
||
| <h1 id="page-title">Home</h1> | ||
| <a href="second" id="nav-second">Go to Second</a> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @page "/second" | ||
|
|
||
| <h1 id="page-title">Second Page</h1> | ||
| <a href="trigger-capture" id="nav-trigger">Go to Trigger</a> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| @page "/trigger-capture" | ||
|
|
||
| <h1 id="page-title">Trigger Capture</h1> | ||
| <button id="btn-capture" @onclick="Capture">Capture Message</button> | ||
|
|
||
| @code { | ||
| private void Capture() | ||
| { | ||
| SentrySdk.CaptureMessage("playwright-test"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using Microsoft.AspNetCore.Components.Web; | ||
| using Microsoft.AspNetCore.Components.WebAssembly.Hosting; | ||
| using Sentry.AspNetCore.Blazor.WebAssembly.PlaywrightTests.TestApp; | ||
|
|
||
| var builder = WebAssemblyHostBuilder.CreateDefault(args); | ||
| builder.UseSentry(options => | ||
| { | ||
| // Fake DSN — Playwright intercepts requests before they reach the network | ||
| options.Dsn = "https://key@o0.ingest.sentry.io/0"; | ||
| options.AutoSessionTracking = false; | ||
| }); | ||
|
|
||
| builder.RootComponents.Add<App>("#app"); | ||
| builder.RootComponents.Add<HeadOutlet>("head::after"); | ||
|
|
||
| await builder.Build().RunAsync(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <IsTestProject>false</IsTestProject> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- Remove test-only global usings inherited from test/Directory.Build.props --> | ||
| <ItemGroup> | ||
| <Using Remove="Sentry.DsnSamples" /> | ||
| <Using Remove="Sentry.Testing" /> | ||
| <Using Remove="Sentry.Protocol" /> | ||
| <Using Remove="FluentAssertions" /> | ||
| <Using Remove="FluentAssertions.Execution" /> | ||
| <Using Remove="NSubstitute" /> | ||
| <Using Remove="NSubstitute.Core" /> | ||
| <Using Remove="NSubstitute.ExceptionExtensions" /> | ||
| <Using Remove="NSubstitute.ReturnsExtensions" /> | ||
| <Using Remove="Xunit" /> | ||
| <Using Remove="Xunit.Abstractions" /> | ||
| <Using Remove="VerifyXunit" /> | ||
| </ItemGroup> | ||
|
Comment on lines
+11
to
+25
|
||
|
|
||
| <!-- Exclude test SDK auto-generated entry point --> | ||
| <PropertyGroup> | ||
| <GenerateProgramFile>false</GenerateProgramFile> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0" /> | ||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.0" PrivateAssets="all" /> | ||
| <ProjectReference Include="..\..\src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| @inherits LayoutComponentBase | ||
|
|
||
| <main> | ||
| @Body | ||
| </main> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| @using Microsoft.AspNetCore.Components.Routing | ||
| @using Microsoft.AspNetCore.Components.Web | ||
| @using Sentry.AspNetCore.Blazor.WebAssembly.PlaywrightTests.TestApp | ||
| @using Sentry.AspNetCore.Blazor.WebAssembly.PlaywrightTests.TestApp.Shared |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
|
|
||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Playwright Test App</title> | ||
| <base href="/" /> | ||
| </head> | ||
|
|
||
| <body> | ||
| <div id="app">Loading...</div> | ||
| <script src="_framework/blazor.webassembly.js"></script> | ||
| </body> | ||
|
|
||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| using System.Collections.Concurrent; | ||
| using System.Diagnostics; | ||
| using System.Net; | ||
| using System.Net.Sockets; | ||
|
|
||
| namespace Sentry.AspNetCore.Blazor.WebAssembly.PlaywrightTests; | ||
|
|
||
| internal sealed class BlazorWasmTestApp : IAsyncDisposable | ||
| { | ||
| private Process? _process; | ||
| private readonly ConcurrentQueue<string> _output = new(); | ||
|
|
||
| public string BaseUrl { get; private set; } = null!; | ||
|
|
||
| public async Task StartAsync() | ||
| { | ||
| var port = GetFreePort(); | ||
| BaseUrl = $"http://localhost:{port}"; | ||
|
|
||
| var projectPath = Path.GetFullPath( | ||
| Path.Combine(AppContext.BaseDirectory, | ||
| "..", "..", "..", "..", | ||
| "Sentry.AspNetCore.Blazor.WebAssembly.PlaywrightTests.TestApp")); | ||
|
|
||
| _process = new Process | ||
| { | ||
| StartInfo = new ProcessStartInfo | ||
| { | ||
| FileName = "dotnet", | ||
| Arguments = $"run --project \"{projectPath}\" --urls {BaseUrl}", | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| UseShellExecute = false, | ||
| } | ||
| }; | ||
| _process.OutputDataReceived += (_, e) => { if (e.Data != null) _output.Enqueue($"[stdout] {e.Data}"); }; | ||
| _process.ErrorDataReceived += (_, e) => { if (e.Data != null) _output.Enqueue($"[stderr] {e.Data}"); }; | ||
| _process.Start(); | ||
| _process.BeginOutputReadLine(); | ||
| _process.BeginErrorReadLine(); | ||
|
|
||
| using var http = new HttpClient(); | ||
| var timeout = TimeSpan.FromSeconds(180); | ||
| var sw = Stopwatch.StartNew(); | ||
| while (sw.Elapsed < timeout) | ||
| { | ||
| if (_process.HasExited) | ||
| { | ||
| var logs = string.Join(Environment.NewLine, _output); | ||
| throw new InvalidOperationException( | ||
| $"Blazor WASM test app exited with code {_process.ExitCode} before becoming ready. Output:{Environment.NewLine}{logs}"); | ||
| } | ||
|
|
||
| try | ||
| { | ||
| var response = await http.GetAsync(BaseUrl); | ||
| if (response.IsSuccessStatusCode) | ||
| { | ||
| return; | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| // Server not ready yet | ||
| } | ||
| await Task.Delay(500); | ||
| } | ||
|
|
||
| var timeoutLogs = string.Join(Environment.NewLine, _output); | ||
| throw new TimeoutException( | ||
| $"Blazor WASM test app did not start within {(int)timeout.TotalSeconds}s at {BaseUrl}. Output:{Environment.NewLine}{timeoutLogs}"); | ||
| } | ||
|
|
||
| private static int GetFreePort() | ||
| { | ||
| using var listener = new TcpListener(IPAddress.Loopback, 0); | ||
| listener.Start(); | ||
| var port = ((IPEndPoint)listener.LocalEndpoint).Port; | ||
| listener.Stop(); | ||
| return port; | ||
| } | ||
|
|
||
| public async ValueTask DisposeAsync() | ||
| { | ||
| if (_process is { HasExited: false }) | ||
| { | ||
| _process.Kill(entireProcessTree: true); | ||
| await _process.WaitForExitAsync(); | ||
| } | ||
| _process?.Dispose(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestApp project missing from SentryNoSamples solution filter
Low Severity
The
SentryNoSamples.slnffilter includes thePlaywrightTestsproject but omits its build dependencyPlaywrightTests.TestApp. BothSentryAspNetCore.slnfandSentryNoMobile.slnfcorrectly include both projects. This inconsistency means the TestApp won't be visible when opening this solution filter in the IDE, despite being aProjectReferencedependency of the included test project.Additional Locations (2)
SentryAspNetCore.slnf#L26-L28SentryNoMobile.slnf#L54-L56There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By design —
SentryNoSamples.slnfexcludes**/*TestApp*which catches the TestApp project. MSBuild still resolves it as aProjectReferencedependency of the PlaywrightTests project, so builds work correctly. The filter just controls what's visible in the IDE.