fix interactive error
This commit is contained in:
@@ -95,9 +95,74 @@ describe('MessageBubble', () => {
|
||||
await waitFor(() =>
|
||||
expect(copyTextToClipboard).toHaveBeenCalledWith('第一行\n第二行'),
|
||||
);
|
||||
expect(
|
||||
await screen.findByRole('menuitem', { name: '已复制' }),
|
||||
).toBeTruthy();
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.queryByRole('menu', { name: '消息右键菜单' }),
|
||||
).toBeNull(),
|
||||
);
|
||||
});
|
||||
|
||||
it('closes the previous menu before another right click opens a new one', () => {
|
||||
render(
|
||||
<>
|
||||
<MessageBubble
|
||||
message={{
|
||||
id: 40,
|
||||
role: 'assistant',
|
||||
text: '第一条消息',
|
||||
attachments: [],
|
||||
toolCall: null,
|
||||
createdAt: '2026-07-20T00:00:00Z',
|
||||
}}
|
||||
busyAction={null}
|
||||
onConfirmToolCall={vi.fn()}
|
||||
onCancelToolCall={vi.fn()}
|
||||
/>
|
||||
<MessageBubble
|
||||
message={{
|
||||
id: 41,
|
||||
role: 'assistant',
|
||||
text: '第二条消息',
|
||||
attachments: [],
|
||||
toolCall: null,
|
||||
createdAt: '2026-07-20T00:00:00Z',
|
||||
}}
|
||||
busyAction={null}
|
||||
onConfirmToolCall={vi.fn()}
|
||||
onCancelToolCall={vi.fn()}
|
||||
/>
|
||||
</>,
|
||||
);
|
||||
|
||||
const messages = screen.getAllByLabelText('Agent消息');
|
||||
fireEvent.contextMenu(messages[0]!, { clientX: 30, clientY: 40 });
|
||||
fireEvent.contextMenu(messages[1]!, { clientX: 70, clientY: 80 });
|
||||
|
||||
const menus = screen.getAllByRole('menu', { name: '消息右键菜单' });
|
||||
expect(menus).toHaveLength(1);
|
||||
expect(menus[0]?.style.left).toBe('70px');
|
||||
expect(menus[0]?.style.top).toBe('80px');
|
||||
});
|
||||
|
||||
it('closes an open menu when the next right click has no menu target', () => {
|
||||
renderMessage({
|
||||
id: 42,
|
||||
role: 'assistant',
|
||||
text: '右键后应可收起',
|
||||
attachments: [],
|
||||
toolCall: null,
|
||||
createdAt: '2026-07-20T00:00:00Z',
|
||||
});
|
||||
|
||||
fireEvent.contextMenu(screen.getByLabelText('Agent消息'), {
|
||||
clientX: 30,
|
||||
clientY: 40,
|
||||
});
|
||||
expect(screen.getByRole('menu', { name: '消息右键菜单' })).toBeTruthy();
|
||||
|
||||
fireEvent.contextMenu(document.body, { clientX: 70, clientY: 80 });
|
||||
|
||||
expect(screen.queryByRole('menu', { name: '消息右键菜单' })).toBeNull();
|
||||
});
|
||||
|
||||
it('copies an attached WebP as PNG without opening the message text menu', async () => {
|
||||
@@ -182,7 +247,11 @@ describe('MessageBubble', () => {
|
||||
.fn()
|
||||
.mockResolvedValue(new Blob(['webp'], { type: 'image/webp' })),
|
||||
} as unknown as Response);
|
||||
await screen.findByRole('menuitem', { name: '已复制' });
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.queryByRole('menu', { name: '消息素材右键菜单' }),
|
||||
).toBeNull(),
|
||||
);
|
||||
expect(bitmapClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -219,9 +288,11 @@ describe('MessageBubble', () => {
|
||||
objectKey: 'editor/reference.png',
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
await screen.findByRole('menuitem', { name: '已引用' }),
|
||||
).toBeTruthy();
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.queryByRole('menu', { name: '消息素材右键菜单' }),
|
||||
).toBeNull(),
|
||||
);
|
||||
});
|
||||
|
||||
it('offers reference for generated images using their media lookup fields', async () => {
|
||||
@@ -335,6 +406,9 @@ describe('MessageBubble', () => {
|
||||
fireEvent.click(screen.getByRole('menuitem', { name: '下载视频' }));
|
||||
|
||||
await waitFor(() => expect(anchorClick).toHaveBeenCalledTimes(1));
|
||||
expect(
|
||||
screen.queryByRole('menu', { name: '消息素材右键菜单' }),
|
||||
).toBeNull();
|
||||
expect(createObjectURL).toHaveBeenCalledWith(expect.any(Blob));
|
||||
expect(revokeObjectURL).toHaveBeenCalledWith('blob:agent-video');
|
||||
});
|
||||
|
||||
@@ -115,17 +115,25 @@ export function MessageBubbleRightClickMenu({
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
const handleContextMenu = (event: MouseEvent) => {
|
||||
if (menuRef.current?.contains(event.target as Node)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener('pointerdown', handlePointerDown);
|
||||
window.addEventListener('contextmenu', handleContextMenu, true);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('scroll', onClose, true);
|
||||
window.addEventListener('resize', onClose);
|
||||
return () => {
|
||||
window.removeEventListener('pointerdown', handlePointerDown);
|
||||
window.removeEventListener('contextmenu', handleContextMenu, true);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('scroll', onClose, true);
|
||||
window.removeEventListener('resize', onClose);
|
||||
|
||||
@@ -197,12 +197,14 @@ export function useRightClickMenu({
|
||||
: false;
|
||||
setRightClickMenu((current) =>
|
||||
current?.target === target && current.pendingAction === action
|
||||
? {
|
||||
...current,
|
||||
pendingAction: null,
|
||||
resultAction: action,
|
||||
result: succeeded ? 'success' : 'error',
|
||||
}
|
||||
? succeeded
|
||||
? null
|
||||
: {
|
||||
...current,
|
||||
pendingAction: null,
|
||||
resultAction: action,
|
||||
result: 'error',
|
||||
}
|
||||
: current,
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user