test(playwright): Site Calls status-filter + empty-state edge cases (Wave 4)
This commit is contained in:
+104
@@ -387,4 +387,108 @@ public class SiteCallsPageTests
|
|||||||
await SiteCallDataSeeder.DeleteByTargetPrefixAsync(targetPrefix);
|
await SiteCallDataSeeder.DeleteByTargetPrefixAsync(targetPrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Status dropdown filters the grid to the selected lifecycle status.
|
||||||
|
/// Seeds two rows sharing a per-run Target prefix — one <c>Parked</c>, one
|
||||||
|
/// <c>Delivered</c> — then filters status=Parked: the Parked marker row
|
||||||
|
/// surfaces (with a <c>Parked</c> status badge) while the Delivered marker
|
||||||
|
/// row is excluded even though both share the prefix. This mirrors
|
||||||
|
/// <see cref="FilterNarrowing_ChannelFilterShrinksGrid"/> but exercises the
|
||||||
|
/// STATUS axis rather than the channel axis.
|
||||||
|
/// <para>
|
||||||
|
/// <c>SourceSite</c> is <c>site-a</c> (a permitted site) on both rows —
|
||||||
|
/// <c>FilterPermittedAsync</c> drops rows whose source site is not permitted.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
[SkippableFact]
|
||||||
|
public async Task StatusFilter_NarrowsToSelectedStatus()
|
||||||
|
{
|
||||||
|
Skip.IfNot(await SiteCallDataSeeder.IsAvailableAsync(), DbUnavailableSkipReason);
|
||||||
|
|
||||||
|
var runId = Guid.NewGuid().ToString("N");
|
||||||
|
var targetPrefix = $"playwright-test/wave4-sc/{runId}/";
|
||||||
|
var parkedId = Guid.NewGuid();
|
||||||
|
var deliveredId = Guid.NewGuid();
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Two rows sharing the prefix: one Parked, one Delivered. site-a is a
|
||||||
|
// permitted source site (FilterPermittedAsync keeps it).
|
||||||
|
await SiteCallDataSeeder.InsertSiteCallAsync(
|
||||||
|
trackedOperationId: parkedId, channel: "ApiOutbound", target: targetPrefix + "parked",
|
||||||
|
sourceSite: "site-a", status: "Parked", retryCount: 3,
|
||||||
|
lastError: "HTTP 503 from ERP", httpStatus: 503,
|
||||||
|
createdAtUtc: now, updatedAtUtc: now);
|
||||||
|
await SiteCallDataSeeder.InsertSiteCallAsync(
|
||||||
|
trackedOperationId: deliveredId, channel: "ApiOutbound", target: targetPrefix + "delivered",
|
||||||
|
sourceSite: "site-a", status: "Delivered", retryCount: 0,
|
||||||
|
createdAtUtc: now, updatedAtUtc: now, httpStatus: 200, terminalAtUtc: now);
|
||||||
|
|
||||||
|
var page = await _fixture.NewAuthenticatedPageAsync();
|
||||||
|
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{SiteCallsUrl}");
|
||||||
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|
||||||
|
// Filter status=Parked and scope to the exact Parked marker — the row
|
||||||
|
// surfaces and its status badge reads Parked.
|
||||||
|
await page.Locator("#sc-status").SelectOptionAsync("Parked");
|
||||||
|
await SetSearchKeywordAsync(page, targetPrefix + "parked");
|
||||||
|
await page.ClickAsync("[data-test='site-calls-query']");
|
||||||
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|
||||||
|
var parkedRow = page.Locator("tbody tr", new() { HasText = targetPrefix + "parked" });
|
||||||
|
await Assertions.Expect(parkedRow).ToBeVisibleAsync();
|
||||||
|
await Assertions.Expect(parkedRow.Locator("span.badge:has-text('Parked')")).ToBeVisibleAsync();
|
||||||
|
|
||||||
|
// Same status=Parked filter, now searching the Delivered marker: the
|
||||||
|
// Delivered row is excluded by the status filter, so no row carries
|
||||||
|
// its marker. The retrying ToHaveCount waits out the re-render.
|
||||||
|
await SetSearchKeywordAsync(page, targetPrefix + "delivered");
|
||||||
|
await page.ClickAsync("[data-test='site-calls-query']");
|
||||||
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|
||||||
|
await Assertions.Expect(
|
||||||
|
page.Locator("tbody tr").Filter(new() { HasText = targetPrefix + "delivered" }))
|
||||||
|
.ToHaveCountAsync(0);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await SiteCallDataSeeder.DeleteByTargetPrefixAsync(targetPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the filters match no rows the grid renders the empty-state card
|
||||||
|
/// rather than a table. A per-run GUID Target is searched (exact match), so
|
||||||
|
/// nothing can match — guaranteed empty without seeding. Asserts both the
|
||||||
|
/// absence of data rows and the empty-state literal.
|
||||||
|
/// <para>
|
||||||
|
/// The empty-state literal lives in <c>div.card > div.card-body…</c>, but
|
||||||
|
/// the filter card also uses <c>.card-body</c>, so a bare <c>.card-body</c>
|
||||||
|
/// locator is ambiguous under strict mode. We assert the literal via
|
||||||
|
/// <see cref="IPage.GetByText(string,PageGetByTextOptions)"/> instead.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
[SkippableFact]
|
||||||
|
public async Task EmptyState_NoMatch_ShowsEmptyCard()
|
||||||
|
{
|
||||||
|
Skip.IfNot(await SiteCallDataSeeder.IsAvailableAsync(), DbUnavailableSkipReason);
|
||||||
|
|
||||||
|
var page = await _fixture.NewAuthenticatedPageAsync();
|
||||||
|
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}{SiteCallsUrl}");
|
||||||
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|
||||||
|
// A per-run GUID target matches nothing (exact match → guaranteed empty).
|
||||||
|
await SetSearchKeywordAsync(page, $"playwright-test/wave4-sc-empty/{Guid.NewGuid():N}/none");
|
||||||
|
await page.ClickAsync("[data-test='site-calls-query']");
|
||||||
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||||
|
|
||||||
|
// No data rows, and the empty-state literal renders. GetByText avoids the
|
||||||
|
// strict-mode ambiguity of the shared .card-body class.
|
||||||
|
await Assertions.Expect(page.Locator("tbody tr")).ToHaveCountAsync(0);
|
||||||
|
await Assertions.Expect(
|
||||||
|
page.GetByText("No cached calls match the current filters."))
|
||||||
|
.ToBeVisibleAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user