mirror of
https://github.com/block/buzz.git
synced 2026-10-30 05:51:46 +00:00
feat(desktop): add Bestie sidebar entry
Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Arjun Mahanti <arjun@squareup.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"test:e2e": "pnpm build:e2e && playwright test",
|
||||
"test:e2e:smoke": "pnpm build:e2e && playwright test --project=smoke",
|
||||
"test:e2e:integration": "pnpm build:e2e && playwright test --project=integration",
|
||||
"test:e2e:bestie": "VITE_BUZZ_BESTIE=1 pnpm build:e2e && playwright test --config=playwright.bestie.config.ts",
|
||||
"test:e2e:release-smoke": "pnpm build:e2e && playwright test --config=playwright.release-smoke.config.ts",
|
||||
"test:e2e:report": "playwright show-report",
|
||||
"tauri:build": "tauri build"
|
||||
|
||||
25
desktop/playwright.bestie.config.ts
Normal file
25
desktop/playwright.bestie.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const webPort = process.env.BUZZ_BESTIE_E2E_WEB_PORT ?? "4174";
|
||||
const webUrl = `http://127.0.0.1:${webPort}`;
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests/e2e",
|
||||
testMatch: "**/bestie-sidebar.spec.ts",
|
||||
timeout: 30_000,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: 1,
|
||||
reporter: [["list"]],
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
baseURL: webUrl,
|
||||
screenshot: "only-on-failure",
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
webServer: {
|
||||
command: `python3 -m http.server ${webPort} -d dist`,
|
||||
cwd: ".",
|
||||
reuseExistingServer: false,
|
||||
url: webUrl,
|
||||
},
|
||||
});
|
||||
@@ -552,7 +552,9 @@ export function AppSidebar({
|
||||
data-testid="sidebar-scroll-content"
|
||||
>
|
||||
<AppSidebarPrimaryMenu
|
||||
bestieRelayUrl={activeCommunity?.relayUrl}
|
||||
homeBadgeCount={homeBadgeCount}
|
||||
onOpenDm={onOpenDm}
|
||||
onSelectAgents={onSelectAgents}
|
||||
onSelectHome={onSelectHome}
|
||||
onSelectProjects={onSelectProjects}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Activity, Bot, Folders, Inbox, Zap } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { useManagedAgentsQuery } from "@/features/agents/hooks";
|
||||
import { pickBestieAgent } from "@/features/agents/lib/bestie";
|
||||
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
|
||||
import { TopbarSearch } from "@/features/search/ui/TopbarSearch";
|
||||
import { SidebarProjectsSection } from "@/features/sidebar/ui/SidebarProjectsSection";
|
||||
import { FeatureGate } from "@/shared/features";
|
||||
@@ -39,7 +43,9 @@ type AppSidebarPinnedHeaderProps = {
|
||||
};
|
||||
|
||||
type AppSidebarPrimaryMenuProps = {
|
||||
bestieRelayUrl?: string | null;
|
||||
homeBadgeCount: number;
|
||||
onOpenDm: (input: { pubkeys: string[] }) => Promise<void>;
|
||||
onSelectAgents: () => void;
|
||||
onSelectHome: () => void;
|
||||
onSelectProjects: () => void;
|
||||
@@ -89,7 +95,9 @@ export function AppSidebarPinnedHeader({
|
||||
}
|
||||
|
||||
export function AppSidebarPrimaryMenu({
|
||||
bestieRelayUrl,
|
||||
homeBadgeCount,
|
||||
onOpenDm,
|
||||
onSelectAgents,
|
||||
onSelectHome,
|
||||
onSelectProjects,
|
||||
@@ -167,6 +175,12 @@ export function AppSidebarPrimaryMenu({
|
||||
<SidebarMenuLabel>Agents</SidebarMenuLabel>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<FeatureGate feature="bestie">
|
||||
<BestieSidebarMenuItem
|
||||
onOpenDm={onOpenDm}
|
||||
relayUrl={bestieRelayUrl}
|
||||
/>
|
||||
</FeatureGate>
|
||||
<FeatureGate feature="workflows">
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
@@ -187,3 +201,39 @@ export function AppSidebarPrimaryMenu({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BestieSidebarMenuItem({
|
||||
onOpenDm,
|
||||
relayUrl,
|
||||
}: {
|
||||
onOpenDm: (input: { pubkeys: string[] }) => Promise<void>;
|
||||
relayUrl?: string | null;
|
||||
}) {
|
||||
const managedAgentsQuery = useManagedAgentsQuery();
|
||||
const bestieAgent = React.useMemo(
|
||||
() => pickBestieAgent(managedAgentsQuery.data ?? [], relayUrl),
|
||||
[managedAgentsQuery.data, relayUrl],
|
||||
);
|
||||
|
||||
if (!bestieAgent) return null;
|
||||
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-testid="open-bestie-dm"
|
||||
onClick={() => void onOpenDm({ pubkeys: [bestieAgent.pubkey] })}
|
||||
tooltip={`Message ${bestieAgent.name}`}
|
||||
type="button"
|
||||
>
|
||||
<ProfileAvatar
|
||||
avatarUrl={bestieAgent.avatarUrl}
|
||||
className="size-4 text-3xs shadow-none"
|
||||
label={bestieAgent.name}
|
||||
plain
|
||||
testId="bestie-sidebar-avatar"
|
||||
/>
|
||||
<SidebarMenuLabel>Bestie</SidebarMenuLabel>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
54
desktop/tests/e2e/bestie-sidebar.spec.ts
Normal file
54
desktop/tests/e2e/bestie-sidebar.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
import { FEATURE_OVERRIDES_STORAGE_KEY } from "../helpers/features";
|
||||
|
||||
const BESTIE_PUBKEY =
|
||||
"be571e0000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
const bestie = {
|
||||
avatarUrl: null,
|
||||
name: "Bestie",
|
||||
personaId: "builtin:bestie",
|
||||
pubkey: BESTIE_PUBKEY,
|
||||
status: "running" as const,
|
||||
};
|
||||
|
||||
test("the enabled Bestie experiment adds a direct-message entry below Agents", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, { managedAgents: [bestie] });
|
||||
await page.goto("/");
|
||||
|
||||
const agentsEntry = page.getByTestId("open-agents-view");
|
||||
const bestieEntry = page.getByTestId("open-bestie-dm");
|
||||
await expect(bestieEntry).toBeVisible();
|
||||
await expect(bestieEntry).toContainText("Bestie");
|
||||
|
||||
const [agentsBox, bestieBox] = await Promise.all([
|
||||
agentsEntry.boundingBox(),
|
||||
bestieEntry.boundingBox(),
|
||||
]);
|
||||
expect(agentsBox).not.toBeNull();
|
||||
expect(bestieBox).not.toBeNull();
|
||||
expect(bestieBox?.y).toBeGreaterThan(agentsBox?.y ?? 0);
|
||||
|
||||
await bestieEntry.click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("Bestie");
|
||||
});
|
||||
|
||||
test("the disabled Bestie experiment does not mount the sidebar entry", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, { managedAgents: [bestie] });
|
||||
await page.addInitScript((key) => {
|
||||
const overrides = JSON.parse(
|
||||
window.localStorage.getItem(key) ?? "{}",
|
||||
) as Record<string, boolean>;
|
||||
overrides.bestie = false;
|
||||
window.localStorage.setItem(key, JSON.stringify(overrides));
|
||||
}, FEATURE_OVERRIDES_STORAGE_KEY);
|
||||
await page.goto("/");
|
||||
|
||||
await expect(page.getByTestId("open-bestie-dm")).toHaveCount(0);
|
||||
});
|
||||
Reference in New Issue
Block a user