收口 Makelore 客户端变更
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-12 22:35:06 +08:00
parent 253bad8b40
commit f4113a872f
232 changed files with 4617 additions and 20121 deletions

View File

@@ -1,6 +1,7 @@
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { AgentBrowserPanel } from '@/pages/Chat/AgentBrowserPanel';
import { TitleBar } from '@/components/layout/TitleBar';
import { deriveAgentBrowserDiagnostics } from '@/lib/agent-browser';
import type {
AgentBrowserCdpEvent,
@@ -123,6 +124,10 @@ describe('AgentBrowserPanel', () => {
beforeEach(() => {
hostEventListeners.clear();
resizeCallback = null;
Object.defineProperty(document, 'elementsFromPoint', {
configurable: true,
value: vi.fn().mockReturnValue([]),
});
Object.defineProperty(window, 'ResizeObserver', {
configurable: true,
value: MockResizeObserver,
@@ -144,6 +149,126 @@ describe('AgentBrowserPanel', () => {
expect(screen.getByRole('button', { name: '打开开发浏览器' })).toBeDisabled();
});
it('renders the collapsed toggle in the fixed titlebar layer', async () => {
installBrowserApi(snapshot());
render(<AgentBrowserPanel projectPath="D:/repo" />);
const titlebarToggle = await screen.findByTestId('agent-browser-titlebar-toggle');
const button = titlebarToggle.querySelector('[data-testid="agent-browser-panel-open"]') as HTMLElement;
expect(button).toHaveAttribute('aria-label', '打开开发浏览器');
expect(button).toHaveClass('no-drag');
expect(button).toHaveClass('bg-transparent');
expect(button).not.toHaveClass('rounded-full');
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
fireEvent.click(button);
expect(await screen.findByTestId('agent-browser-panel')).toBeInTheDocument();
fireEvent.click(titlebarToggle.querySelector('[data-testid="agent-browser-panel-open"]') as HTMLElement);
await waitFor(() => {
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
});
});
it('keeps the active browser indicator inside the titlebar toggle bounds', async () => {
installBrowserApi(snapshot('attached'));
render(<AgentBrowserPanel projectPath="D:/repo" />);
const titlebarToggle = await screen.findByTestId('agent-browser-titlebar-toggle');
await waitFor(() => {
expect(titlebarToggle.querySelector('span[aria-hidden="true"]')).toBeInTheDocument();
});
const indicator = titlebarToggle.querySelector('span[aria-hidden="true"]');
expect(indicator).toHaveClass('absolute', 'right-2', 'top-2');
expect(indicator).not.toHaveClass('right-0.5', 'top-0.5');
});
it('keeps the toggle visible when mounted with the integrated titlebar', async () => {
window.electron.platform = 'darwin';
installBrowserApi(snapshot());
render(
<>
<TitleBar integrated />
<AgentBrowserPanel projectPath="D:/repo" />
</>,
);
const titlebarToggle = await screen.findByTestId('agent-browser-titlebar-toggle');
await waitFor(() => {
expect(titlebarToggle.querySelector('[data-testid="agent-browser-panel-open"]')).toBeInTheDocument();
});
expect(titlebarToggle).toHaveClass('fixed', 'top-0', 'right-0', 'z-[100]', 'pointer-events-auto');
expect(titlebarToggle).toHaveStyle({ right: '0px' });
expect(document.documentElement.style.getPropertyValue('--agent-browser-titlebar-logo-gap')).toBe('');
expect(screen.queryByTestId('agent-browser-panel-close')).not.toBeInTheDocument();
fireEvent.click(titlebarToggle.querySelector('[data-testid="agent-browser-panel-open"]') as HTMLElement);
await waitFor(() => {
expect(document.documentElement.style.getPropertyValue('--agent-browser-titlebar-logo-gap')).toBe('4px');
});
});
it('starts the browser at the same baseline width as the other workspace columns', async () => {
installBrowserApi(snapshot());
render(
<div data-testid="opencode-chat-layout">
<div data-testid="agent-conversation-sidebar" />
<div data-testid="opencode-chat-canvas" />
<AgentBrowserPanel projectPath="D:/repo" />
</div>,
);
vi.spyOn(screen.getByTestId('opencode-chat-layout'), 'getBoundingClientRect')
.mockReturnValue({ width: 960 } as DOMRect);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
expect(await screen.findByTestId('agent-browser-panel')).toHaveStyle({ width: '320px' });
expect(screen.getByTestId('agent-browser-panel-spacer')).toHaveStyle({ width: '320px' });
});
it('keeps diagnostics collapsed and expands Console or Network upward from the bottom', async () => {
installBrowserApi(snapshot());
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
const diagnostics = await screen.findByTestId('agent-browser-diagnostics');
const consoleTab = screen.getByRole('tab', { name: /Console/ });
expect(screen.queryByText('尚未开启')).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: '开启 AI 调试' })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: '打开' })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: '清空调试记录' })).not.toBeInTheDocument();
expect(diagnostics).toHaveAttribute('data-state', 'closed');
expect(diagnostics).toHaveStyle({ height: '42px' });
expect(consoleTab).toHaveAttribute('aria-expanded', 'false');
expect(screen.queryByTestId('agent-browser-diagnostics-resize-handle')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '展开调试面板' }));
expect(diagnostics).toHaveAttribute('data-state', 'open');
fireEvent.click(screen.getByRole('button', { name: '收起调试面板' }));
expect(diagnostics).toHaveAttribute('data-state', 'closed');
fireEvent.click(consoleTab);
expect(diagnostics).toHaveAttribute('data-state', 'open');
expect(diagnostics).toHaveStyle({ height: '260px' });
expect(consoleTab).toHaveAttribute('aria-expanded', 'true');
expect(screen.getByTestId('agent-browser-diagnostics-resize-handle')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: /Network/ }));
expect(screen.getByLabelText('Network 请求')).toBeInTheDocument();
const resizeHandle = screen.getByTestId('agent-browser-diagnostics-resize-handle');
fireEvent.pointerDown(resizeHandle, { button: 0, clientY: 500, pointerId: 1 });
fireEvent.pointerMove(resizeHandle, { clientY: 420, pointerId: 1 });
expect(diagnostics).toHaveStyle({ height: '340px' });
fireEvent.pointerUp(resizeHandle, { pointerId: 1 });
fireEvent.click(screen.getByRole('button', { name: '收起调试面板' }));
expect(diagnostics).toHaveAttribute('data-state', 'closed');
expect(diagnostics).toHaveStyle({ height: '42px' });
});
it('keeps bounded placeholders for oversized Console and Network events', () => {
const diagnostics = deriveAgentBrowserDiagnostics([
{
@@ -231,7 +356,7 @@ describe('AgentBrowserPanel', () => {
fireEvent.change(screen.getByRole('textbox', { name: '网页地址' }), {
target: { value: 'localhost:4173' },
});
fireEvent.click(screen.getByRole('button', { name: '打开' }));
fireEvent.submit(screen.getByRole('textbox', { name: '网页地址' }).closest('form') as HTMLFormElement);
await waitFor(() => {
const call = hostApiFetchMock.mock.calls.find(
@@ -248,7 +373,20 @@ describe('AgentBrowserPanel', () => {
expect(window.devicePixelRatio).toBe(2);
});
it('expands when the agent opens the shared browser', async () => {
it('does not invent a URL when the address field is empty', async () => {
installBrowserApi(snapshot());
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
const addressInput = screen.getByRole('textbox', { name: '网页地址' });
expect(addressInput).toHaveValue('');
fireEvent.submit(addressInput.closest('form') as HTMLFormElement);
await new Promise((resolve) => window.setTimeout(resolve, 0));
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/agent-browser/open')).toBe(false);
});
it('keeps the panel collapsed when the agent opens the shared browser', async () => {
installBrowserApi(snapshot('attached'));
render(<AgentBrowserPanel projectId="project-1" projectPath="D:/repo" />);
@@ -257,8 +395,13 @@ describe('AgentBrowserPanel', () => {
hostEventListeners.get('agent-browser:show')?.(snapshot('attached'));
});
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
expect(screen.getByTestId('agent-browser-panel-open')).toHaveAttribute(
'title',
'开发浏览器已收起AI 调试已暂停',
);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
expect(await screen.findByTestId('agent-browser-panel')).toBeInTheDocument();
expect(screen.getByTestId('agent-browser-debug-status')).toHaveTextContent('AI 可调试');
});
it('ignores a delayed show event from the previous project', () => {
@@ -277,12 +420,14 @@ describe('AgentBrowserPanel', () => {
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
});
it('restores an already active shared browser after remounting', async () => {
it('restores an already active shared browser without expanding the panel', async () => {
installBrowserApi(snapshot('attached'));
render(<AgentBrowserPanel projectId="project-1" projectPath="D:/repo" />);
expect(await screen.findByTestId('agent-browser-panel-open')).toBeInTheDocument();
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
expect(await screen.findByTestId('agent-browser-panel')).toBeInTheDocument();
expect(screen.getByTestId('agent-browser-debug-status')).toHaveTextContent('AI 可调试');
});
it('updates native view bounds and hides it when collapsed', async () => {
@@ -290,7 +435,6 @@ describe('AgentBrowserPanel', () => {
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByRole('button', { name: '打开开发浏览器' }));
expect(await screen.findByText('AI 可调试')).toBeInTheDocument();
const viewport = screen.getByTestId('agent-browser-viewport');
vi.spyOn(viewport, 'getBoundingClientRect').mockReturnValue({
x: 240,
@@ -324,10 +468,51 @@ describe('AgentBrowserPanel', () => {
});
});
it('resizes the panel from its left edge and auto-collapses below the threshold', async () => {
installBrowserApi(snapshot('attached'));
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
const panel = await screen.findByTestId('agent-browser-panel');
const handle = screen.getByTestId('agent-browser-panel-resize-handle');
const panelRect = vi.spyOn(panel, 'getBoundingClientRect');
panelRect.mockReturnValue({ width: 440 } as DOMRect);
vi.spyOn(panel.parentElement as HTMLElement, 'getBoundingClientRect').mockReturnValue({
width: 1200,
} as DOMRect);
fireEvent.pointerDown(handle, {
button: 0,
clientX: 600,
pointerId: 1,
});
fireEvent.pointerMove(handle, {
clientX: 440,
pointerId: 1,
});
expect(panel).toHaveStyle({ width: '600px' });
panelRect.mockReturnValue({ width: 600 } as DOMRect);
fireEvent.pointerDown(handle, {
button: 0,
clientX: 600,
pointerId: 2,
});
fireEvent.pointerMove(handle, {
clientX: 950,
pointerId: 2,
});
expect(screen.getByTestId('agent-browser-panel')).toHaveAttribute('data-state', 'closed');
await waitFor(() => expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument());
expect(screen.getByTestId('agent-browser-panel-open')).toBeInTheDocument();
});
it('hides the native view while a renderer modal is open', async () => {
installBrowserApi(snapshot('attached'));
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByTestId('agent-browser-panel-open'));
const viewport = await screen.findByTestId('agent-browser-viewport');
vi.spyOn(viewport, 'getBoundingClientRect').mockReturnValue({
x: 240,
@@ -420,6 +605,7 @@ describe('AgentBrowserPanel', () => {
render(<AgentBrowserPanel projectPath="D:/repo" />);
fireEvent.click(screen.getByRole('button', { name: '打开开发浏览器' }));
fireEvent.click(screen.getByRole('tab', { name: /Console/ }));
expect(await screen.findByText('ready')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: /Network/ }));