feat(ui): introduce each teacher with an individual welcome bubble

This commit is contained in:
鲨鱼辣椒 committed 2026-09-29 14:02:50 +08:00
1 parent e4b99d5e32
commit c82b1a43be
9 files changed
+253 -22

No files matched your search

+16 -14
View File
@@ -2186,9 +2186,9 @@ test('teacher avatar consumes the welcome bubble through close, reopen and reloa
await page.getByTestId('ai-module-option-programming').click();
await page.evaluate(() => { window.location.hash = '/chat'; });
const teacherButton = page.getByRole('button', { name: '与代码智能体聊天', exact: true });
const invitation = page.getByTestId('teacher-invitation');
const invitation = page.locator('[data-testid="teacher-welcomes"] .teacher-welcome-card[data-agent-id="teacher-code"]');
const teacher = page.getByTestId('teacher-chat-panel');
await expect(invitation).toHaveAttribute('data-bubble-kind', 'welcome');
await expect(invitation).toBeVisible();
await teacherButton.click();
await expect(teacher).toBeVisible();
await expect(teacher.getByText('一起理解代码', { exact: true })).toBeVisible();
@@ -2205,6 +2205,7 @@ test('teacher avatar consumes the welcome bubble through close, reopen and reloa
await page.reload();
await expect(teacherButton).toBeVisible();
await expect(invitation).toHaveCount(0);
await expect(page.getByRole('button', { name: '收起朋友的介绍' })).toBeVisible();
expect((await readState(electronApp)).captured.filter(item => /\/(agent-topics|messages|teacher-check-in|prompt)$/.test(item.path) && item.method === 'POST')).toHaveLength(0);
} finally { await releaseSnapshot(electronApp); }
});
@@ -2229,9 +2230,9 @@ test('teacher stays in the top-right header without legacy check-ins or taking o
await expect(teacherButton.locator('img')).toHaveAttribute('src', /avatar-01/);
await expect(page.getByRole('checkbox', { name: '智能体偶尔来看看' })).toHaveCount(0);
await expect(page.getByTestId('teacher-companion')).toHaveCount(1);
const invitation = companion.getByTestId('teacher-invitation');
await expect(invitation).toHaveAttribute('data-bubble-kind', 'welcome');
await expect(invitation).toContainText('一起理解代码');
const invitation = companion.locator('.teacher-welcome-card[data-agent-id="teacher-code"]');
await expect(invitation).toBeVisible();
await expect(invitation).toContainText('一起发现代码里的小奥秘,把想法变成作品。');
await expect(invitation).toBeInViewport({ ratio: 1 });
expect((await readState(electronApp)).captured.filter((item) => item.path.endsWith('/teacher-check-in'))).toHaveLength(0);
await page.screenshot({ path: test.info().outputPath('teacher-entry-greeting.png') });
@@ -2261,27 +2262,28 @@ test('teacher stays in the top-right header without legacy check-ins or taking o
expect(introductionViewportBounds!.y).toBeGreaterThanOrEqual(introductionBounds!.y + introductionBounds!.height);
await page.keyboard.press('Escape');
await expect(introduction).toHaveCount(0);
await expect(invitation).toHaveAttribute('data-bubble-kind', 'welcome');
await expect(invitation).toBeVisible();
expect((await readState(electronApp)).captured.filter(item => /\/(messages|teacher-check-in)$/.test(item.path) && item.method === 'POST')).toHaveLength(0);
await page.getByRole('tab', { name: '操作对话', exact: true }).click();
await composer.focus();
await page.clock.fastForward(300_000);
await expect(invitation).toContainText('一起理解代码');
await expect(invitation).toHaveAttribute('data-bubble-kind', 'welcome');
await expect(invitation).toContainText('一起发现代码里的小奥秘,把想法变成作品。');
await expect(invitation).toBeVisible();
expect((await readState(electronApp)).captured.filter((item) => item.path.endsWith('/teacher-check-in'))).toHaveLength(0);
await expect(invitation).toBeInViewport({ ratio: 1 });
const presence = companion.getByTestId('teacher-presence');
await expect(presence.getByRole('button', { name: '打开这条智能体消息' })).toBeVisible();
await expect(presence.getByRole('button', { name: '打开这条智能体消息' }).locator('img')).toHaveAttribute('src', await teacherButton.locator('img').getAttribute('src') as string);
const presence = companion.getByTestId('teacher-welcomes');
await expect(presence.getByRole('button', { name: '认识代码智能体' })).toBeVisible();
await expect(presence.getByRole('button', { name: '认识代码智能体' }).locator('img')).toHaveAttribute('src', await teacherButton.locator('img').getAttribute('src') as string);
const [invitationBounds, companionBounds, avatarBounds, presenceBounds, speakerBounds] = await Promise.all([
invitation.boundingBox(), companion.boundingBox(), teacherButton.locator('img').boundingBox(), presence.boundingBox(), presence.getByRole('button', { name: '打开这条智能体消息' }).boundingBox(),
invitation.boundingBox(), companion.boundingBox(), teacherButton.locator('img').boundingBox(), presence.boundingBox(), presence.getByRole('button', { name: '认识代码智能体' }).boundingBox(),
]);
expect(invitationBounds).not.toBeNull();
expect(companionBounds).not.toBeNull();
expect(avatarBounds).not.toBeNull();
expect(invitationBounds!.y).toBeGreaterThanOrEqual(avatarBounds!.y + avatarBounds!.height);
expect(Math.abs(presenceBounds!.x + presenceBounds!.width - companionBounds!.x - companionBounds!.width)).toBeLessThanOrEqual(2);
expect(speakerBounds!.x - invitationBounds!.x - invitationBounds!.width).toBeLessThanOrEqual(10);
expect(speakerBounds!.x).toBeGreaterThanOrEqual(presenceBounds!.x);
expect(speakerBounds!.x + speakerBounds!.width).toBeLessThan(invitationBounds!.x + invitationBounds!.width);
await expect(composer).toBeFocused();
await expect(composer).toHaveValue('我还在写自己的想法');
await expect(page.getByRole('tab', { name: '操作对话', exact: true })).toHaveAttribute('aria-selected', 'true');
@@ -2303,7 +2305,7 @@ test('teacher stays in the top-right header without legacy check-ins or taking o
await expect(composer).toHaveValue('我还在写自己的想法');
await page.clock.fastForward(300_000);
expect((await readState(electronApp)).captured.filter((item) => item.path.endsWith('/teacher-check-in'))).toHaveLength(0);
await invitation.getByRole('button', { name: '和智能体聊聊' }).click();
await invitation.getByRole('button', { name: '和代码智能体聊聊' }).click();
await expect(invitation).toHaveCount(0);
await expect(presence).toHaveCount(0);
await expect(teacherButton).toBeVisible();