test(desktop): align channel management e2e with edit modal

This commit is contained in:
Thomas Petersen
2026-06-16 08:40:17 -04:00
parent 767614f387
commit 3a1bebfb01
4 changed files with 125 additions and 94 deletions

View File

@@ -14,6 +14,13 @@ async function openManagementSheet(page: import("@playwright/test").Page) {
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
}
async function openEditDialog(page: import("@playwright/test").Page) {
await page.getByTestId("channel-management-edit").click();
await expect(
page.getByRole("dialog", { name: "Edit channel" }),
).toBeVisible();
}
async function settle(page: import("@playwright/test").Page) {
await page.evaluate(() =>
Promise.all(document.getAnimations().map((a) => a.finished)),
@@ -26,6 +33,7 @@ test.describe("channel controls screenshots", () => {
}) => {
await installMockBridge(page);
await openManagementSheet(page);
await openEditDialog(page);
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();
@@ -43,6 +51,7 @@ test.describe("channel controls screenshots", () => {
test("02 — Private toggled on", async ({ page }) => {
await installMockBridge(page);
await openManagementSheet(page);
await openEditDialog(page);
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();
@@ -52,7 +61,7 @@ test.describe("channel controls screenshots", () => {
).toBeChecked();
// Save button enables once the visibility actually changed.
await expect(
page.getByTestId("channel-management-save-lifecycle"),
page.getByTestId("channel-management-save-changes"),
).toBeEnabled();
await settle(page);
@@ -62,6 +71,7 @@ test.describe("channel controls screenshots", () => {
test("03 — Ephemeral on with friendly timeout field", async ({ page }) => {
await installMockBridge(page);
await openManagementSheet(page);
await openEditDialog(page);
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();
@@ -71,7 +81,7 @@ test.describe("channel controls screenshots", () => {
await expect(ttl).toBeVisible();
await ttl.fill("1d12h");
await expect(
page.getByTestId("channel-management-save-lifecycle"),
page.getByTestId("channel-management-save-changes"),
).toBeEnabled();
await settle(page);
@@ -83,6 +93,7 @@ test.describe("channel controls screenshots", () => {
}) => {
await installMockBridge(page);
await openManagementSheet(page);
await openEditDialog(page);
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();
@@ -92,7 +103,7 @@ test.describe("channel controls screenshots", () => {
await ttl.fill("soon");
await expect(ttl).toHaveAttribute("aria-invalid", "true");
await expect(
page.getByTestId("channel-management-save-lifecycle"),
page.getByTestId("channel-management-save-changes"),
).toBeDisabled();
await settle(page);
@@ -122,30 +133,28 @@ test.describe("channel controls screenshots", () => {
await sheet.screenshot({ path: `${SHOTS}/06-management-sheet.png` });
});
test("07 — saving lifecycle leaves details save idle", async ({ page }) => {
test("07 — saving lifecycle uses unified save", async ({ page }) => {
await installMockBridge(page, { updateChannelDelayMs: 500 });
await openManagementSheet(page);
await openEditDialog(page);
const sheet = page.getByTestId("channel-management-sheet");
await page.getByTestId("channel-management-ephemeral-toggle").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
page.getByTestId("channel-management-save-changes"),
).toBeEnabled();
await page.getByTestId("channel-management-save-lifecycle").click();
await page.getByTestId("channel-management-save-changes").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
page.getByTestId("channel-management-save-changes"),
).toHaveText("Saving...");
await expect(
page.getByTestId("channel-management-save-details"),
).toHaveText("Save details");
await sheet.screenshot({
path: `${SHOTS}/07-lifecycle-saving-details-idle.png`,
path: `${SHOTS}/07-lifecycle-saving-unified-save.png`,
});
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toHaveText("Save visibility");
page.getByRole("dialog", { name: "Edit channel" }),
).toHaveCount(0);
});
test("08 — saved ephemeral lifecycle is reflected after reopen", async ({
@@ -153,19 +162,21 @@ test.describe("channel controls screenshots", () => {
}) => {
await installMockBridge(page);
await openManagementSheet(page);
await openEditDialog(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 page.getByTestId("channel-management-save-changes").click();
await expect(
page.getByTestId("channel-management-save-lifecycle"),
).toHaveText("Save visibility");
page.getByRole("dialog", { name: "Edit channel" }),
).toHaveCount(0);
await page.keyboard.press("Escape");
await expect(
page.getByTestId("channel-management-sheet"),
).not.toBeVisible();
await page.getByTestId("channel-management-trigger").click();
await openEditDialog(page);
const lifecycle = page.getByTestId("channel-management-lifecycle");
await lifecycle.scrollIntoViewIfNeeded();

View File

@@ -20,6 +20,13 @@ async function closeChannelManagement(page: import("@playwright/test").Page) {
await expect(page.getByTestId("channel-management-sheet")).not.toBeVisible();
}
async function openChannelEditDialog(page: import("@playwright/test").Page) {
await page.getByTestId("channel-management-edit").click();
await expect(
page.getByRole("dialog", { name: "Edit channel" }),
).toBeVisible();
}
async function openMembersSidebar(
page: import("@playwright/test").Page,
channelName: string,
@@ -983,28 +990,30 @@ test("manage channel updates details and context", async ({ page }) => {
await page.goto("/");
await openChannelManagement(page, "general");
await openChannelEditDialog(page);
const editDialog = page.getByRole("dialog", { name: "Edit channel" });
await page.getByTestId("channel-management-name").fill(newName);
await page.getByTestId("channel-management-description").fill(newDescription);
await page.getByTestId("channel-management-save-details").click();
await editDialog.getByTestId("channel-management-name").fill(newName);
await editDialog
.getByTestId("channel-management-description")
.fill(newDescription);
await editDialog.getByTestId("channel-management-topic").fill(newTopic);
await editDialog.getByTestId("channel-management-purpose").fill(newPurpose);
await editDialog.getByTestId("channel-management-save-changes").click();
await expect(editDialog).toHaveCount(0);
await expect(page.getByTestId("chat-title")).toHaveText(newName);
await expect(page.getByTestId("stream-list")).toContainText(newName);
const saveTopicButton = page.getByTestId("channel-management-save-topic");
const savePurposeButton = page.getByTestId("channel-management-save-purpose");
await page.getByTestId("channel-management-topic").fill(newTopic);
await saveTopicButton.click();
await expect(saveTopicButton).toHaveText("Save topic");
await expect(page.getByTestId("channel-management-topic")).toHaveValue(
await expect(page.getByTestId("channel-management-name-row")).toContainText(
newName,
);
await expect(
page.getByTestId("channel-management-description"),
).toContainText(newDescription);
await expect(page.getByTestId("channel-management-topic")).toContainText(
newTopic,
);
await page.getByTestId("channel-management-purpose").fill(newPurpose);
await savePurposeButton.click();
await expect(savePurposeButton).toHaveText("Save purpose");
await expect(page.getByTestId("channel-management-purpose")).toHaveValue(
await expect(page.getByTestId("channel-management-purpose")).toContainText(
newPurpose,
);
@@ -1017,19 +1026,23 @@ test("manage channel updates details and context", async ({ page }) => {
await expect(page.getByTestId("chat-title")).toHaveText(newName);
await page.getByTestId("channel-management-trigger").click();
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
await openChannelEditDialog(page);
const reopenedEditDialog = page.getByRole("dialog", {
name: "Edit channel",
});
await expect(page.getByTestId("channel-management-name")).toHaveValue(
newName,
);
await expect(page.getByTestId("channel-management-description")).toHaveValue(
newDescription,
);
await expect(page.getByTestId("channel-management-topic")).toHaveValue(
newTopic,
);
await expect(page.getByTestId("channel-management-purpose")).toHaveValue(
newPurpose,
);
await expect(
reopenedEditDialog.getByTestId("channel-management-name"),
).toHaveValue(newName);
await expect(
reopenedEditDialog.getByTestId("channel-management-description"),
).toHaveValue(newDescription);
await expect(
reopenedEditDialog.getByTestId("channel-management-topic"),
).toHaveValue(newTopic);
await expect(
reopenedEditDialog.getByTestId("channel-management-purpose"),
).toHaveValue(newPurpose);
});
test("manage channel updates visibility and ephemeral lifecycle independently", async ({
@@ -1037,21 +1050,19 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
}) => {
await page.goto("/");
await openChannelManagement(page, "general");
await openChannelEditDialog(page);
const saveDetailsButton = page.getByTestId("channel-management-save-details");
const saveLifecycleButton = page.getByTestId(
"channel-management-save-lifecycle",
);
let saveChangesButton = page.getByTestId("channel-management-save-changes");
await expect(saveLifecycleButton).toBeDisabled();
await expect(saveChangesButton).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();
await expect(saveChangesButton).toBeEnabled();
const commandCountBeforeEnable = (await readCommandPayloadLog(page)).length;
await saveLifecycleButton.click();
await saveChangesButton.click();
await expect
.poll(async () =>
(await readCommandPayloadLog(page)).slice(commandCountBeforeEnable),
@@ -1064,8 +1075,9 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
}),
}),
);
await expect(saveLifecycleButton).toHaveText("Save visibility");
await expect(saveDetailsButton).toHaveText("Save details");
await expect(page.getByRole("dialog", { name: "Edit channel" })).toHaveCount(
0,
);
const channelAfterEnable = await invokeMockCommand<{
ttl_seconds: number | null;
@@ -1080,6 +1092,8 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
await closeChannelManagement(page);
await openChannelManagement(page, "general");
await openChannelEditDialog(page);
saveChangesButton = page.getByTestId("channel-management-save-changes");
await expect(
page.getByTestId("channel-management-private-toggle"),
@@ -1091,10 +1105,10 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
await page.getByTestId("channel-management-private-toggle").click();
await page.getByTestId("channel-management-ephemeral-toggle").click();
await expect(saveLifecycleButton).toBeEnabled();
await expect(saveChangesButton).toBeEnabled();
const commandCountBeforeDisable = (await readCommandPayloadLog(page)).length;
await saveLifecycleButton.click();
await saveChangesButton.click();
await expect
.poll(async () =>
(await readCommandPayloadLog(page)).slice(commandCountBeforeDisable),
@@ -1107,9 +1121,9 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
}),
}),
);
await expect(saveLifecycleButton).toHaveText("Save visibility");
await expect(saveDetailsButton).toHaveText("Save details");
await expect(page.getByTestId("channel-management-ttl")).toHaveCount(0);
await expect(page.getByRole("dialog", { name: "Edit channel" })).toHaveCount(
0,
);
const channelAfterDisable = await invokeMockCommand<{
ttl_seconds: number | null;
@@ -1124,6 +1138,7 @@ test("manage channel updates visibility and ephemeral lifecycle independently",
await closeChannelManagement(page);
await openChannelManagement(page, "general");
await openChannelEditDialog(page);
await expect(
page.getByTestId("channel-management-private-toggle"),
@@ -1142,12 +1157,12 @@ test("manage channel keeps canvas near the top of the sheet", async ({
const sheet = page.getByTestId("channel-management-sheet");
// Canvas section should appear before the name input in the DOM.
// Canvas ingress should appear before the channel metadata rows in the DOM.
const canvasBox = await sheet
.getByTestId("channel-canvas-section")
.getByTestId("channel-canvas-ingress")
.boundingBox();
const nameBox = await sheet
.getByTestId("channel-management-name")
.getByTestId("channel-management-name-row")
.boundingBox();
expect(canvasBox).not.toBeNull();

View File

@@ -28,6 +28,13 @@ async function openChannelManagement(page: import("@playwright/test").Page) {
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
}
async function openChannelEditDialog(page: import("@playwright/test").Page) {
await page.getByTestId("channel-management-edit").click();
await expect(
page.getByRole("dialog", { name: "Edit channel" }),
).toBeVisible();
}
async function closeChannelManagement(page: import("@playwright/test").Page) {
await page.keyboard.press("Escape");
await expect(page.getByTestId("channel-management-sheet")).not.toBeVisible();
@@ -455,32 +462,23 @@ test("manage sheet updates channel details and context through the relay", async
await createStream(page, initialName, initialDescription);
await openChannelManagement(page);
await page.getByTestId("channel-management-name").fill(renamedChannel);
await page
await openChannelEditDialog(page);
const editDialog = page.getByRole("dialog", { name: "Edit channel" });
await editDialog.getByTestId("channel-management-name").fill(renamedChannel);
await editDialog
.getByTestId("channel-management-description")
.fill(updatedDescription);
await page.getByTestId("channel-management-save-details").click();
await editDialog.getByTestId("channel-management-topic").fill(updatedTopic);
await editDialog
.getByTestId("channel-management-purpose")
.fill(updatedPurpose);
await editDialog.getByTestId("channel-management-save-changes").click();
await expect(editDialog).toHaveCount(0);
await expect(page.getByTestId("chat-title")).toHaveText(renamedChannel);
await expect(page.getByTestId("stream-list")).toContainText(renamedChannel);
const saveTopicButton = page.getByTestId("channel-management-save-topic");
const savePurposeButton = page.getByTestId("channel-management-save-purpose");
await page.getByTestId("channel-management-topic").fill(updatedTopic);
await saveTopicButton.click();
await expect(saveTopicButton).toHaveText("Save topic");
await expect(page.getByTestId("channel-management-topic")).toHaveValue(
updatedTopic,
);
await page.getByTestId("channel-management-purpose").fill(updatedPurpose);
await savePurposeButton.click();
await expect(savePurposeButton).toHaveText("Save purpose");
await expect(page.getByTestId("channel-management-purpose")).toHaveValue(
updatedPurpose,
);
await closeChannelManagement(page);
await page.reload();
@@ -493,18 +491,23 @@ test("manage sheet updates channel details and context through the relay", async
);
await openChannelManagement(page);
await expect(page.getByTestId("channel-management-name")).toHaveValue(
renamedChannel,
);
await expect(page.getByTestId("channel-management-description")).toHaveValue(
updatedDescription,
);
await expect(page.getByTestId("channel-management-topic")).toHaveValue(
updatedTopic,
);
await expect(page.getByTestId("channel-management-purpose")).toHaveValue(
updatedPurpose,
);
await openChannelEditDialog(page);
const reopenedEditDialog = page.getByRole("dialog", {
name: "Edit channel",
});
await expect(
reopenedEditDialog.getByTestId("channel-management-name"),
).toHaveValue(renamedChannel);
await expect(
reopenedEditDialog.getByTestId("channel-management-description"),
).toHaveValue(updatedDescription);
await expect(
reopenedEditDialog.getByTestId("channel-management-topic"),
).toHaveValue(updatedTopic);
await expect(
reopenedEditDialog.getByTestId("channel-management-purpose"),
).toHaveValue(updatedPurpose);
});
test("manage sheet archive and unarchive survives a reload through the relay", async ({

View File

@@ -113,8 +113,10 @@ test.describe("relay connectivity screenshots", () => {
await expect(page.getByTestId("chat-title")).toHaveText("general");
await page.getByTestId("channel-management-trigger").click();
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
await page.getByTestId("channel-canvas-ingress").click();
// ChannelCanvas shows the destructive error paragraph when the query fails.
// ChannelCanvas shows the destructive error paragraph in the drill-in view
// when the query fails.
const canvasSection = page.getByTestId("channel-canvas-section");
await canvasSection.scrollIntoViewIfNeeded();
await expect(