Fix channel visibility controls (#940)

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
Wes
2026-06-10 07:46:43 -07:00
committed by GitHub
parent 3e56331e9c
commit 2dc466fe0b
7 changed files with 240 additions and 37 deletions

View File

@@ -121,4 +121,65 @@ test.describe("channel controls screenshots", () => {
await sheet.screenshot({ path: `${SHOTS}/06-management-sheet.png` });
});
test("07 — saving lifecycle leaves details save idle", async ({ page }) => {
await installMockBridge(page, { updateChannelDelayMs: 500 });
await openManagementSheet(page);
const sheet = page.getByTestId("channel-management-sheet");
await page.getByTestId("channel-management-ephemeral-toggle").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toBeEnabled();
await page.getByTestId("channel-management-save-lifecycle").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toHaveText("Saving...");
await expect(
page.getByTestId("channel-management-save-details"),
).toHaveText("Save details");
await sheet.screenshot({
path: `${SHOTS}/07-lifecycle-saving-details-idle.png`,
});
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toHaveText("Save visibility");
});
test("08 — saved ephemeral lifecycle is reflected after reopen", async ({
page,
}) => {
await installMockBridge(page);
await openManagementSheet(page);
await page.getByTestId("channel-management-private-toggle").click();
await page.getByTestId("channel-management-ephemeral-toggle").click();
await page.getByTestId("channel-management-save-lifecycle").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toHaveText("Save visibility");
await page.keyboard.press("Escape");
await expect(
page.getByTestId("channel-management-sheet"),
).not.toBeVisible();
await page.getByTestId("channel-management-trigger").click();
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();
await expect(
page.getByTestId("channel-management-private-toggle"),
).toHaveAttribute("data-state", "checked");
await expect(
page.getByTestId("channel-management-ephemeral-toggle"),
).toHaveAttribute("data-state", "checked");
await expect(page.getByTestId("channel-management-ttl")).toHaveValue("1d");
await settle(page);
await lifecycle.screenshot({
path: `${SHOTS}/08-ephemeral-persisted-after-reopen.png`,
});
});
});

View File

@@ -115,13 +115,25 @@ async function getManagedAgentPubkey(
}
async function readCommandLog(page: import("@playwright/test").Page) {
return page.evaluate(() => {
return (
(window as Window & { __SPROUT_E2E_COMMANDS__?: string[] })
.__SPROUT_E2E_COMMANDS__ ?? []
);
});
}
async function readCommandPayloadLog(page: import("@playwright/test").Page) {
return page.evaluate(() => {
return (
(
window as Window & {
__SPROUT_E2E_COMMANDS__?: string[];
__SPROUT_E2E_COMMAND_LOG__?: Array<{
command: string;
payload: unknown;
}>;
}
).__SPROUT_E2E_COMMANDS__ ?? []
).__SPROUT_E2E_COMMAND_LOG__ ?? []
);
});
}
@@ -748,6 +760,108 @@ test("manage channel updates details and context", async ({ page }) => {
);
});
test("manage channel updates visibility and ephemeral lifecycle independently", async ({
page,
}) => {
await page.goto("/");
await openChannelManagement(page, "general");
const saveDetailsButton = page.getByTestId("channel-management-save-details");
const saveLifecycleButton = page.getByTestId(
"channel-management-save-lifecycle",
);
await expect(saveLifecycleButton).toBeDisabled();
await page.getByTestId("channel-management-private-toggle").click();
await page.getByTestId("channel-management-ephemeral-toggle").click();
await expect(page.getByTestId("channel-management-ttl")).toBeVisible();
await expect(saveLifecycleButton).toBeEnabled();
const commandCountBeforeEnable = (await readCommandPayloadLog(page)).length;
await saveLifecycleButton.click();
await expect
.poll(async () =>
(await readCommandPayloadLog(page)).slice(commandCountBeforeEnable),
)
.toContainEqual(
expect.objectContaining({
command: "update_channel",
payload: expect.objectContaining({
input: expect.objectContaining({ ttlSeconds: 86400 }),
}),
}),
);
await expect(saveLifecycleButton).toHaveText("Save visibility");
await expect(saveDetailsButton).toHaveText("Save details");
const channelAfterEnable = await invokeMockCommand<{
ttl_seconds: number | null;
visibility: string;
}>(page, "get_channel_details", {
channelId: "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50",
});
expect(channelAfterEnable).toMatchObject({
ttl_seconds: 86400,
visibility: "private",
});
await closeChannelManagement(page);
await openChannelManagement(page, "general");
await expect(
page.getByTestId("channel-management-private-toggle"),
).toHaveAttribute("data-state", "checked");
await expect(
page.getByTestId("channel-management-ephemeral-toggle"),
).toHaveAttribute("data-state", "checked");
await expect(page.getByTestId("channel-management-ttl")).toHaveValue("1d");
await page.getByTestId("channel-management-private-toggle").click();
await page.getByTestId("channel-management-ephemeral-toggle").click();
await expect(saveLifecycleButton).toBeEnabled();
const commandCountBeforeDisable = (await readCommandPayloadLog(page)).length;
await saveLifecycleButton.click();
await expect
.poll(async () =>
(await readCommandPayloadLog(page)).slice(commandCountBeforeDisable),
)
.toContainEqual(
expect.objectContaining({
command: "update_channel",
payload: expect.objectContaining({
input: expect.objectContaining({ ttlSeconds: null }),
}),
}),
);
await expect(saveLifecycleButton).toHaveText("Save visibility");
await expect(saveDetailsButton).toHaveText("Save details");
await expect(page.getByTestId("channel-management-ttl")).toHaveCount(0);
const channelAfterDisable = await invokeMockCommand<{
ttl_seconds: number | null;
visibility: string;
}>(page, "get_channel_details", {
channelId: "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50",
});
expect(channelAfterDisable).toMatchObject({
ttl_seconds: null,
visibility: "open",
});
await closeChannelManagement(page);
await openChannelManagement(page, "general");
await expect(
page.getByTestId("channel-management-private-toggle"),
).toHaveAttribute("data-state", "unchecked");
await expect(
page.getByTestId("channel-management-ephemeral-toggle"),
).toHaveAttribute("data-state", "unchecked");
await expect(page.getByTestId("channel-management-ttl")).toHaveCount(0);
});
test("manage channel keeps canvas near the top of the sheet", async ({
page,
}) => {

View File

@@ -51,6 +51,7 @@ type MockBridgeOptions = {
profileReadDelayMs?: number;
profileReadError?: string;
profileUpdateError?: string;
updateChannelDelayMs?: number;
stallWebsocketSends?: boolean;
// NIP-IA gate inputs — drive the archive-button gate matrix in
// tests/e2e/identity-archive.spec.ts.