diff --git a/tests/DrawTogether.End2End.Tests/DrawingMouseInteractionTests.cs b/tests/DrawTogether.End2End.Tests/DrawingMouseInteractionTests.cs
index 1bd28ed..6d478d9 100644
--- a/tests/DrawTogether.End2End.Tests/DrawingMouseInteractionTests.cs
+++ b/tests/DrawTogether.End2End.Tests/DrawingMouseInteractionTests.cs
@@ -49,8 +49,10 @@ public async Task CanDrawWithMouse()
_output.WriteLine($"Navigating to {endpoint}");
- // Navigate to the app
- await page.GotoAsync(endpoint.ToString());
+ // Navigate to the app. Retry on ERR_NETWORK_CHANGED: a freshly launched
+ // Chromium can abort its first navigation when Aspire's container network
+ // is still settling.
+ await GotoWithNetworkChangeRetryAsync(page, endpoint.ToString(), output: _output);
// Handle authentication if needed - example for form-based auth
await HandleNewUserRegistration(page, endpoint, _output);
diff --git a/tests/DrawTogether.End2End.Tests/DrawingTouchInteractionTests.cs b/tests/DrawTogether.End2End.Tests/DrawingTouchInteractionTests.cs
index d39c8a6..6f5cef8 100644
--- a/tests/DrawTogether.End2End.Tests/DrawingTouchInteractionTests.cs
+++ b/tests/DrawTogether.End2End.Tests/DrawingTouchInteractionTests.cs
@@ -1,4 +1,4 @@
-using DrawTogether.End2End.Tests.Util;
+using DrawTogether.End2End.Tests.Util;
using Microsoft.Playwright;
using Xunit.Abstractions;
using static DrawTogether.End2End.Tests.Util.DrawTogetherPlaywrightHelpers;
@@ -63,8 +63,10 @@ public async Task CanDrawWithTouch(int viewPortX, int viewPortY)
_output.WriteLine($"Navigating to {endpoint}");
- // Navigate to the app
- await page.GotoAsync(endpoint.ToString());
+ // Navigate to the app. Retry on ERR_NETWORK_CHANGED: a freshly launched
+ // Chromium can abort its first navigation when Aspire's container network
+ // is still settling.
+ await GotoWithNetworkChangeRetryAsync(page, endpoint.ToString(), output: _output);
// Handle authentication if needed - example for form-based auth
await HandleNewUserRegistration(page, endpoint, _output);
diff --git a/tests/DrawTogether.End2End.Tests/Util/DrawTogetherPlaywrightHelpers.cs b/tests/DrawTogether.End2End.Tests/Util/DrawTogetherPlaywrightHelpers.cs
index 1ff262f..c42bc8e 100644
--- a/tests/DrawTogether.End2End.Tests/Util/DrawTogetherPlaywrightHelpers.cs
+++ b/tests/DrawTogether.End2End.Tests/Util/DrawTogetherPlaywrightHelpers.cs
@@ -5,6 +5,42 @@ namespace DrawTogether.End2End.Tests.Util;
public static class DrawTogetherPlaywrightHelpers
{
+ ///
+ /// The first navigation from a freshly launched Chromium can be aborted with
+ /// net::ERR_NETWORK_CHANGED: the browser tears down in-flight requests when it
+ /// observes a host network-state change, and Docker creating/removing container bridge
+ /// interfaces while Aspire starts resources produces exactly that.
+ /// See https://github.com/microsoft/playwright/issues/13062 (same signature, retry clears it)
+ /// and https://github.com/Grizzly-Endeavors/residuum/issues/592 (Docker bridge churn).
+ /// The abort happens before the page renders anything, so retrying is side-effect free.
+ ///
+ private const string NetworkChangedError = "ERR_NETWORK_CHANGED";
+
+ public static async Task GotoWithNetworkChangeRetryAsync(
+ IPage page,
+ string url,
+ PageGotoOptions? options = null,
+ ITestOutputHelper? output = null,
+ int maxAttempts = 3)
+ {
+ for (var attempt = 1; ; attempt++)
+ {
+ try
+ {
+ return await page.GotoAsync(url, options);
+ }
+ catch (PlaywrightException ex) when (
+ attempt < maxAttempts &&
+ ex.Message.Contains(NetworkChangedError, StringComparison.Ordinal))
+ {
+ output?.WriteLine(
+ $"Navigation to {url} aborted by {NetworkChangedError} " +
+ $"(attempt {attempt}/{maxAttempts}); retrying.");
+ await Task.Delay(TimeSpan.FromMilliseconds(250 * attempt));
+ }
+ }
+ }
+
public static async Task LocateDrawingBox(IPage page, ITestOutputHelper output)
{
// Wait for the canvas to be present