mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Limit agent profile quick actions (#1425)
This commit is contained in:
@@ -10,7 +10,11 @@ import {
|
||||
useChannelsQuery,
|
||||
useOpenDmMutation,
|
||||
} from "@/features/channels/hooks";
|
||||
import { useProfileQuery, useUserProfileQuery } from "@/features/profile/hooks";
|
||||
import {
|
||||
useProfileQuery,
|
||||
useUserProfileQuery,
|
||||
useUsersBatchQuery,
|
||||
} from "@/features/profile/hooks";
|
||||
import { channelMessagesKey } from "@/features/messages/lib/messageQueryKeys";
|
||||
import {
|
||||
useRelayAgentsQuery,
|
||||
@@ -178,6 +182,9 @@ export function UserProfilePopover({
|
||||
const openDmMutation = useOpenDmMutation();
|
||||
const { isStarting: isStartingHuddle, startHuddle } = useHuddle();
|
||||
const profileQuery = useUserProfileQuery(open ? pubkey : undefined);
|
||||
const usersBatchQuery = useUsersBatchQuery(open ? [pubkey] : [], {
|
||||
enabled: open,
|
||||
});
|
||||
const relayAgentsQuery = useRelayAgentsQuery({
|
||||
enabled: open,
|
||||
});
|
||||
@@ -196,12 +203,24 @@ export function UserProfilePopover({
|
||||
const managedAgent = managedAgentsQuery.data?.find(
|
||||
(a) => a.pubkey === pubkey,
|
||||
);
|
||||
const isBotProfile = role === "bot" || Boolean(relayAgent || managedAgent);
|
||||
const profile = profileQuery.data;
|
||||
const normalizedPubkey = normalizePubkey(pubkey);
|
||||
const isAgentByOaOwner = Boolean(
|
||||
usersBatchQuery.data?.profiles[normalizedPubkey]?.isAgent,
|
||||
);
|
||||
const isAgentByProfileOwner = profile?.ownerPubkey != null;
|
||||
const isBotProfile =
|
||||
role === "bot" ||
|
||||
Boolean(relayAgent || managedAgent) ||
|
||||
isAgentByProfileOwner ||
|
||||
isAgentByOaOwner;
|
||||
const isAgentClassificationPending =
|
||||
open &&
|
||||
role !== "bot" &&
|
||||
(relayAgentsQuery.isPending || managedAgentsQuery.isPending);
|
||||
const profile = profileQuery.data;
|
||||
(profileQuery.isPending ||
|
||||
relayAgentsQuery.isPending ||
|
||||
managedAgentsQuery.isPending ||
|
||||
usersBatchQuery.isPending);
|
||||
const displayName = profile?.displayName ?? truncatePubkey(pubkey);
|
||||
// Owner signal mirrors UserProfilePanel: a declared NIP-OA owner whose agent
|
||||
// runs elsewhere holds no local seckey, so key custody (`isOwner`) alone
|
||||
@@ -217,6 +236,8 @@ export function UserProfilePopover({
|
||||
currentPubkey !== undefined &&
|
||||
currentPubkey.toLowerCase() === pubkey.toLowerCase();
|
||||
const showProfileActions = currentPubkey !== undefined && !isSelf;
|
||||
const showHumanProfileActions =
|
||||
showProfileActions && !isBotProfile && !isAgentClassificationPending;
|
||||
const selfProfileQuery = useProfileQuery(open && showProfileActions);
|
||||
const isCurrentUserOwner =
|
||||
currentPubkey !== undefined &&
|
||||
@@ -314,9 +335,9 @@ export function UserProfilePopover({
|
||||
const handleHuddle = React.useCallback(async () => {
|
||||
if (
|
||||
!showProfileActions ||
|
||||
!showHumanProfileActions ||
|
||||
pendingAction !== null ||
|
||||
isStartingHuddle ||
|
||||
isAgentClassificationPending
|
||||
isStartingHuddle
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -327,7 +348,7 @@ export function UserProfilePopover({
|
||||
try {
|
||||
const dm = await openDmMutation.mutateAsync({ pubkeys: [pubkey] });
|
||||
await goChannel(dm.id);
|
||||
await startHuddle(dm.id, isBotProfile ? [pubkey] : []);
|
||||
await startHuddle(dm.id, []);
|
||||
await queryClient.invalidateQueries({ queryKey: channelsQueryKey });
|
||||
if (isMountedRef.current) {
|
||||
setOpen(false);
|
||||
@@ -344,19 +365,24 @@ export function UserProfilePopover({
|
||||
}, [
|
||||
clearHoverTimer,
|
||||
goChannel,
|
||||
isAgentClassificationPending,
|
||||
isBotProfile,
|
||||
isStartingHuddle,
|
||||
openDmMutation,
|
||||
pendingAction,
|
||||
pubkey,
|
||||
queryClient,
|
||||
showHumanProfileActions,
|
||||
showProfileActions,
|
||||
startHuddle,
|
||||
]);
|
||||
|
||||
const handleWave = React.useCallback(async () => {
|
||||
if (!showProfileActions || pendingAction !== null) return;
|
||||
if (
|
||||
!showProfileActions ||
|
||||
!showHumanProfileActions ||
|
||||
pendingAction !== null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearHoverTimer();
|
||||
setPendingAction("wave");
|
||||
@@ -444,6 +470,7 @@ export function UserProfilePopover({
|
||||
pubkey,
|
||||
queryClient,
|
||||
selfProfileQuery.data?.displayName,
|
||||
showHumanProfileActions,
|
||||
showProfileActions,
|
||||
]);
|
||||
|
||||
@@ -594,34 +621,36 @@ export function UserProfilePopover({
|
||||
) : null}
|
||||
{showProfileActions ? (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
aria-label="Wave"
|
||||
className="buzz-wave-hover-trigger shrink-0 px-3 transition-transform duration-100 ease-out motion-reduce:transition-none motion-safe:active:scale-[0.97]"
|
||||
data-testid={`user-profile-popover-wave-${pubkey}`}
|
||||
disabled={
|
||||
pendingAction !== null || openDmMutation.isPending
|
||||
}
|
||||
onClick={() => {
|
||||
void handleWave();
|
||||
}}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
{pendingAction === "wave" ? (
|
||||
<Spinner
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5 border-2"
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="buzz-wave-hand text-sm leading-none"
|
||||
>
|
||||
👋
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
{showHumanProfileActions ? (
|
||||
<Button
|
||||
aria-label="Wave"
|
||||
className="buzz-wave-hover-trigger shrink-0 px-3 transition-transform duration-100 ease-out motion-reduce:transition-none motion-safe:active:scale-[0.97]"
|
||||
data-testid={`user-profile-popover-wave-${pubkey}`}
|
||||
disabled={
|
||||
pendingAction !== null || openDmMutation.isPending
|
||||
}
|
||||
onClick={() => {
|
||||
void handleWave();
|
||||
}}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
{pendingAction === "wave" ? (
|
||||
<Spinner
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5 border-2"
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="buzz-wave-hand text-sm leading-none"
|
||||
>
|
||||
👋
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
className="min-w-0 flex-1"
|
||||
data-testid={`user-profile-popover-message-${pubkey}`}
|
||||
@@ -645,32 +674,33 @@ export function UserProfilePopover({
|
||||
)}
|
||||
Message
|
||||
</Button>
|
||||
<Button
|
||||
className="min-w-0 flex-1"
|
||||
data-testid={`user-profile-popover-huddle-${pubkey}`}
|
||||
disabled={
|
||||
pendingAction !== null ||
|
||||
openDmMutation.isPending ||
|
||||
isStartingHuddle ||
|
||||
isAgentClassificationPending
|
||||
}
|
||||
onClick={() => {
|
||||
void handleHuddle();
|
||||
}}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
{pendingAction === "huddle" ? (
|
||||
<Spinner
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5 border-2"
|
||||
/>
|
||||
) : (
|
||||
<Headphones />
|
||||
)}
|
||||
Huddle
|
||||
</Button>
|
||||
{showHumanProfileActions ? (
|
||||
<Button
|
||||
className="min-w-0 flex-1"
|
||||
data-testid={`user-profile-popover-huddle-${pubkey}`}
|
||||
disabled={
|
||||
pendingAction !== null ||
|
||||
openDmMutation.isPending ||
|
||||
isStartingHuddle
|
||||
}
|
||||
onClick={() => {
|
||||
void handleHuddle();
|
||||
}}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
{pendingAction === "huddle" ? (
|
||||
<Spinner
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5 border-2"
|
||||
/>
|
||||
) : (
|
||||
<Headphones />
|
||||
)}
|
||||
Huddle
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
+106
-184
@@ -26,8 +26,6 @@ const CASEY_PROFILE_PUBKEY =
|
||||
"1111111111111111111111111111111111111111111111111111111111111111";
|
||||
const PROFILE_ONLY_AGENT_PUBKEY =
|
||||
"8f83d6b7f3d74f7d933ae3a54dd8c6cc85c7f98e531c16e5a827b953441a8d67";
|
||||
const UNSEEDED_PROFILE_ONLY_AGENT_PUBKEY =
|
||||
"7777777777777777777777777777777777777777777777777777777777777777";
|
||||
const SYSTEM_MESSAGE_KIND = 40099;
|
||||
|
||||
/** Locator scoped to the mention autocomplete dropdown inside the composer. */
|
||||
@@ -65,27 +63,6 @@ function commandCount(commands: string[], command: string) {
|
||||
return commands.filter((entry) => entry === command).length;
|
||||
}
|
||||
|
||||
async function readStartHuddleMemberPubkeys(
|
||||
page: import("@playwright/test").Page,
|
||||
) {
|
||||
const commandLog = await readCommandPayloadLog(page);
|
||||
return commandLog.flatMap((entry) => {
|
||||
if (entry.command !== "start_huddle") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const payload =
|
||||
entry.payload && typeof entry.payload === "object"
|
||||
? (entry.payload as {
|
||||
memberPubkeys?: unknown;
|
||||
member_pubkeys?: unknown;
|
||||
})
|
||||
: null;
|
||||
const memberPubkeys = payload?.memberPubkeys ?? payload?.member_pubkeys;
|
||||
return Array.isArray(memberPubkeys) ? memberPubkeys.map(String) : [];
|
||||
});
|
||||
}
|
||||
|
||||
async function emitMockMessage(
|
||||
page: import("@playwright/test").Page,
|
||||
channelName: string,
|
||||
@@ -168,6 +145,21 @@ async function waitForTimelineSettled(page: import("@playwright/test").Page) {
|
||||
await expect(page.locator("[data-render-pending]")).toHaveCount(0);
|
||||
}
|
||||
|
||||
async function expectAgentProfileMessageOnly(
|
||||
profilePopover: import("@playwright/test").Locator,
|
||||
pubkey: string,
|
||||
) {
|
||||
await expect(
|
||||
profilePopover.getByTestId(`user-profile-popover-message-${pubkey}`),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
profilePopover.getByTestId(`user-profile-popover-wave-${pubkey}`),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
profilePopover.getByTestId(`user-profile-popover-huddle-${pubkey}`),
|
||||
).toHaveCount(0);
|
||||
}
|
||||
|
||||
test("@ trigger shows unified autocomplete with agents first", async ({
|
||||
page,
|
||||
}) => {
|
||||
@@ -916,9 +908,7 @@ test("system add and remove rows use agent mention styling for managed agents",
|
||||
).toHaveText("portal");
|
||||
});
|
||||
|
||||
test("system agent profile huddle passes profile-only bot pubkey", async ({
|
||||
page,
|
||||
}) => {
|
||||
test("system agent profile only exposes message action", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
@@ -960,18 +950,13 @@ test("system agent profile huddle passes profile-only bot pubkey", async ({
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-huddle-${PROFILE_ONLY_AGENT_PUBKEY}`)
|
||||
.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([PROFILE_ONLY_AGENT_PUBKEY]));
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
PROFILE_ONLY_AGENT_PUBKEY,
|
||||
);
|
||||
});
|
||||
|
||||
test("system agent avatar huddle passes profile-only bot pubkey", async ({
|
||||
page,
|
||||
}) => {
|
||||
test("system agent avatar only exposes message action", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
@@ -1006,13 +991,48 @@ test("system agent avatar huddle passes profile-only bot pubkey", async ({
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-huddle-${PROFILE_ONLY_AGENT_PUBKEY}`)
|
||||
.click();
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
PROFILE_ONLY_AGENT_PUBKEY,
|
||||
);
|
||||
});
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([PROFILE_ONLY_AGENT_PUBKEY]));
|
||||
test("profile-only agent author popover only exposes message action", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
searchProfiles: [
|
||||
{
|
||||
pubkey: PROFILE_ONLY_AGENT_PUBKEY,
|
||||
displayName: "mira",
|
||||
isAgent: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
await waitForMockLiveSubscription(page, "general");
|
||||
|
||||
await emitMockMessage(page, "general", "Mira status update.", {
|
||||
pubkey: PROFILE_ONLY_AGENT_PUBKEY,
|
||||
});
|
||||
await waitForTimelineSettled(page);
|
||||
|
||||
const messageRow = page
|
||||
.getByTestId("message-row")
|
||||
.filter({ hasText: "Mira status update." })
|
||||
.first();
|
||||
await messageRow.locator("button").first().hover();
|
||||
|
||||
const profilePopover = page.locator(
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
PROFILE_ONLY_AGENT_PUBKEY,
|
||||
);
|
||||
});
|
||||
|
||||
test("system member-joined rows render the joined person as a mention chip", async ({
|
||||
@@ -1315,7 +1335,7 @@ test("hovering avatar opens popover, clicking opens profile panel", async ({
|
||||
await expect(page.getByTestId("user-profile-panel")).toBeVisible();
|
||||
});
|
||||
|
||||
test("bot profile huddle passes the bot pubkey", async ({ page }) => {
|
||||
test("bot profile only exposes message action", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-agents").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("agents");
|
||||
@@ -1331,18 +1351,13 @@ test("bot profile huddle passes the bot pubkey", async ({ page }) => {
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await expect(profilePopover.getByText("Codex")).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(
|
||||
`user-profile-popover-huddle-${TEST_IDENTITIES.charlie.pubkey}`,
|
||||
)
|
||||
.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([TEST_IDENTITIES.charlie.pubkey]));
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
TEST_IDENTITIES.charlie.pubkey,
|
||||
);
|
||||
});
|
||||
|
||||
test("agent mention profile huddle passes the bot pubkey", async ({ page }) => {
|
||||
test("agent mention profile only exposes message action", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
@@ -1364,39 +1379,57 @@ test("agent mention profile huddle passes the bot pubkey", async ({ page }) => {
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(
|
||||
`user-profile-popover-huddle-${TEST_IDENTITIES.charlie.pubkey}`,
|
||||
)
|
||||
.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([TEST_IDENTITIES.charlie.pubkey]));
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
TEST_IDENTITIES.charlie.pubkey,
|
||||
);
|
||||
});
|
||||
|
||||
test("profile popover wave sends a direct message", async ({ page }) => {
|
||||
test("profile popover wave sends a direct message for a human profile", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, { sendMessageDelayMs: 2_500 });
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
await waitForMockLiveSubscription(page, "general");
|
||||
|
||||
const aliceMessage = page
|
||||
await emitMockMessage(page, "general", "Bob says hello.", {
|
||||
pubkey: TEST_IDENTITIES.bob.pubkey,
|
||||
});
|
||||
await waitForTimelineSettled(page);
|
||||
|
||||
const bobMessage = page
|
||||
.getByTestId("message-row")
|
||||
.filter({ hasText: "Hey team — checking in." })
|
||||
.filter({ hasText: "Bob says hello." })
|
||||
.first();
|
||||
await aliceMessage.locator("button").first().hover();
|
||||
await bobMessage.locator("button").first().hover();
|
||||
|
||||
const profilePopover = page.locator(
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-message-${TEST_IDENTITIES.bob.pubkey}`,
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-huddle-${TEST_IDENTITIES.bob.pubkey}`,
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-wave-${TEST_IDENTITIES.bob.pubkey}`,
|
||||
),
|
||||
).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-wave-${TEST_IDENTITIES.alice.pubkey}`)
|
||||
.getByTestId(`user-profile-popover-wave-${TEST_IDENTITIES.bob.pubkey}`)
|
||||
.click();
|
||||
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("alice-tyler");
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("bob-tyler");
|
||||
const waveAttachment = page.getByTestId("message-wave-attachment");
|
||||
await expect(waveAttachment).toBeVisible({ timeout: 1_500 });
|
||||
await expect(page.getByText("Sending")).toHaveCount(0, { timeout: 4_000 });
|
||||
@@ -1431,108 +1464,7 @@ test("profile popover wave sends a direct message", async ({ page }) => {
|
||||
);
|
||||
});
|
||||
|
||||
test("wave attachment huddle passes the bot DM pubkey", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-agents").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("agents");
|
||||
|
||||
const charlieMessage = page
|
||||
.getByTestId("message-row")
|
||||
.filter({ hasText: "Indexing the channel catalog now." })
|
||||
.first();
|
||||
await charlieMessage.locator("button").first().hover();
|
||||
|
||||
const profilePopover = page.locator(
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await expect(profilePopover.getByText("Codex")).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-wave-${TEST_IDENTITIES.charlie.pubkey}`)
|
||||
.click();
|
||||
|
||||
await expect(page.getByTestId("message-wave-attachment")).toBeVisible();
|
||||
await page
|
||||
.getByTestId("message-wave-attachment")
|
||||
.getByRole("button", { name: "Start huddle" })
|
||||
.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([TEST_IDENTITIES.charlie.pubkey]));
|
||||
});
|
||||
|
||||
test("wave attachment huddle waits for placeholder profile-only bot data", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
searchProfiles: [
|
||||
{
|
||||
pubkey: UNSEEDED_PROFILE_ONLY_AGENT_PUBKEY,
|
||||
displayName: "nova",
|
||||
ownerPubkey: TEST_IDENTITIES.tyler.pubkey,
|
||||
isAgent: true,
|
||||
},
|
||||
],
|
||||
usersBatchDelayMs: 2_000,
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
await waitForMockLiveSubscription(page, "general", SYSTEM_MESSAGE_KIND);
|
||||
|
||||
await page.evaluate(
|
||||
({ kind, targetPubkey }) => {
|
||||
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
|
||||
channelName: "general",
|
||||
content: JSON.stringify({
|
||||
type: "member_joined",
|
||||
actor: targetPubkey,
|
||||
target: targetPubkey,
|
||||
}),
|
||||
kind,
|
||||
});
|
||||
},
|
||||
{
|
||||
kind: SYSTEM_MESSAGE_KIND,
|
||||
targetPubkey: UNSEEDED_PROFILE_ONLY_AGENT_PUBKEY,
|
||||
},
|
||||
);
|
||||
await waitForTimelineSettled(page);
|
||||
|
||||
const joinedRow = page
|
||||
.getByTestId("system-message-row")
|
||||
.filter({ hasText: "joined the channel" });
|
||||
const agentChip = joinedRow.locator("[data-mention]", {
|
||||
hasText: "77777777…7777",
|
||||
});
|
||||
await expect(agentChip).toBeVisible({ timeout: 5_000 });
|
||||
await agentChip.hover();
|
||||
|
||||
const profilePopover = page.locator(
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(
|
||||
`user-profile-popover-wave-${UNSEEDED_PROFILE_ONLY_AGENT_PUBKEY}`,
|
||||
)
|
||||
.click();
|
||||
|
||||
const startHuddleButton = page
|
||||
.getByTestId("message-wave-attachment")
|
||||
.getByRole("button", { name: "Start huddle" });
|
||||
await expect(startHuddleButton).toBeDisabled();
|
||||
await expect(startHuddleButton).toBeEnabled({ timeout: 5_000 });
|
||||
await startHuddleButton.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([UNSEEDED_PROFILE_ONLY_AGENT_PUBKEY]));
|
||||
});
|
||||
|
||||
test("wave attachment huddle waits for delayed bot DM pubkey", async ({
|
||||
test("delayed agent profile keeps wave and huddle hidden while classifying", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
@@ -1572,18 +1504,8 @@ test("wave attachment huddle waits for delayed bot DM pubkey", async ({
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-wave-${DELAYED_RELAY_AGENT_PUBKEY}`)
|
||||
.click();
|
||||
|
||||
const startHuddleButton = page
|
||||
.getByTestId("message-wave-attachment")
|
||||
.getByRole("button", { name: "Start huddle" });
|
||||
await expect(startHuddleButton).toBeDisabled();
|
||||
await expect(startHuddleButton).toBeEnabled({ timeout: 7_000 });
|
||||
await startHuddleButton.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([DELAYED_RELAY_AGENT_PUBKEY]));
|
||||
await expectAgentProfileMessageOnly(
|
||||
profilePopover,
|
||||
DELAYED_RELAY_AGENT_PUBKEY,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -83,42 +83,6 @@ async function selectHomeInboxFilter(
|
||||
await page.getByRole("menuitemradio", { name: label }).click();
|
||||
}
|
||||
|
||||
async function readCommandPayloadLog(page: import("@playwright/test").Page) {
|
||||
return page.evaluate(() => {
|
||||
return (
|
||||
(
|
||||
window as Window & {
|
||||
__BUZZ_E2E_COMMAND_LOG__?: Array<{
|
||||
command: string;
|
||||
payload: unknown;
|
||||
}>;
|
||||
}
|
||||
).__BUZZ_E2E_COMMAND_LOG__ ?? []
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function readStartHuddleMemberPubkeys(
|
||||
page: import("@playwright/test").Page,
|
||||
) {
|
||||
const commandLog = await readCommandPayloadLog(page);
|
||||
return commandLog.flatMap((entry) => {
|
||||
if (entry.command !== "start_huddle") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const payload =
|
||||
entry.payload && typeof entry.payload === "object"
|
||||
? (entry.payload as {
|
||||
memberPubkeys?: unknown;
|
||||
member_pubkeys?: unknown;
|
||||
})
|
||||
: null;
|
||||
const memberPubkeys = payload?.memberPubkeys ?? payload?.member_pubkeys;
|
||||
return Array.isArray(memberPubkeys) ? memberPubkeys.map(String) : [];
|
||||
});
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
});
|
||||
@@ -230,7 +194,7 @@ test("inbox feed shows channel and agent activity sections", async ({
|
||||
);
|
||||
});
|
||||
|
||||
test("inbox agent hover huddle passes the agent pubkey", async ({ page }) => {
|
||||
test("inbox agent hover only exposes message action", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
|
||||
await selectHomeInboxFilter(page, "Agents");
|
||||
@@ -244,13 +208,21 @@ test("inbox agent hover huddle passes the agent pubkey", async ({ page }) => {
|
||||
'[data-testid="user-profile-popover"][data-state="open"]',
|
||||
);
|
||||
await expect(profilePopover).toBeVisible();
|
||||
await profilePopover
|
||||
.getByTestId(`user-profile-popover-huddle-${DEFAULT_AGENT_ACTIVITY_PUBKEY}`)
|
||||
.click();
|
||||
|
||||
await expect
|
||||
.poll(() => readStartHuddleMemberPubkeys(page))
|
||||
.toEqual(expect.arrayContaining([DEFAULT_AGENT_ACTIVITY_PUBKEY]));
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-message-${DEFAULT_AGENT_ACTIVITY_PUBKEY}`,
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-wave-${DEFAULT_AGENT_ACTIVITY_PUBKEY}`,
|
||||
),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
profilePopover.getByTestId(
|
||||
`user-profile-popover-huddle-${DEFAULT_AGENT_ACTIVITY_PUBKEY}`,
|
||||
),
|
||||
).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("opens a mocked forum activity item from the inbox feed", async ({
|
||||
|
||||
Reference in New Issue
Block a user