test(e2e): cap live browser contexts to bound Blazor circuit pressure (fixes full-suite timeouts); import negative-test review fixes
This commit is contained in:
@@ -27,6 +27,23 @@ public class PlaywrightFixture : IAsyncLifetime
|
||||
public IPlaywright Playwright { get; private set; } = null!;
|
||||
public IBrowser Browser { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Live browser contexts created by <see cref="NewPageAsync"/>, oldest first.
|
||||
/// Capped to <see cref="MaxRetainedContexts"/> so finished tests' contexts are
|
||||
/// closed eagerly rather than leaking for the whole run (see <see cref="NewPageAsync"/>).
|
||||
/// </summary>
|
||||
private readonly List<IBrowserContext> _contexts = new();
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of browser contexts kept open at once. Each context holds a live
|
||||
/// Blazor Server SignalR circuit on the Central UI; the full suite runs serially within
|
||||
/// the <c>Playwright</c> collection, so contexts from already-finished tests can be closed
|
||||
/// safely. Leaving every context open accumulated ~one circuit per test and slowed late
|
||||
/// tests into navigation/visibility timeouts. A small cap (covering any single test that
|
||||
/// opens more than one page) keeps server-side circuit pressure flat across the run.
|
||||
/// </summary>
|
||||
private const int MaxRetainedContexts = 4;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
|
||||
@@ -40,12 +57,36 @@ public class PlaywrightFixture : IAsyncLifetime
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new browser context and page. Each test gets an isolated session.
|
||||
/// Create a new browser context and page. Each test gets an isolated session. Contexts
|
||||
/// from already-finished tests are closed eagerly once more than
|
||||
/// <see cref="MaxRetainedContexts"/> are open, to bound the number of concurrent Blazor
|
||||
/// Server circuits the run holds on the Central UI.
|
||||
/// </summary>
|
||||
public async Task<IPage> NewPageAsync()
|
||||
{
|
||||
var context = await Browser.NewContextAsync();
|
||||
return await context.NewPageAsync();
|
||||
var page = await context.NewPageAsync();
|
||||
|
||||
List<IBrowserContext> toClose = new();
|
||||
lock (_contexts)
|
||||
{
|
||||
_contexts.Add(context);
|
||||
int excess = _contexts.Count - MaxRetainedContexts;
|
||||
if (excess > 0)
|
||||
{
|
||||
toClose = _contexts.GetRange(0, excess);
|
||||
_contexts.RemoveRange(0, excess);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var old in toClose)
|
||||
{
|
||||
// Best-effort: a finished test's context may already be gone; never fail a test
|
||||
// (or the next page creation) on teardown of a stale context.
|
||||
try { await old.CloseAsync(); } catch { /* best-effort */ }
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+4
-2
@@ -190,7 +190,7 @@ public class TransportImportTests
|
||||
await CliRunner.AddAttributeAsync(tmplId, "Value", "Double");
|
||||
await CliRunner.BundleExportAsync(bundlePath, tmplId, "correct-passphrase-1", "src-env");
|
||||
|
||||
var page = await _fixture.NewAuthenticatedPageAsync("multi-role", "password");
|
||||
var page = await _fixture.NewAuthenticatedPageAsync();
|
||||
|
||||
// ── STEP 1: Upload ────────────────────────────────────────────────────────
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/design/transport/import");
|
||||
@@ -213,7 +213,9 @@ public class TransportImportTests
|
||||
await Assertions.Expect(page.Locator("[data-testid='error-message']"))
|
||||
.ToContainTextAsync("Wrong passphrase. Please try again.", new() { Timeout = 10_000 });
|
||||
await Assertions.Expect(page.Locator("#import-passphrase")).ToBeVisibleAsync();
|
||||
await Assertions.Expect(page.Locator("[data-testid='diff-summary']")).ToBeHiddenAsync();
|
||||
// Assert the diff step is genuinely absent (the @switch never rendered it), not merely
|
||||
// hidden — ToBeHiddenAsync is vacuously true for an element that doesn't exist.
|
||||
await Assertions.Expect(page.Locator("[data-testid='diff-summary']")).ToHaveCountAsync(0);
|
||||
|
||||
// Secondary indicator: one failed attempt recorded (1 of MaxUnlockAttempts).
|
||||
await Assertions.Expect(page.Locator("[data-testid='unlock-attempts']"))
|
||||
|
||||
Reference in New Issue
Block a user