feat: integrate learning module
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-16 20:52:16 +08:00
parent 26b52d76e3
commit 01bee3188b
107 changed files with 6318 additions and 12063 deletions

View File

@@ -88,6 +88,7 @@ test.describe('AI Design workspace', () => {
await expect(page.getByTestId('image-workspace-message').first()).toHaveClass(/w-full/);
const firstQuote = page.locator('[data-testid^="design-quote-"]').first();
await expect(firstQuote.getByRole('button', { name: '继续调整' })).toBeVisible();
await expect(firstQuote.getByTestId('design-quote-model')).toHaveCount(0);
await expect(firstQuote).not.toContainText('提示词模式');
await page.getByRole('button', { name: '确认并开始生成' }).click();
await expect(page.getByRole('button', { name: '确认生成' })).toHaveCount(0);
@@ -119,6 +120,7 @@ test.describe('AI Design workspace', () => {
await expect(page.getByTestId('image-workspace-conversation')).toContainText(secondPrompt);
await expect(page.getByTestId('design-quote-final-prompt')).toHaveValue(secondPrompt);
await page.getByTestId('design-quote-final-prompt').fill('用户编辑后的动态视频完整提示词');
await expect(page.getByTestId('design-quote-model')).toHaveCount(0);
await page.getByTestId('design-quote-resolution').selectOption('720P');
await page.getByTestId('design-quote-aspect-ratio').selectOption('9:16');
await page.getByTestId('design-quote-duration').selectOption('4');

View File

@@ -13,6 +13,7 @@ async function installFirstChatHost(electronApp: ElectronApplication): Promise<v
type MainState = {
captured: CapturedRequest[];
promptPosted: boolean;
abortPosted: boolean;
releaseInitialHistory: (() => void) | null;
};
const mainGlobal = globalThis as typeof globalThis & {
@@ -21,6 +22,7 @@ async function installFirstChatHost(electronApp: ElectronApplication): Promise<v
const state: MainState = {
captured: [],
promptPosted: false,
abortPosted: false,
releaseInitialHistory: null,
};
mainGlobal.__makeloreFirstChatE2EState = state;
@@ -169,7 +171,7 @@ async function installFirstChatHost(electronApp: ElectronApplication): Promise<v
if (path === '/api/opencode/sessions/status') {
return respond({
statuses: {
[session.id]: { type: state.promptPosted ? 'idle' : 'busy' },
[session.id]: { type: state.promptPosted && !state.abortPosted ? 'busy' : 'idle' },
},
});
}
@@ -183,6 +185,10 @@ async function installFirstChatHost(electronApp: ElectronApplication): Promise<v
state.promptPosted = true;
return respond({ success: true }, 202);
}
if (path === `/api/opencode/sessions/${session.id}/abort` && method === 'POST') {
state.abortPosted = true;
return respond({ success: true });
}
if (path.endsWith('/todos')) return respond({ todos: [] });
if (path.endsWith('/diff')) return respond({ diffs: [] });
if (path === '/api/opencode/questions') return respond({ questions: [] });
@@ -249,6 +255,15 @@ test('submits the first prompt without waiting for the known-empty session histo
));
return { historyPending, promptPosted };
}).toEqual({ historyPending: true, promptPosted: true });
await expect(page.getByTestId('assistant-waiting-placeholder')).toHaveCount(0);
const stopButton = page.getByRole('button', { name: '停止当前对话' });
await expect(stopButton).toBeEnabled();
await expect(stopButton.getByTestId('opencode-agent-response-loader')).toBeVisible();
await stopButton.click();
await expect.poll(async () => (await capturedRequests(electronApp)).map(
(request) => `${request.method} ${request.path}`,
)).toContain('POST /api/opencode/sessions/ses_first_chat_e2e/abort');
} finally {
await releaseInitialHistory(electronApp);
}

View File

