Integrate project classroom with teacher and friend consultations
This commit is contained in:
@@ -53,6 +53,11 @@ function body(init?: RequestInit): Record<string, unknown> {
|
||||
return JSON.parse(String(init?.body ?? '{}')) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function latestPresentation() {
|
||||
const request = hostApiFetchMock.mock.calls.filter(([path]) => path === '/api/agent-browser/present').at(-1);
|
||||
return request ? body(request[1] as RequestInit) : null;
|
||||
}
|
||||
|
||||
function Harness({ initialOpen = false }: { initialOpen?: boolean }) {
|
||||
const [open, setOpen] = useState(initialOpen);
|
||||
return (
|
||||
@@ -164,4 +169,71 @@ describe('AgentBrowserPanel', () => {
|
||||
([path]) => String(path).startsWith('/api/agent-browser/state?'),
|
||||
)).toHaveLength(1));
|
||||
});
|
||||
|
||||
it('hides an embedded work page between tabs and reopens it without destroying or navigating it', async () => {
|
||||
const onOpenChange = vi.fn();
|
||||
const view = render(<AgentBrowserPanel projectId="project-a" open embedded onOpenChange={onOpenChange} />);
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ project_id: 'project-a', visible: true }));
|
||||
expect(screen.getByRole('textbox', { name: '网页地址' })).toHaveValue('http://127.0.0.1:4173/');
|
||||
|
||||
view.rerender(<AgentBrowserPanel projectId="project-a" open={false} embedded onOpenChange={onOpenChange} />);
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: false }));
|
||||
expect(screen.queryByTestId('agent-browser-panel')).not.toBeInTheDocument();
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/agent-browser/close')).toBe(false);
|
||||
|
||||
view.rerender(<AgentBrowserPanel projectId="project-a" open embedded onOpenChange={onOpenChange} />);
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: true }));
|
||||
expect(screen.getByRole('textbox', { name: '网页地址' })).toHaveValue('http://127.0.0.1:4173/');
|
||||
expect(screen.getByRole('button', { name: '刷新网页' })).toBeEnabled();
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => ['/api/agent-browser/close', '/api/agent-browser/open', '/api/agent-browser/navigate'].includes(String(path)))).toBe(false);
|
||||
|
||||
view.unmount();
|
||||
expect(hostApiFetchMock.mock.calls.filter(([path]) => path === '/api/agent-browser/close')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it.each(['absolute', 'fixed'])('hides the native work page under a %s consultation and restores it beside a relative sidebar', async (position) => {
|
||||
const onOpenChange = vi.fn();
|
||||
const content = (dockPosition: string) => <>
|
||||
<AgentBrowserPanel projectId="project-a" open embedded onOpenChange={onOpenChange} />
|
||||
<aside id="coding-consultation-dock" style={{ position: dockPosition as 'relative' | 'absolute' | 'fixed' }}>老师咨询</aside>
|
||||
</>;
|
||||
const view = render(content('relative'));
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: true }));
|
||||
|
||||
view.rerender(content(position));
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: false }));
|
||||
expect(screen.getByText('老师咨询')).toBeVisible();
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/agent-browser/close')).toBe(false);
|
||||
|
||||
view.rerender(content('relative'));
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: true }));
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/agent-browser/close')).toBe(false);
|
||||
});
|
||||
|
||||
it('rechecks consultation occlusion after a responsive resize without needing a DOM change', async () => {
|
||||
const originalGetComputedStyle = window.getComputedStyle.bind(window);
|
||||
let consultationPosition = 'relative';
|
||||
vi.spyOn(window, 'getComputedStyle').mockImplementation((element, pseudoElement) => {
|
||||
const computed = originalGetComputedStyle(element, pseudoElement);
|
||||
if (element.id !== 'coding-consultation-dock') return computed;
|
||||
// jsdom does not evaluate responsive media queries; simulate their computed position.
|
||||
return new Proxy(computed, {
|
||||
get: (target, property) => property === 'position' ? consultationPosition : Reflect.get(target, property),
|
||||
});
|
||||
});
|
||||
render(<>
|
||||
<AgentBrowserPanel projectId="project-a" open embedded onOpenChange={vi.fn()} />
|
||||
<aside id="coding-consultation-dock">朋友咨询</aside>
|
||||
</>);
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: true }));
|
||||
|
||||
consultationPosition = 'absolute';
|
||||
act(() => window.dispatchEvent(new Event('resize')));
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: false }));
|
||||
|
||||
consultationPosition = 'relative';
|
||||
act(() => window.dispatchEvent(new Event('resize')));
|
||||
await waitFor(() => expect(latestPresentation()).toMatchObject({ visible: true }));
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/agent-browser/close')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user