fix(e2e): wait for shell-ready after group chat reload (#94)
* fix(e2e): wait for shell-ready after group chat reload After voice/connect and full page reload on a group route, bootstrap can still be in flight. Use waitForChatComposer (data-ready + Message combobox) instead of asserting the composer immediately, matching #87. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * fix(e2e): wait for shell before approval transcript after reload Transcript messages are not painted until bootstrap finishes; match group-chats and #87 by waiting on data-ready before asserting durable approval copy survives reload. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * style: format approval-resume e2e import Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * fix(e2e): wait for archived sidebar after space delete After deleting a space the Archived section can appear only once bootstrap finishes; wait for shell-ready after archive reload and assert the Archived toggle before clicking. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * fix(e2e): expect archived sidebar when last bot is archived After archiving the only bot, the shell is ready without a Message composer. Wait for data-ready and the Archived toggle instead of waitForChatComposer on reload. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * fix(e2e): wait for shell in composer-paste openComposer helper Third paste spec failed when Message appeared before bootstrap finished; reuse waitForChatComposer after navigating to /app. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
a974f09520
commit
d1f7034aa5
@ -1,5 +1,5 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
|
import { captureScreenshot, completeOnboarding, signup, waitForChatComposer } from "./helpers";
|
||||||
|
|
||||||
test.describe.configure({ mode: "serial" });
|
test.describe.configure({ mode: "serial" });
|
||||||
|
|
||||||
@ -47,8 +47,9 @@ test("approval input resumes durable work", async ({ page }, testInfo) => {
|
|||||||
await expect(handledAnswer).toBeVisible();
|
await expect(handledAnswer).toBeVisible();
|
||||||
await expect(page.getByText("Answered: Paris", { exact: true })).toBeVisible();
|
await expect(page.getByText("Answered: Paris", { exact: true })).toBeVisible();
|
||||||
|
|
||||||
await page.reload();
|
await page.reload({ waitUntil: "domcontentloaded" });
|
||||||
await expect(resumed).toBeVisible({ timeout: 20_000 });
|
await waitForChatComposer(page);
|
||||||
|
await expect(resumed).toBeVisible({ timeout: 30_000 });
|
||||||
await expect(handledAnswer).toBeVisible();
|
await expect(handledAnswer).toBeVisible();
|
||||||
await expect(page.getByText("Answered: Paris", { exact: true })).toBeVisible();
|
await expect(page.getByText("Answered: Paris", { exact: true })).toBeVisible();
|
||||||
await captureScreenshot(page, testInfo, "24-approval-resumed-after-reload");
|
await captureScreenshot(page, testInfo, "24-approval-resumed-after-reload");
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
import { expect, type Page, test } from "@playwright/test";
|
import { expect, type Page, test } from "@playwright/test";
|
||||||
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
|
import { captureScreenshot, completeOnboarding, signup, waitForChatComposer } from "./helpers";
|
||||||
|
|
||||||
async function openComposer(page: Page) {
|
async function openComposer(page: Page) {
|
||||||
const stamp = Date.now();
|
const stamp = Date.now();
|
||||||
await signup(page, `composer-paste-${stamp}@rakazo.test`, "password12", "Paste Test");
|
await signup(page, `composer-paste-${stamp}@rakazo.test`, "password12", "Paste Test");
|
||||||
await completeOnboarding(page);
|
await completeOnboarding(page);
|
||||||
await page.goto("/app");
|
await page.goto("/app", { waitUntil: "domcontentloaded" });
|
||||||
await page.waitForURL(/\/app\/[^/]+$/);
|
await page.waitForURL(/\/app\/[^/]+$/);
|
||||||
const composer = page.getByRole("combobox", { name: /Message/ });
|
await waitForChatComposer(page);
|
||||||
await expect(composer).toBeVisible();
|
return page.getByRole("combobox", { name: /Message/ });
|
||||||
return composer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("pasting an image into the composer creates an attachment", async ({ page }, testInfo) => {
|
test("pasting an image into the composer creates an attachment", async ({ page }, testInfo) => {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
openNewGroup,
|
openNewGroup,
|
||||||
rpc,
|
rpc,
|
||||||
signup,
|
signup,
|
||||||
|
waitForChatComposer,
|
||||||
} from "./helpers";
|
} from "./helpers";
|
||||||
|
|
||||||
async function createBot(page: Page, name: string) {
|
async function createBot(page: Page, name: string) {
|
||||||
@ -86,9 +87,9 @@ test("create group from + and see two bots in one transcript", async ({ page },
|
|||||||
provider: "scripted",
|
provider: "scripted",
|
||||||
apiKey: "fake-group-voice-key",
|
apiKey: "fake-group-voice-key",
|
||||||
});
|
});
|
||||||
await page.reload();
|
await page.reload({ waitUntil: "domcontentloaded" });
|
||||||
await expect(page).toHaveURL(groupUrl);
|
await expect(page).toHaveURL(groupUrl);
|
||||||
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
await waitForChatComposer(page);
|
||||||
|
|
||||||
const groups = await rpc<
|
const groups = await rpc<
|
||||||
Array<{
|
Array<{
|
||||||
@ -237,9 +238,7 @@ test("create group from + and see two bots in one transcript", async ({ page },
|
|||||||
const cityAsk = page.locator("p").filter({ hasText: /^Which city should I use\?$/ });
|
const cityAsk = page.locator("p").filter({ hasText: /^Which city should I use\?$/ });
|
||||||
if ((await cityAsk.count()) === 0) {
|
if ((await cityAsk.count()) === 0) {
|
||||||
await page.reload({ waitUntil: "domcontentloaded" });
|
await page.reload({ waitUntil: "domcontentloaded" });
|
||||||
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible({
|
await waitForChatComposer(page);
|
||||||
timeout: 15_000,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
await expect(cityAsk).toBeVisible({ timeout: 15_000 });
|
await expect(cityAsk).toBeVisible({ timeout: 15_000 });
|
||||||
await page.getByRole("button", { name: "Edit first" }).click();
|
await page.getByRole("button", { name: "Edit first" }).click();
|
||||||
@ -289,7 +288,7 @@ test("create group from + and see two bots in one transcript", async ({ page },
|
|||||||
await expect(page).toHaveURL(new RegExp(`/app/g/${reviewGroup.id}$`));
|
await expect(page).toHaveURL(new RegExp(`/app/g/${reviewGroup.id}$`));
|
||||||
await expect(page.getByTestId("transcript")).not.toContainText("Answered: Paris");
|
await expect(page.getByTestId("transcript")).not.toContainText("Answered: Paris");
|
||||||
releaseReviewSnapshot();
|
releaseReviewSnapshot();
|
||||||
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
|
await waitForChatComposer(page);
|
||||||
await page.unroute("**/rpc/threads/get");
|
await page.unroute("**/rpc/threads/get");
|
||||||
await projekte
|
await projekte
|
||||||
.getByRole("button", { name: /^Draft team/ })
|
.getByRole("button", { name: /^Draft team/ })
|
||||||
@ -321,7 +320,7 @@ test("create group from + and see two bots in one transcript", async ({ page },
|
|||||||
await captureScreenshot(page, testInfo, "group-settings-mobile");
|
await captureScreenshot(page, testInfo, "group-settings-mobile");
|
||||||
|
|
||||||
await rpc(page, "groups/remove", { groupId: reviewGroup.id });
|
await rpc(page, "groups/remove", { groupId: reviewGroup.id });
|
||||||
await page.goto(`/app/g/${reviewGroup.id}`);
|
await page.goto(`/app/g/${reviewGroup.id}`, { waitUntil: "domcontentloaded" });
|
||||||
await page.waitForURL(/\/app\/(?!g\/)[^/]+$/);
|
await page.waitForURL(/\/app\/(?!g\/)[^/]+$/);
|
||||||
await expect(page.getByRole("combobox", { name: /Message/ })).toBeVisible();
|
await waitForChatComposer(page);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -182,10 +182,18 @@ test("deleting the last bot in a space stays in the app after first use", async
|
|||||||
const [chief] = await rpc<Array<{ id: string }>>(page, "bots/list", {});
|
const [chief] = await rpc<Array<{ id: string }>>(page, "bots/list", {});
|
||||||
expect(chief).toBeTruthy();
|
expect(chief).toBeTruthy();
|
||||||
await rpc(page, "bots/archive", { botId: chief!.id });
|
await rpc(page, "bots/archive", { botId: chief!.id });
|
||||||
await page.reload();
|
await page.reload({ waitUntil: "domcontentloaded" });
|
||||||
|
await expect(page.getByTestId("shell-root")).toHaveAttribute("data-ready", "true", {
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
await expect(page).not.toHaveURL(/\/onboarding/);
|
await expect(page).not.toHaveURL(/\/onboarding/);
|
||||||
|
|
||||||
const sidebar = page.locator("aside").first();
|
const sidebar = page.locator("aside").first();
|
||||||
|
// No active bot thread after archiving the only bot — shell shows Archived, not Message.
|
||||||
|
await expect(page.getByRole("combobox", { name: "Message" })).toHaveCount(0);
|
||||||
|
await expect(sidebar.getByRole("button", { name: /^Archived/ })).toBeVisible({
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
await openNewSpace(page);
|
await openNewSpace(page);
|
||||||
const dialog = page.getByRole("dialog", { name: "New space" });
|
const dialog = page.getByRole("dialog", { name: "New space" });
|
||||||
await dialog.getByLabel("Name").fill("Side");
|
await dialog.getByLabel("Name").fill("Side");
|
||||||
@ -214,6 +222,8 @@ test("deleting the last bot in a space stays in the app after first use", async
|
|||||||
.getByRole("button", { name: "Delete", exact: true })
|
.getByRole("button", { name: "Delete", exact: true })
|
||||||
.click();
|
.click();
|
||||||
await expect(sidebar.getByText("Side", { exact: true })).toHaveCount(0);
|
await expect(sidebar.getByText("Side", { exact: true })).toHaveCount(0);
|
||||||
await sidebar.getByRole("button", { name: /^Archived/ }).click();
|
const archivedToggle = sidebar.getByRole("button", { name: /^Archived/ });
|
||||||
|
await expect(archivedToggle).toBeVisible({ timeout: 30_000 });
|
||||||
|
await archivedToggle.click();
|
||||||
await expect(sidebar.getByText("Chief", { exact: true })).toBeVisible();
|
await expect(sidebar.getByText("Chief", { exact: true })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user