@@ -484,13 +484,27 @@ async function configureSlashCompactionHost(
role: 'assistant',
time: { created: 1_753_190_401_000 },
},
parts: [{
id: 'part_slash_e2e_assistant',
sessionID: 'ses_slash_e2e',
messageID: 'msg_slash_e2e_assistant',
type: 'text',
text: '已有的助手回复',
}],
parts: [
{
id: 'part_slash_e2e_assistant',
sessionID: 'ses_slash_e2e',
messageID: 'msg_slash_e2e_assistant',
type: 'text',
text: '已有的助手回复,我先确认上下文,再继续处理。',
},
{
id: 'part_slash_e2e_tool',
sessionID: 'ses_slash_e2e',
messageID: 'msg_slash_e2e_assistant',
type: 'tool',
tool: 'read_file',
callID: 'call_slash_e2e_read',
state: {
status: 'completed',
input: { filePath: 'README.md' },
},
},
],
},
];
});
@@ -670,6 +684,11 @@ test.describe('OpenCode slash commands', () => {
'[data-chat-message-id="msg_slash_e2e_assistant"]',
);
await expect(existingAssistant).toBeVisible();
await expect(existingAssistant.getByTestId('chat-message-bubble-assistant'))
.toContainText('已有的助手回复,我先确认上下文,再继续处理。');
await expect(existingAssistant.getByTestId('chat-message-avatar-assistant')).toBeVisible();
await expect(transcript.getByTestId('chat-execution-graph')).toBeVisible();
await expect(transcript.getByTestId('chat-execution-step')).toHaveCount(1);
const composer = page.getByRole('textbox');
await expect(composer).toBeVisible();
await composer.fill('/compact');

View File

@@ -12,13 +12,9 @@ test.describe('Project configuration skills', () => {
try {
const userDataDir = await app.evaluate(({ app: electronApp }) => electronApp.getPath('userData'));
const skillDir = path.join(userDataDir, 'opencode', 'niancode-config', 'skills', 'agent-browser');
const gameEngineSkillDir = path.join(userDataDir, 'opencode', 'niancode-config', 'skills', 'game-engine');
await mkdir(path.join(skillDir, 'references'), { recursive: true });
await writeFile(path.join(skillDir, 'SKILL.md'), '---\nname: agent-browser\ndescription: E2E browser debugging skill.\n---\n\n# Browser debugging\n');
await writeFile(path.join(skillDir, 'references', 'guide.md'), '# Reference guide\n');
await mkdir(path.join(gameEngineSkillDir, 'assets'), { recursive: true });
await writeFile(path.join(gameEngineSkillDir, 'SKILL.md'), '---\nname: game-engine\ndescription: E2E game engine skill.\n---\n\n# Game Engine Skill\n\n## Makelore 使用边界\n');
await writeFile(path.join(gameEngineSkillDir, 'assets', '2d-platform-game.md'), '# 2D Platform Game Template\n');
await app.evaluate(({ ipcMain }, selectedPath) => {
ipcMain.removeHandler('dialog:open');
@@ -118,11 +114,7 @@ test.describe('Project configuration skills', () => {
await expect(page.getByTestId('skill-main-file')).toContainText('# Browser debugging');
await page.getByRole('button', { name: '返回技能列表' }).locator('svg').click();
await expect(page.getByTestId('skill-card-agent-browser')).toBeVisible();
await expect(page.getByTestId('skill-card-game-engine')).toBeVisible();
await page.getByTestId('skill-card-game-engine').click();
await expect(page.getByTestId('skill-structure')).toContainText('assets/2d-platform-game.md');
await expect(page.getByTestId('skill-main-file')).toContainText('Makelore 使用边界');
await page.getByRole('button', { name: '返回技能列表' }).locator('svg').click();
await expect(page.getByTestId('skill-card-game-engine')).toHaveCount(0);
await page.getByRole('button', { name: '关闭' }).click();
await expect(page.getByTestId('resource-card-publish')).toHaveCount(0);
await expect(page.getByRole('button', { name: '一键提交审核' })).toBeVisible();