Merge project conversations and cloud coding teacher into main
This commit is contained in:
@@ -446,6 +446,8 @@ async function installCodingFirstChatHost(
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const teacherDefinition = {schema_version:1,teacher_id:'coding-teacher',name:'代码老师',description:'',avatar_id:'avatar-01',welcome_message:'一起理解代码',suggested_questions:['解释当前代码'],system_prompt:'教学',skills:[],model:{model_id:'model-a',reasoning_choice:{mode:'default'}},limits:{max_input_tokens:8000,max_output_tokens:1500}};
|
||||
let teacherTopic: Record<string, unknown> | null = null;
|
||||
ipcMain.removeHandler('hostapi:fetch');
|
||||
ipcMain.handle('hostapi:fetch', async (
|
||||
_event,
|
||||
@@ -518,6 +520,17 @@ async function installCodingFirstChatHost(
|
||||
};
|
||||
}
|
||||
|
||||
if (path === '/api/coding/teacher/config') return respond({enabled:true,revision:1,published_version:1,definition:teacherDefinition});
|
||||
if (path.endsWith('/teacher-topics') && method === 'GET') return respond({items:teacherTopic?[{id:teacherTopic.id,title:'老师话题'}]:[],lastSelectedTopicId:teacherTopic?.id??null});
|
||||
if (path.endsWith('/teacher-topics') && method === 'POST') {
|
||||
teacherTopic={schemaVersion:1,revision:0,id:'teacher-topic',accountId:'e2e',projectId:project.id,sourceConversationId:conversation.id,definition:teacherDefinition,version:1,createdAt:now,updatedAt:now,requests:[]};
|
||||
return respond(teacherTopic,201);
|
||||
}
|
||||
if (path.endsWith('/teacher-topics/teacher-topic/messages') && method === 'POST') {
|
||||
teacherTopic={...teacherTopic,revision:2,requests:[{id:body!.requestId,text:body!.text,references:body!.references??[],createdAt:now,sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,status:'completed',response:'先理解状态如何随点击变化,再修改代码。'}]};
|
||||
return respond(teacherTopic,202);
|
||||
}
|
||||
if (path.endsWith('/teacher-topics/teacher-topic')) return respond(teacherTopic);
|
||||
if (path === '/api/coding/projects') {
|
||||
return respond({ projects: [project], activeProjectId: project.id });
|
||||
}
|
||||
@@ -867,7 +880,7 @@ test('conversation menus rename, archive and restore without selecting or stoppi
|
||||
page = await getStableWindow(electronApp);
|
||||
await page.getByTestId('ai-module-option-programming').click();
|
||||
await page.evaluate(() => { window.location.hash = '/chat'; });
|
||||
const sidebar = page.getByTestId('coding-conversation-sidebar');
|
||||
const sidebar = page.getByTestId('project-conversations');
|
||||
const header = page.getByTestId('coding-conversation-header');
|
||||
await expect(header).toContainText('新对话');
|
||||
await sidebar.getByRole('button', { name: '对话操作:Second Conversation' }).focus();
|
||||
@@ -883,13 +896,13 @@ test('conversation menus rename, archive and restore without selecting or stoppi
|
||||
await page.keyboard.press('Enter');
|
||||
await page.getByRole('menuitem', { name: '归档', exact: true }).click();
|
||||
await expect(sidebar.getByRole('button', { name: '重命名后的会话', exact: true })).toHaveCount(0);
|
||||
await sidebar.getByRole('button', { name: '已归档(1)' }).click();
|
||||
await sidebar.getByRole('button', { name: '查看归档对话' }).click();
|
||||
await sidebar.getByRole('button', { name: '重命名后的会话', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: '恢复对话', exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '发送', exact: true })).toHaveCount(0);
|
||||
await page.getByRole('button', { name: '恢复对话', exact: true }).click();
|
||||
await expect(header).toContainText('重命名后的会话');
|
||||
await expect(sidebar.getByText('最近对话', { exact: true })).toBeVisible();
|
||||
await expect(sidebar.getByRole('button', { name: '查看归档对话' })).toBeVisible();
|
||||
const requests = (await readState(electronApp)).captured;
|
||||
expect(requests.some((request) => request.path.endsWith('/abort'))).toBe(false);
|
||||
expect(requests.filter((request) => request.method === 'PATCH' && request.path.endsWith('/conversation-pi-second'))
|
||||
@@ -985,11 +998,11 @@ test('first PI Conversation is editable under 500 ms and submits before runtime
|
||||
performance.mark('pi-first-chat-start');
|
||||
window.location.hash = '/chat';
|
||||
});
|
||||
const builderButton = page.getByRole('button', { name: 'Builder', exact: true });
|
||||
await expect(builderButton).toHaveAttribute('aria-expanded', 'true');
|
||||
const builderConversations = page.getByRole('group', { name: 'Builder 的对话' });
|
||||
await expect(page.getByRole('button', { name: 'Builder', exact: true })).toHaveCount(0);
|
||||
const builderConversations = page.getByRole('group', { name: '项目会话' });
|
||||
await expect(builderConversations).toBeVisible();
|
||||
await expect(builderConversations.getByRole('button', { name: '新对话', exact: true })).toBeVisible();
|
||||
await expect(builderConversations.getByRole('button', { name: '新对话', exact: true })).toHaveCount(0);
|
||||
expect((await readState(electronApp)).captured.filter(item => item.path === '/api/coding/projects/conversations' && item.method === 'POST')).toHaveLength(0);
|
||||
const composer = page.getByRole('textbox');
|
||||
await expect(composer).toBeEnabled();
|
||||
const editableMs = await page.evaluate(() => (
|
||||
@@ -1090,7 +1103,7 @@ test('hidden Conversation badge ignores process failures until interaction or ta
|
||||
await expect(page.getByTestId('main-layout')).toBeVisible();
|
||||
await page.evaluate(() => { window.location.hash = '/chat'; });
|
||||
|
||||
const conversations = page.getByRole('group', { name: 'Builder 的对话' });
|
||||
const conversations = page.getByRole('group', { name: '项目会话' });
|
||||
const firstConversation = conversations.getByRole('button', { name: '新对话', exact: true });
|
||||
const secondConversation = conversations.getByRole('button', { name: 'Second Conversation' });
|
||||
const secondUnreadBadge = secondConversation.locator('[aria-label="未读"]');
|
||||
@@ -1420,7 +1433,7 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
|
||||
await page.getByRole('button', { name: '关闭开发浏览器' }).last().click();
|
||||
await expect(page.getByTestId('agent-browser-panel')).toHaveCount(0);
|
||||
await expect(page.getByRole('button', { name: '项目设置' })).toHaveCount(1);
|
||||
await expect(page.getByTestId('coding-conversation-sidebar')).toBeVisible();
|
||||
await expect(page.getByTestId('project-conversations')).toBeVisible();
|
||||
const activeProcess = page.getByTestId('coding-process-group');
|
||||
await expect(activeProcess).toHaveAttribute('open', '');
|
||||
await expect(activeProcess.locator('summary').first()).toContainText('处理中');
|
||||
@@ -1551,7 +1564,7 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
|
||||
&& request.body?.text === '继续'
|
||||
))).toBe(true);
|
||||
|
||||
const builderConversations = page.getByRole('group', { name: 'Builder 的对话' });
|
||||
const builderConversations = page.getByRole('group', { name: '项目会话' });
|
||||
await expect(builderConversations).toBeVisible();
|
||||
const forkActions = page.getByRole('button', { name: '从这里创建新对话分支' });
|
||||
await expect(forkActions).toHaveCount(1);
|
||||
@@ -1693,3 +1706,33 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
|
||||
await releaseSnapshot(electronApp);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('project teacher side chat returns advice to the coding draft without submitting it', async ({launchElectronApp}) => {
|
||||
const electronApp=await launchElectronApp({skipSetup:true});let page=await getStableWindow(electronApp);
|
||||
const connection=await page.evaluate(async()=>({token:await window.electron.ipcRenderer.invoke('hostapi:token') as string,baseUrl:await window.electron.ipcRenderer.invoke('hostapi:base-url') as string}));
|
||||
await installCodingFirstChatHost(electronApp,connection,true);await settleSnapshot(electronApp);await disableCodingEventSource(page);
|
||||
try {
|
||||
await page.reload();page=await getStableWindow(electronApp);
|
||||
await page.getByTestId('ai-module-option-programming').click();await page.evaluate(()=>{window.location.hash='/chat';});
|
||||
await expect(page.getByTestId('project-conversations')).toBeVisible();
|
||||
await expect(page.getByTestId('coding-conversation-sidebar')).toHaveCount(0);
|
||||
const composer=page.getByTestId('coding-message-composer').getByRole('textbox');
|
||||
await composer.fill('保留我的草稿');
|
||||
await page.getByRole('button',{name:'问老师',exact:true}).click();
|
||||
const teacher=page.getByTestId('teacher-chat-panel');
|
||||
await expect(teacher.getByText('一起理解代码')).toBeVisible();
|
||||
await teacher.getByRole('textbox',{name:'向老师提问'}).fill('帮我理解当前代码');
|
||||
await teacher.getByRole('button',{name:'提问',exact:true}).click();
|
||||
await expect(teacher.getByText('先理解状态如何随点击变化,再修改代码。')).toBeVisible();
|
||||
await teacher.getByRole('button',{name:'带回主会话草稿'}).click();
|
||||
await expect(composer).toHaveValue('保留我的草稿\n\n先理解状态如何随点击变化,再修改代码。');
|
||||
const requests=(await readState(electronApp)).captured;
|
||||
expect(requests.filter(item=>item.path.endsWith('/prompt')&&item.method==='POST')).toHaveLength(0);
|
||||
expect(requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST')).toHaveLength(1);
|
||||
await page.screenshot({path:test.info().outputPath('project-teacher-side-chat.png')});
|
||||
await teacher.getByRole('button',{name:'关闭老师'}).click();await expect(teacher).toHaveCount(0);
|
||||
await page.getByRole('button',{name:'问老师',exact:true}).click();
|
||||
await expect(page.getByText('先理解状态如何随点击变化,再修改代码。',{exact:true})).toBeVisible();
|
||||
} finally {await releaseSnapshot(electronApp);}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user