Remove teacher conversation discussion components
This commit is contained in:
1 parent
592920da06
commit
317def7912
9 files changed
+150
-371
No files matched your search
@@ -1,5 +1,5 @@
|
||||
// Deterministic responses for layout/interaction tests only; never imported by the app.
|
||||
import type { TeacherDefinition, TeacherTopic, TeacherSend, TeacherDiscussionAction } from '../../../shared/coding-teacher';
|
||||
import type { TeacherDefinition, TeacherTopic, TeacherSend } from '../../../shared/coding-teacher';
|
||||
import type { TeacherDiscussionContent } from '../../../shared/teacher-discussion';
|
||||
const definition: TeacherDefinition = { schema_version: 1, teacher_id: 'coding-teacher', name: '麦洛智能体', description: '', avatar_id: 'avatar-01', welcome_message: '', suggested_questions: [], system_prompt: '测试', skills: [], model: { model_id: 'fixture', reasoning_choice: { mode: 'default' } }, limits: { max_input_tokens: 8000, max_output_tokens: 1500 } };
|
||||
const samples: Record<string, TeacherDiscussionContent> = {
|
||||
@@ -28,42 +28,14 @@ export const teacherApi = {
|
||||
read: async () => structuredClone(current),
|
||||
create: async () => structuredClone(current),
|
||||
events: async () => stream,
|
||||
updateDiscussion: async (_base: string, _id: string, input: TeacherDiscussionAction) => {
|
||||
const tool = current.discussion!;
|
||||
if (input.action === 'pause' || input.action === 'finish') tool.status = input.action === 'pause' ? 'paused' : 'finished';
|
||||
else tool.status = 'active';
|
||||
if (input.action === 'back-ideas' && tool.previousIdeas) {
|
||||
if (tool.content.kind === 'structure') tool.previousStructure = structuredClone(tool.content);
|
||||
tool.content = structuredClone(tool.previousIdeas);
|
||||
}
|
||||
if (tool.content.kind === 'ideas') {
|
||||
const item = tool.content.items.find(item => item.id === input.itemId);
|
||||
if (item && input.action === 'keep-idea') item.state = 'kept';
|
||||
if (item && input.action === 'aside-idea') item.state = 'aside';
|
||||
if (item && input.action === 'first-idea') tool.content.firstItemId = item.id;
|
||||
}
|
||||
tool.revision++;
|
||||
snapshot();
|
||||
return structuredClone(current);
|
||||
},
|
||||
send: async (_base: string, _id: string, input: TeacherSend) => {
|
||||
if (input.presentation || input.discussion) throw new Error('New chat requests must not ask for components');
|
||||
current.requests.push({ id: input.requestId, text: input.text, references: [], createdAt: 'now', status: 'running', sourceCursor: { workerGeneration: 0, seq: 1 }, sourceCapturedAt: 'now', includedSourceMessageIds: [], omittedMessages: 0, response: '', discussionContext: input.discussion });
|
||||
snapshot();
|
||||
timer = setTimeout(() => {
|
||||
const request = current.requests.at(-1)!;
|
||||
request.status = 'completed';
|
||||
request.response = '好,已经把你的补充放在上面了。我们可以接着想。';
|
||||
const tool = current.discussion;
|
||||
if (tool?.status === 'active' && input.discussion) {
|
||||
if (input.discussion.transition === 'structure' && tool.content.kind === 'ideas') {
|
||||
tool.previousIdeas = structuredClone(tool.content);
|
||||
tool.content = structuredClone(samples.structure);
|
||||
} else if (tool.content.kind === 'ideas') tool.content.items.push({ id: `new-${tool.revision}`, text: input.text, state: 'kept' });
|
||||
else if (tool.content.kind === 'structure') tool.content.nodes.push({ id: `new-${tool.revision}`, label: input.text, parentId: 'world' });
|
||||
else if (tool.content.kind === 'flow') tool.content.nodes.push({ id: `new-${tool.revision}`, label: input.text, kind: 'outcome' });
|
||||
else tool.content.rows.push({ id: `new-${tool.revision}`, label: '再想想这一点', cells: tool.content.columns.map(col => ({ columnId: col.id, text: '先观察实际玩的时候有什么不同。' })) });
|
||||
tool.revision++;
|
||||
}
|
||||
request.response = '好,我们可以接着聊。';
|
||||
snapshot();
|
||||
}, 350);
|
||||
return structuredClone(current);
|
||||
@@ -73,3 +45,5 @@ export const teacherApi = {
|
||||
export const teacherTopicsPath = () => '/fixture';
|
||||
|
||||
export const legacyTopicBase = (base: string) => base;
|
||||
|
||||
export const teacherConversationsPath = () => '/fixture/conversations';
|
||||
@@ -1879,6 +1879,12 @@ test('project consultations preserve student drafts and switch between work and
|
||||
await expect(composer).toHaveValue('保留我的草稿');
|
||||
await teacher.getByRole('button',{name:'我也说不清,你带我看看',exact:true}).click();
|
||||
await expect(teacher.getByText('你最近做的哪一步,让你停下来想了一会儿?')).toBeVisible();
|
||||
await expect(teacher.getByTestId('teacher-discussion')).toHaveCount(0);
|
||||
const consultationSends = (await readState(electronApp)).captured.filter(item => item.path.endsWith('/messages') && item.method === 'POST');
|
||||
for (const sent of consultationSends) {
|
||||
expect(sent.body).not.toHaveProperty('presentation');
|
||||
expect(sent.body).not.toHaveProperty('discussion');
|
||||
}
|
||||
await expect(teacher.getByRole('textbox',{name:'向智能体提问'})).toHaveValue('还没说完的困惑');
|
||||
await page.getByRole('tab',{name:'操作对话',exact:true}).click();
|
||||
await expect(composer).toBeVisible();
|
||||
|
||||
@@ -8,7 +8,7 @@ test.beforeAll(async () => {
|
||||
server = await createServer({ configFile: false, cacheDir: 'node_modules/.vite-teacher-discussion', resolve: { alias: { '@': resolve('src') } }, esbuild: { jsx: 'automatic' }, server: { host: '127.0.0.1', port: 0 }, plugins: [{
|
||||
name: 'isolated-discussion-test', enforce: 'pre',
|
||||
load(id) {
|
||||
if (id.endsWith('/src/lib/coding-teacher.ts')) return "export { teacherApi, teacherTopicsPath, legacyTopicBase } from '/tests/e2e/fixtures/teacher-discussion-api.ts';";
|
||||
if (id.endsWith('/src/lib/coding-teacher.ts')) return "export { teacherApi, teacherTopicsPath, teacherConversationsPath, legacyTopicBase } from '/tests/e2e/fixtures/teacher-discussion-api.ts';";
|
||||
if (id.endsWith('/src/stores/auth.ts')) return 'export const useAuthStore = selector => selector({user:{userId:"fixture"}});';
|
||||
},
|
||||
configureServer(vite) { vite.middlewares.use((req, res, next) => {
|
||||
@@ -49,7 +49,7 @@ for (const width of [319, 508]) {
|
||||
});
|
||||
}
|
||||
for (const width of [319, 508]) for (const kind of ['ordinary', 'ideas', 'structure', 'flow', 'comparison']) {
|
||||
test(`${kind} pins the current tool and composer at ${width}px`, async ({ page }) => {
|
||||
test(`${kind} history uses the message pane without discussion cards at ${width}px`, async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1180, height: 800 });
|
||||
const errors: string[] = []; page.on('pageerror', error => errors.push(error.message));
|
||||
await page.addInitScript(() => localStorage.setItem(
|
||||
@@ -60,43 +60,23 @@ for (const width of [319, 508]) for (const kind of ['ordinary', 'ideas', 'struct
|
||||
await expect(input).toBeEnabled();
|
||||
await expect(page.locator('form')).not.toHaveText(/^0/);
|
||||
await expect(page.getByText('查看旧版朋友草稿')).toHaveCount(0);
|
||||
const tool = page.getByTestId('teacher-discussion');
|
||||
await expect(page.getByTestId('teacher-discussion')).toHaveCount(0);
|
||||
const composer = await input.boundingBox();
|
||||
await page.locator('.consultation-messages').hover();
|
||||
await page.mouse.wheel(0, -1500);
|
||||
expect((await input.boundingBox())?.y).toBe(composer!.y);
|
||||
expect(composer!.y + composer!.height).toBeLessThan(800);
|
||||
if (kind === 'ordinary') {
|
||||
await expect(tool).toHaveCount(0);
|
||||
await input.fill('保留我的草稿');
|
||||
await page.getByRole('button', { name: '有什么好玩的想法?' }).click();
|
||||
await expect(page.getByText('好,已经把你的补充放在上面了。我们可以接着想。')).toBeVisible();
|
||||
await expect(input).toHaveValue('保留我的草稿');
|
||||
} else {
|
||||
await expect(tool).toHaveAttribute('data-kind', kind);
|
||||
const before = await tool.boundingBox();
|
||||
const composer = await input.boundingBox();
|
||||
await page.locator('.consultation-messages').hover();
|
||||
await page.mouse.wheel(0, -1500);
|
||||
await expect.poll(async () => (await tool.boundingBox())?.y).toBe(before!.y);
|
||||
expect((await input.boundingBox())?.y).toBe(composer!.y);
|
||||
expect(await tool.evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
||||
expect(composer!.y + composer!.height).toBeLessThan(800);
|
||||
await input.fill('它还喜欢和我踢球');
|
||||
await page.getByRole('button', { name: '提问', exact: true }).click();
|
||||
await expect(page.getByRole('status')).toContainText('正在梳理');
|
||||
await expect(page.getByText('好,已经把你的补充放在上面了。我们可以接着想。')).toBeVisible();
|
||||
await expect(tool).toHaveCount(1);
|
||||
await expect(tool).toHaveAttribute('data-kind', kind);
|
||||
if (kind === 'ideas') {
|
||||
await page.getByRole('button', { name: '把想法理一理' }).click();
|
||||
await expect(tool).toHaveAttribute('data-kind', 'structure');
|
||||
await page.getByRole('button', { name: '回去补充想法' }).click();
|
||||
await expect(tool).toHaveAttribute('data-kind', 'ideas');
|
||||
await expect(tool.getByText('它还喜欢和我踢球', { exact: true })).toHaveCount(1);
|
||||
}
|
||||
await input.fill('先留下下一句');
|
||||
await page.getByRole('button', { name: '先这些', exact: true }).click();
|
||||
await expect(tool).toHaveAttribute('data-collapsed', 'true');
|
||||
await expect(input).toHaveValue('先留下下一句');
|
||||
await page.getByRole('button', { name: '接着改', exact: true }).click();
|
||||
await expect(tool).toHaveAttribute('data-collapsed', 'false');
|
||||
}
|
||||
await expect(page.getByText('好,我们可以接着聊。')).toBeVisible();
|
||||
await expect(page.getByTestId('teacher-discussion')).toHaveCount(0);
|
||||
await expect.poll(() => page.locator('.consultation-messages').evaluate(el => el.scrollWidth <= el.clientWidth)).toBe(true);
|
||||
await page.screenshot({ path: test.info().outputPath(`${kind}-${width}.png`) });
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -568,75 +568,66 @@ describe('teacher side chat', () => {
|
||||
});
|
||||
|
||||
|
||||
describe('pinned discussion workspace', () => {
|
||||
const ideas = { id: 'tool', revision: 1, status: 'active' as const, content: {
|
||||
kind: 'ideas' as const, title: '一起想我的宠物游戏', items: [{ id: 'dog', text: '养只小狗', state: 'kept' as const }],
|
||||
} };
|
||||
async function openDiscussion(status: 'active' | 'offered' = 'active') {
|
||||
const current = { ...structuredClone(first), discussion: { ...structuredClone(ideas), status } };
|
||||
describe('conversation without discussion components', () => {
|
||||
const contents = [
|
||||
{ kind: 'ideas' as const, title: '旧想法板', items: [{ id: 'dog', text: '养只小狗', state: 'kept' as const }] },
|
||||
{ kind: 'structure' as const, title: '旧结构图', nodes: [{ id: 'dog', label: '照顾小狗' }] },
|
||||
{ kind: 'flow' as const, title: '旧流程图', nodes: [{ id: 'home', kind: 'event' as const, label: '回家' }], edges: [] },
|
||||
{ kind: 'comparison' as const, title: '旧对照表', columns: [{ id: 'a', label: '主动回应' }, { id: 'b', label: '点击回应' }], rows: [] },
|
||||
];
|
||||
const discussion = { id: 'tool', revision: 1, status: 'active' as const, content: contents[0] };
|
||||
|
||||
it.each(contents)('keeps $kind history readable without rendering a component or sending its context', async (content) => {
|
||||
const current = { ...structuredClone(first), discussion: { ...discussion, content },
|
||||
requests: [request({ response: '已有的普通回答', suggestedQuestions: ['继续聊聊?'] })] };
|
||||
api.read.mockResolvedValue(current);
|
||||
api.send.mockResolvedValue({ ...current, revision: 2 });
|
||||
render(<TeacherChatPanel projectId="p" sourceId="c" />);
|
||||
await screen.findByText('已有的普通回答');
|
||||
expect(screen.queryByTestId('teacher-discussion')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(content.title)).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '继续聊聊?' })).toBeVisible();
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '普通问题' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '提问', exact: true }));
|
||||
await waitFor(() => expect(api.send).toHaveBeenCalled());
|
||||
const input = api.send.mock.calls[0][2];
|
||||
expect(input.text).toBe('普通问题');
|
||||
expect(input).not.toHaveProperty('presentation');
|
||||
expect(input).not.toHaveProperty('discussion');
|
||||
expect(api.updateDiscussion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not restore offered components from live snapshots and still stops a reply without losing the draft', async () => {
|
||||
const current = { ...structuredClone(first), discussion: { ...discussion, status: 'offered' as const } };
|
||||
api.read.mockResolvedValue(current);
|
||||
render(<TeacherChatPanel projectId="p" sourceId="c" />);
|
||||
await screen.findByText('一起想我的宠物游戏');
|
||||
await waitFor(() => expect(streams.has('first')).toBe(true));
|
||||
return current;
|
||||
}
|
||||
it('quotes a focused item into the existing composer without sending, then sends the locked version', async () => {
|
||||
const current = await openDiscussion();
|
||||
api.send.mockImplementation(async (_base, _id, input) => ({ ...current, revision: 2, requests: [request({ text: input.text, response: '先聊聊小狗。' })] }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '养只小狗', exact: true }));
|
||||
expect(api.send).not.toHaveBeenCalled();
|
||||
expect(screen.getByText('接着聊:养只小狗')).toBeVisible();
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '我希望是柯基' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '提问', exact: true }));
|
||||
await waitFor(() => expect(api.send).toHaveBeenCalledWith('p/agent-topics', 'first', expect.objectContaining({ presentation: 'discussion-v1', discussion: { toolId: 'tool', revision: 1, focusId: 'dog' }, text: '我希望是柯基' })));
|
||||
expect(screen.getAllByTestId('teacher-discussion')).toHaveLength(1);
|
||||
});
|
||||
it('finishes provisionally, keeps the draft, chats without changing the tool, and resumes explicitly', async () => {
|
||||
const current = await openDiscussion();
|
||||
api.updateDiscussion.mockResolvedValueOnce({ ...current, revision: 2, discussion: { ...ideas, status: 'finished', revision: 2 } });
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '还在想的下一句' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '先这些', exact: true }));
|
||||
await screen.findByText('先留在这里');
|
||||
expect(screen.getByLabelText('向智能体提问')).toHaveValue('还在想的下一句');
|
||||
expect(screen.getByTestId('teacher-discussion')).toHaveAttribute('data-collapsed', 'true');
|
||||
api.send.mockImplementation(async (_base, _id, input) => ({ ...current, revision: 3, discussion: { ...ideas, status: 'finished', revision: 2 }, requests: [request({ text: input.text, response: '我们慢慢聊。' })] }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '提问', exact: true }));
|
||||
await screen.findByText('我们慢慢聊。');
|
||||
expect(api.send.mock.calls[0][2].discussion).toBeUndefined();
|
||||
api.updateDiscussion.mockResolvedValueOnce({ ...current, revision: 4, discussion: { ...ideas, revision: 3 } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '接着改', exact: true }));
|
||||
await screen.findByRole('button', { name: '养只小狗', exact: true });
|
||||
expect(api.updateDiscussion.mock.calls[1][2]).toEqual({ toolId: 'tool', revision: 2, action: 'resume' });
|
||||
});
|
||||
it('offers a tool before activating, and preserves a draft during explicit structure conversion', async () => {
|
||||
const current = await openDiscussion('offered');
|
||||
api.updateDiscussion.mockResolvedValue({ ...current, revision: 2, discussion: { ...ideas, revision: 2 } });
|
||||
expect(screen.getByRole('button', { name: '养只小狗', exact: true })).toBeDisabled();
|
||||
fireEvent.click(screen.getByRole('button', { name: '用这个一起想', exact: true }));
|
||||
await screen.findByRole('button', { name: '把想法理一理', exact: true });
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '不要丢掉的草稿' } });
|
||||
api.send.mockResolvedValue({ ...current, revision: 3, discussion: { ...ideas, revision: 2 }, requests: [] });
|
||||
fireEvent.click(screen.getByRole('button', { name: '把想法理一理', exact: true }));
|
||||
await waitFor(() => expect(api.send).toHaveBeenCalled());
|
||||
expect(api.send.mock.calls[0][2].discussion).toEqual({ toolId: 'tool', revision: 2, transition: 'structure' });
|
||||
expect(screen.getByLabelText('向智能体提问')).toHaveValue('不要丢掉的草稿');
|
||||
});
|
||||
it('uses the same pinned component for SSE updates and leaves it untouched while thinking or stopping', async () => {
|
||||
const current = await openDiscussion();
|
||||
const stream = streams.get('first')!;
|
||||
const updating = { ...current, revision: 2, requests: [request({ status: 'running', response: '', discussionContext: { toolId: 'tool', revision: 1 } })] };
|
||||
act(() => stream.dispatchEvent(new MessageEvent('snapshot', { data: JSON.stringify(updating) })));
|
||||
expect(screen.getByRole('status')).toHaveTextContent('正在梳理');
|
||||
expect(screen.getByRole('button', { name: '养只小狗', exact: true })).toBeVisible();
|
||||
await ready();
|
||||
const updating = { ...current, revision: 2, requests: [request({ status: 'running', discussionContext: { toolId: 'tool', revision: 1 } })] };
|
||||
act(() => streams.get('first')!.dispatchEvent(new MessageEvent('snapshot', { data: JSON.stringify(updating) })));
|
||||
expect(screen.getByRole('status')).toHaveTextContent('智能体正在想');
|
||||
expect(screen.queryByTestId('teacher-discussion')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: '用这个一起想' })).not.toBeInTheDocument();
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '下一轮草稿' } });
|
||||
api.cancel.mockResolvedValue({ ...updating, revision: 3, requests: [request({ status: 'cancelled' })] });
|
||||
fireEvent.click(screen.getByRole('button', { name: '停止回复' }));
|
||||
await screen.findByText('这次先停在这里。想继续时可以再说说。');
|
||||
expect(screen.getByLabelText('向智能体提问')).toHaveValue('下一轮草稿');
|
||||
act(() => stream.dispatchEvent(new MessageEvent('snapshot', { data: JSON.stringify({ ...current, revision: 4, discussion: { ...ideas, revision: 2, content: { ...ideas.content, title: '一起想小狗和花园' } } }) })));
|
||||
expect(screen.getAllByTestId('teacher-discussion')).toHaveLength(1);
|
||||
expect(screen.getByText('一起想小狗和花园')).toBeVisible();
|
||||
});
|
||||
|
||||
it('retries a saved pre-upgrade request verbatim instead of creating a second model call', async () => {
|
||||
const pending: TeacherSend = { requestId: 'old-uncertain-request', text: '结果还没确认的问题', references: [],
|
||||
sourceConversationId: 'c', presentation: 'discussion-v1', discussion: { toolId: 'tool', revision: 1 } };
|
||||
localStorage.setItem('makelore-consultation-draft:signed-out:p:teacher', JSON.stringify({ text: pending.text, references: [], pending }));
|
||||
api.read.mockResolvedValue({ ...structuredClone(first), discussion });
|
||||
api.send.mockResolvedValue({ ...structuredClone(first), revision: 2, requests: [request({ id: pending.requestId, response: '收到原来的回答' })] });
|
||||
render(<TeacherChatPanel projectId="p" sourceId="c" />);
|
||||
await ready();
|
||||
fireEvent.click(screen.getByRole('button', { name: '提问', exact: true }));
|
||||
await screen.findByText('收到原来的回答');
|
||||
expect(api.send).toHaveBeenCalledExactlyOnceWith('p/agent-topics', 'first', pending);
|
||||
expect(screen.queryByTestId('teacher-discussion')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sends a normal suggested question directly without changing the composer draft', async () => {
|
||||
const current = { ...first, requests: [request({ response: '先听听你的想法。', suggestedQuestions: ['怎样知道小狗喜欢我?'] })] };
|
||||
api.read.mockResolvedValue(current);
|
||||
@@ -653,23 +644,6 @@ describe('pinned discussion workspace', () => {
|
||||
});
|
||||
|
||||
|
||||
it('comparison focuses and directly sends the selected difference while preserving the existing draft', async () => {
|
||||
const current = { ...first, discussion: { id: 'compare', revision: 3, status: 'active' as const, content: {
|
||||
kind: 'comparison' as const, title: '它什么时候回应我?', columns: [{ id: 'a', label: '点击才回应' }, { id: 'b', label: '主动回应' }], rows: [{ id: 'home', label: '回到家时', cells: [{ columnId: 'a', text: '等我点击' }, { columnId: 'b', text: '跑来欢迎我' }] }],
|
||||
} } };
|
||||
api.read.mockResolvedValue(current);
|
||||
api.send.mockResolvedValue({ ...current, revision: 2 });
|
||||
render(<TeacherChatPanel projectId="p" sourceId="c" />);
|
||||
await screen.findByText('回到家时');
|
||||
fireEvent.change(screen.getByLabelText('向智能体提问'), { target: { value: '自己还没说完的话' } });
|
||||
fireEvent.click(screen.getAllByRole('button', { name: '聊聊这一点' })[1]);
|
||||
await waitFor(() => expect(api.send).toHaveBeenCalled());
|
||||
expect(api.send.mock.calls[0][2]).toMatchObject({ text: '聊聊「回到家时」时,「主动回应」这一点:跑来欢迎我', discussion: { toolId: 'compare', revision: 3, focusId: 'home:b' } });
|
||||
expect(screen.getByLabelText('向智能体提问')).toHaveValue('自己还没说完的话');
|
||||
expect(screen.getAllByTestId('teacher-discussion')).toHaveLength(1);
|
||||
});
|
||||
|
||||
|
||||
describe('merged cloud teacher classroom', () => {
|
||||
it('keeps an old unassigned project draft readable without copying it to the selected agent', async () => {
|
||||
const published = { ...definition, config_id: 'one' };
|
||||
@@ -710,7 +684,9 @@ describe('merged cloud teacher classroom', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '提问', exact: true }));
|
||||
await screen.findByText('先看看玩家会做什么。');
|
||||
expect(api.create).not.toHaveBeenCalled();
|
||||
expect(api.sendConversation).toHaveBeenCalledWith('p', 'algorithm', expect.objectContaining({ projectId: 'p', sourceConversationId: 'c', presentation: 'discussion-v1' }));
|
||||
expect(api.sendConversation).toHaveBeenCalledWith('p', 'algorithm', expect.objectContaining({ projectId: 'p', sourceConversationId: 'c' }));
|
||||
expect(api.sendConversation.mock.calls[0][2]).not.toHaveProperty('presentation');
|
||||
expect(api.sendConversation.mock.calls[0][2]).not.toHaveProperty('discussion');
|
||||
expect(screen.getByRole('complementary', { name: '算法智能体咨询' })).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user