fix: show teacher introductions as standalone clickable bubbles
This commit is contained in:
@@ -65,7 +65,7 @@ for (const width of [375, 768, 1280, 1470]) {
|
||||
await page.getByTestId('community-invitation').scrollIntoViewIfNeeded();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
|
||||
await expect(page.getByRole('button', { name: '去作品广场逛逛' })).toBeVisible();
|
||||
await teachers.getByRole('button', { name: '先自己试试' }).click();
|
||||
while (await teachers.count()) await teachers.getByRole('button', { name: /^收起.*的介绍$/ }).first().click();
|
||||
await heading.scrollIntoViewIfNeeded();
|
||||
const composer = page.getByTestId('coding-message-composer');
|
||||
await expect(composer).toBeInViewport({ ratio: 1 });
|
||||
@@ -125,7 +125,7 @@ for (const width of [1280, 1470]) {
|
||||
}).toBeLessThan(2);
|
||||
const inputBeforeDismiss = await composer.boundingBox();
|
||||
const headingBeforeDismiss = await heading.boundingBox();
|
||||
await teachers.getByRole('button', { name: '先自己试试' }).click();
|
||||
while (await teachers.count()) await teachers.getByRole('button', { name: /^收起.*的介绍$/ }).first().click();
|
||||
await expect(composer.getByRole('textbox')).toHaveValue('保留这份草稿');
|
||||
expect(await composer.boundingBox()).toEqual(inputBeforeDismiss);
|
||||
expect(await heading.boundingBox()).toEqual(headingBeforeDismiss);
|
||||
|
||||
@@ -25,7 +25,6 @@ export function Layout() {
|
||||
welcomes: new URLSearchParams(location.search).has('teachers') ? agents.flatMap((agent, index) => dismissed.includes(agent.teacher_id)
|
||||
? [] : [{ agentId: agent.teacher_id, definition: agent.definition, text: agent.definition.description, tone: index }]) : [],
|
||||
dismissWelcome: (id: string) => setDismissed(values => [...values, id]),
|
||||
dismissWelcomes: () => setDismissed(['0', '1']),
|
||||
} as ComponentProps<typeof TeacherCompanion>['companion'];
|
||||
return <div className="flex h-screen">
|
||||
{new URLSearchParams(location.search).has('sidebar') && <aside data-testid="project-sidebar" className="shrink-0 border-r p-4 text-sm" style={{ width: 210, background: '#f1f6fc' }}>我的项目</aside>}
|
||||
|
||||
@@ -33,7 +33,6 @@ export function Layout() {
|
||||
welcome: null, enabled: true, loading: false, checking: false, error: null,
|
||||
welcomes: welcomeMode && !open ? agents.flatMap((agent, index) => dismissed.includes(agent.teacher_id) ? [] : [{ agentId: agent.teacher_id, definition: agent.definition, text: agent.definition.description, tone: index % 3 }]) : [],
|
||||
dismissWelcome: (id: string) => setDismissed(ids => [...new Set([...ids, id])]),
|
||||
dismissWelcomes: () => setDismissed(agents.map(agent => agent.teacher_id)),
|
||||
refreshing, refreshStatus,
|
||||
refresh: async () => {
|
||||
setRefreshing(true);
|
||||
|
||||
@@ -2221,6 +2221,9 @@ test('teacher introductions collapse after three operation replies and remain av
|
||||
await page.evaluate(() => { window.location.hash = '/chat'; });
|
||||
const introductions = page.getByTestId('teacher-welcomes');
|
||||
await expect(introductions.locator('.teacher-welcome-card')).toHaveCount(2);
|
||||
await expect(introductions).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
await expect(introductions.getByText('来认识一下')).toHaveCount(0);
|
||||
await expect(introductions.getByRole('button', { name: '先自己试试' })).toHaveCount(0);
|
||||
const startedAt = await page.evaluate(() => Date.now());
|
||||
for (let round = 1; round <= 3; round++) {
|
||||
for (const status of ['running', 'idle'] as const) {
|
||||
|
||||
@@ -41,6 +41,10 @@ for (const width of [375, 768, 1280]) {
|
||||
await page.goto(baseUrl + '?agents=3&welcomes');
|
||||
const group = page.getByTestId('teacher-welcomes');
|
||||
await expect(group.locator('.teacher-welcome-card')).toHaveCount(3);
|
||||
await expect(group).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
await expect(group).toHaveCSS('box-shadow', 'none');
|
||||
await expect(group.getByText('来认识一下')).toHaveCount(0);
|
||||
await expect(group.getByRole('button', { name: '先自己试试' })).toHaveCount(0);
|
||||
await expect(group).toBeInViewport({ ratio: 1 });
|
||||
for (const card of await group.locator('.teacher-welcome-card').all()) {
|
||||
await expect(card).toBeInViewport({ ratio: 1 });
|
||||
@@ -53,12 +57,13 @@ for (const width of [375, 768, 1280]) {
|
||||
await page.screenshot({ path: test.info().outputPath(`teacher-introductions-${width}.png`) });
|
||||
await group.getByRole('button', { name: '收起智能体2的介绍' }).click();
|
||||
await expect(group.locator('.teacher-welcome-card')).toHaveCount(2);
|
||||
await group.getByRole('button', { name: '和智能体3聊聊' }).click();
|
||||
await expect(page.locator('#coding-consultation-dock')).toHaveCount(0);
|
||||
await group.getByRole('button', { name: '和智能体3聊聊' }).click({ position: { x: 8, y: 8 } });
|
||||
await expect(page.getByRole('button', { name: '与智能体3聊天' })).toHaveAttribute('aria-pressed', 'true');
|
||||
await expect(group).toHaveCount(0);
|
||||
await page.getByRole('button', { name: '关闭智能体栏' }).click();
|
||||
await expect(group.locator('.teacher-welcome-card')).toHaveCount(1);
|
||||
await group.getByRole('button', { name: '先自己试试' }).click();
|
||||
while (await group.count()) await group.getByRole('button', { name: /^收起.*的介绍$/ }).first().click();
|
||||
await expect(group).toHaveCount(0);
|
||||
});
|
||||
}
|
||||
@@ -75,7 +80,12 @@ test('many teacher introductions remain scrollable and honor reduced motion', as
|
||||
await last.scrollIntoViewIfNeeded();
|
||||
await expect(last).toBeInViewport({ ratio: 1 });
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(520);
|
||||
await group.getByRole('button', { name: '先自己试试' }).click();
|
||||
await last.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(page.getByRole('button', { name: '与智能体8聊天' })).toHaveAttribute('aria-pressed', 'true');
|
||||
await page.getByRole('button', { name: '关闭智能体栏' }).click();
|
||||
await expect(group.locator('.teacher-welcome-card')).toHaveCount(7);
|
||||
while (await group.count()) await group.getByRole('button', { name: /^收起.*的介绍$/ }).first().click();
|
||||
await expect(group).toHaveCount(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -193,10 +193,13 @@ describe('individual teacher introductions', () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
const group = screen.getByTestId('teacher-welcomes');
|
||||
expect(within(group).queryByText('来认识一下')).not.toBeInTheDocument();
|
||||
expect(within(group).queryByRole('button', { name: '先自己试试' })).not.toBeInTheDocument();
|
||||
for (const agent of agents) expect(within(group).getByText(agent.definition.description)).toBeVisible();
|
||||
expect(screen.queryByTestId('teacher-invitation')).not.toBeInTheDocument();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '收起体验老师的介绍' })));
|
||||
expect(within(group).queryByText('体验老师的自我介绍')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-chat-panel')).not.toBeInTheDocument();
|
||||
expect(within(group).getByText('方法老师的自我介绍')).toBeVisible();
|
||||
expect(within(group).getByText('故事老师的自我介绍')).toBeVisible();
|
||||
await advance();
|
||||
@@ -217,7 +220,7 @@ describe('individual teacher introductions', () => {
|
||||
expect(screen.getByRole('button', { name: '收起体验老师的介绍' })).toBeVisible();
|
||||
await act(async () => next.rerender(<Harness projectId="other" />));
|
||||
expect(screen.getByRole('button', { name: '收起方法老师的介绍' })).toBeVisible();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '先自己试试' })));
|
||||
for (const agent of agents) await act(async () => fireEvent.click(screen.getByRole('button', { name: `收起${agent.definition.name}的介绍` })));
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
await act(async () => next.rerender(<Harness />));
|
||||
expect(screen.queryByRole('button', { name: '收起方法老师的介绍' })).not.toBeInTheDocument();
|
||||
@@ -230,7 +233,7 @@ describe('individual teacher introductions', () => {
|
||||
it('opens the clicked teacher and consumes only their introduction, preserving the others for later', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '和故事老师聊聊' })));
|
||||
await act(async () => fireEvent.click(within(screen.getByTestId('teacher-welcomes')).getByText('故事老师的自我介绍')));
|
||||
expect(screen.getByRole('button', { name: '与故事老师聊天' })).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(screen.getByTestId('teacher-chat-panel')).toHaveTextContent('故事老师的聊天欢迎语');
|
||||
expect(screen.getByTestId('teacher-chat-panel')).not.toHaveTextContent('故事老师的自我介绍');
|
||||
@@ -243,11 +246,11 @@ describe('individual teacher introductions', () => {
|
||||
expect(api.sendConversation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('introduces only newly delivered teachers after dismiss-all, without resetting seen replies or existing receipts on refresh', async () => {
|
||||
it('introduces only newly delivered teachers after individually dismissing every introduction, without resetting seen replies or existing receipts on refresh', async () => {
|
||||
localStorage.setItem('makelore-teacher-presence:kid:seen', JSON.stringify(['keep-seen']));
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '先自己试试' })));
|
||||
for (const agent of agents) await act(async () => fireEvent.click(screen.getByRole('button', { name: `收起${agent.definition.name}的介绍` })));
|
||||
const added = { ...agents[0], teacher_id: 'new', definition: { ...agents[0].definition, config_id: 'new', name: '新老师', description: '', welcome_message: '新老师的云端欢迎语' } };
|
||||
api.catalog.mockResolvedValue({ items: [...agents.map(agent => ({ ...agent, version: 2 })), added] });
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '刷新智能体' })));
|
||||
|
||||
Reference in New Issue
Block a user