mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-28 00:48:07 +00:00
* feat(frontend): pin recent chats * fix(threads): address pin-chat review feedback - Stop bumping updated_at on metadata-only PATCH (pin/unpin) via a new update_metadata(touch=False) path so unpinning no longer jumps a chat to the top of the updated_at-sorted recent list. - Narrow patchThreadMetadata to a ThreadMetadataPatchResponse matching the Gateway's actual response (no values/context). - Namespace the pinned metadata key as deerflow_pinned for consistency with deerflow_sidecar / deerflow_branch. - Cover touch/touch=False behavior in repo + router tests; document the e2e mock's updated_at preservation now mirrors production. * style(frontend): format thread utils test * fix(threads): make pinned ordering server-side * test(frontend): keep infinite-scroll fixture order stable * test(frontend): stabilize lark reconnect e2e * docs: clarify thread pin metadata contract
167 lines
5.9 KiB
TypeScript
167 lines
5.9 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { mockLangGraphAPI } from "./utils/mock-api";
|
|
|
|
test.describe("Integrations settings", () => {
|
|
test("opens integrations settings from a query-string deep link", async ({
|
|
page,
|
|
}) => {
|
|
mockLangGraphAPI(page);
|
|
|
|
await page.goto("/workspace/chats/new?settings=integrations");
|
|
|
|
const dialog = page.getByRole("dialog", { name: "Settings" });
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.getByText("Lark / Feishu CLI")).toBeVisible();
|
|
});
|
|
|
|
test("keeps a single settings dialog across deep link and nav menu openings", async ({
|
|
page,
|
|
}) => {
|
|
mockLangGraphAPI(page);
|
|
|
|
// Deep link opens the shared dialog on Integrations.
|
|
await page.goto("/workspace/chats/new?settings=integrations");
|
|
const dialog = page.getByRole("dialog", { name: "Settings" });
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.getByText("Lark / Feishu CLI")).toBeVisible();
|
|
await expect(page.getByRole("dialog", { name: "Settings" })).toHaveCount(1);
|
|
|
|
// Close the modal before using the sidebar. While the modal is open, the
|
|
// background is intentionally inert and Playwright should not be able to
|
|
// click sidebar controls there.
|
|
await page.keyboard.press("Escape");
|
|
await expect(page.getByRole("dialog", { name: "Settings" })).toHaveCount(0);
|
|
|
|
// Opening again from the nav menu must still use the same shared host, not
|
|
// mount a second SettingsDialog instance.
|
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
|
await sidebar.getByRole("button", { name: /Settings and more/ }).click();
|
|
await page.getByRole("menuitem", { name: "Settings" }).click();
|
|
|
|
// Exactly one Settings dialog is mounted/visible at any time.
|
|
await expect(page.getByRole("dialog", { name: "Settings" })).toHaveCount(1);
|
|
});
|
|
|
|
test("can install the Lark integration skill pack from settings", async ({
|
|
page,
|
|
}) => {
|
|
mockLangGraphAPI(page);
|
|
let authStartRequest: unknown;
|
|
const authCompleteRequests: unknown[] = [];
|
|
let authCompleteCount = 0;
|
|
await page.route(
|
|
"**/api/integrations/lark/auth/complete",
|
|
async (route) => {
|
|
authCompleteRequests.push(route.request().postDataJSON());
|
|
authCompleteCount += 1;
|
|
if (authCompleteCount > 1) {
|
|
await route.fulfill({
|
|
status: 504,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ detail: "Authorization still pending." }),
|
|
});
|
|
return;
|
|
}
|
|
await route.fallback();
|
|
},
|
|
);
|
|
await page.route("**/api/integrations/lark/config/start", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
verification_url: "about:blank",
|
|
device_code: "mock-config-device-code",
|
|
expires_in: 600,
|
|
interval: 5,
|
|
user_code: "config",
|
|
brand: "feishu",
|
|
}),
|
|
});
|
|
});
|
|
await page.route("**/api/integrations/lark/auth/start", async (route) => {
|
|
authStartRequest = route.request().postDataJSON();
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
verification_url: "https://open.feishu.cn/auth/mock-device",
|
|
device_code: "mock-device-code",
|
|
expires_in: 600,
|
|
user_code: null,
|
|
hint: null,
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto("/workspace/chats/new");
|
|
|
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
|
await sidebar.getByRole("button", { name: /Settings and more/ }).click();
|
|
await page.getByRole("menuitem", { name: "Settings" }).click();
|
|
|
|
const dialog = page.getByRole("dialog", { name: "Settings" });
|
|
await expect(dialog).toBeVisible();
|
|
await dialog.getByRole("button", { name: "Integrations" }).click();
|
|
|
|
await expect(dialog.getByText("Lark / Feishu CLI")).toBeVisible();
|
|
await expect(
|
|
dialog.getByText("Install the official skill pack first"),
|
|
).toBeVisible();
|
|
|
|
await dialog.getByRole("button", { name: "Install" }).click();
|
|
await expect(
|
|
page.getByText("Installed 3 Lark/Feishu skills."),
|
|
).toBeVisible();
|
|
|
|
// Sandbox-runtime readiness row surfaces once the init-container runtime is
|
|
// reported ready, so a green UI can't hide a chat-time command-not-found.
|
|
await expect(dialog.getByText("Sandbox runtime")).toBeVisible();
|
|
await expect(
|
|
dialog.getByText("Provisioned by init container"),
|
|
).toBeVisible();
|
|
|
|
await dialog.getByRole("button", { name: "Calendar" }).click();
|
|
await dialog
|
|
.getByLabel("Exact OAuth scope")
|
|
.fill("calendar:calendar.event:read");
|
|
await dialog.getByRole("button", { name: "Connect Lark" }).click();
|
|
await expect(dialog.getByText("about:blank")).toBeVisible();
|
|
await expect(dialog.getByText(/app configuration/i)).toHaveCount(0);
|
|
|
|
await dialog
|
|
.getByRole("button", {
|
|
name: "I completed browser confirmation, continue",
|
|
})
|
|
.click();
|
|
await expect
|
|
.poll(() => authStartRequest)
|
|
.toMatchObject({
|
|
recommend: false,
|
|
domains: ["calendar"],
|
|
scope: "calendar:calendar.event:read",
|
|
});
|
|
|
|
await expect
|
|
.poll(() => authCompleteRequests)
|
|
.toContainEqual({
|
|
device_code: "mock-device-code",
|
|
wait_timeout_seconds: 8,
|
|
});
|
|
await expect(
|
|
dialog.getByText("Lark authorization is live-verified"),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByText("Authorization page opened. Waiting for completion..."),
|
|
).toHaveCount(0);
|
|
|
|
await dialog.getByRole("button", { name: "Calendar" }).click();
|
|
await dialog.getByLabel("Exact OAuth scope").fill("");
|
|
await dialog.getByRole("button", { name: "Reconnect Lark" }).click();
|
|
await expect(
|
|
dialog.getByText("https://open.feishu.cn/auth/mock-device"),
|
|
).toBeVisible();
|
|
});
|
|
});
|