test(e2e): Transport Export wizard reaches download summary for a zztest template
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
using Microsoft.Playwright;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Transport;
|
||||
|
||||
/// <summary>
|
||||
/// Happy-path end-to-end for the Transport Export wizard (Component #24, Task T21).
|
||||
///
|
||||
/// <para>
|
||||
/// Drives the full four-step wizard at <c>/design/transport/export</c> against the
|
||||
/// running dev cluster, exporting a single throwaway template the test creates via
|
||||
/// the CLI:
|
||||
/// </para>
|
||||
/// <list type="number">
|
||||
/// <item>Step 1 — Select : filter to the zztest template and tick its checkbox.</item>
|
||||
/// <item>Step 2 — Review : confirm the resolved closure (Next).</item>
|
||||
/// <item>Step 3 — Encrypt: supply a matching passphrase (≥ 8 chars) and Export.</item>
|
||||
/// <item>Step 4 — Download: assert the success summary.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>
|
||||
/// Step 1's template list is a <c>TemplateFolderTree</c> in checkbox mode. The inner
|
||||
/// <c>TreeView</c> renders an <c><input type="checkbox" class="tv-checkbox"></c>
|
||||
/// per node and the name in a sibling <c>span.tv-label</c>; there is no
|
||||
/// <c><label for></c> association, and in checkbox mode clicking the label text
|
||||
/// does NOT toggle the box (content-click only selects in Single mode). So the test
|
||||
/// clicks the checkbox <c>input</c> itself, scoped to the row carrying the template's
|
||||
/// label text.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Step 4's download is a JS-interop blob stream (NOT a DOM <c><a download></c>),
|
||||
/// so the test asserts the success DOM (<c>[data-testid='download-summary']</c>) rather
|
||||
/// than waiting for a Playwright download event — which would hang, since no
|
||||
/// browser-level download fires.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Collection("Playwright")]
|
||||
public sealed class TransportExportTests
|
||||
{
|
||||
private readonly PlaywrightFixture _fixture;
|
||||
|
||||
public TransportExportTests(PlaywrightFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task ExportTemplate_ReachesDownloadSummary()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
|
||||
var tmplName = CliRunner.UniqueName("exptmpl");
|
||||
var tmplId = await CliRunner.CreateTemplateAsync(tmplName);
|
||||
await CliRunner.AddAttributeAsync(tmplId, "Value", "Double");
|
||||
try
|
||||
{
|
||||
var page = await _fixture.NewAuthenticatedPageAsync("multi-role", "password");
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/design/transport/export");
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
// ── STEP 1: Select ────────────────────────────────────────────────────────
|
||||
// Narrow the tree to just the zztest template, then tick its checkbox.
|
||||
await page.Locator("#export-filter").FillAsync(tmplName);
|
||||
|
||||
// The template node is a TreeView leaf row: a checkbox input plus a
|
||||
// span.tv-label carrying the name. Clicking the label text is a no-op in
|
||||
// checkbox mode, so we click the input directly, scoped to the row whose
|
||||
// label matches the unique zztest name.
|
||||
var templateCheckbox = page
|
||||
.Locator("[data-testid='group-templates'] li")
|
||||
.Filter(new() { Has = page.Locator($"span.tv-label:text-is('{tmplName}')") })
|
||||
.Locator("input.tv-checkbox")
|
||||
.First;
|
||||
await Assertions.Expect(templateCheckbox).ToBeVisibleAsync(new() { Timeout = 10_000 });
|
||||
await templateCheckbox.CheckAsync();
|
||||
|
||||
// Next enables only once a selection exists; clicking it resolves the closure.
|
||||
var step1Next = page.Locator("button.btn.btn-primary:has-text('Next')");
|
||||
await Assertions.Expect(step1Next).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||
await step1Next.ClickAsync();
|
||||
|
||||
// ── STEP 2: Review ────────────────────────────────────────────────────────
|
||||
// The seed group lists the picked template; Next is always enabled here.
|
||||
await Assertions.Expect(page.Locator("[data-testid='seed-group']"))
|
||||
.ToBeVisibleAsync(new() { Timeout = 15_000 });
|
||||
await page.Locator("button.btn.btn-primary:has-text('Next')").ClickAsync();
|
||||
|
||||
// ── STEP 3: Encrypt ───────────────────────────────────────────────────────
|
||||
const string passphrase = "zztest-passphrase-123";
|
||||
await Assertions.Expect(page.Locator("#passphrase"))
|
||||
.ToBeVisibleAsync(new() { Timeout = 10_000 });
|
||||
await page.Locator("#passphrase").FillAsync(passphrase);
|
||||
await page.Locator("#passphrase-confirm").FillAsync(passphrase);
|
||||
|
||||
// Export enables only when the two passphrases match AND length ≥ 8.
|
||||
var exportBtn = page.Locator("button.btn.btn-primary:has-text('Export')");
|
||||
await Assertions.Expect(exportBtn).ToBeEnabledAsync(new() { Timeout = 10_000 });
|
||||
await exportBtn.ClickAsync();
|
||||
|
||||
// ── STEP 4: Download ──────────────────────────────────────────────────────
|
||||
// JS-interop blob download — assert the success DOM, never WaitForDownload.
|
||||
var summary = page.Locator("[data-testid='download-summary']");
|
||||
await Assertions.Expect(summary).ToBeVisibleAsync(new() { Timeout = 20_000 });
|
||||
await Assertions.Expect(summary).ToContainTextAsync("Bundle ready");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CliRunner.DeleteTemplateAsync(tmplId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user