fix: show and dismiss teacher introductions as one group
This commit is contained in:
@@ -59,7 +59,7 @@ beforeEach(() => {
|
||||
});
|
||||
afterEach(() => { cleanup(); vi.useRealTimers(); vi.unstubAllGlobals(); });
|
||||
|
||||
describe('individual teacher introductions', () => {
|
||||
describe('grouped teacher introductions', () => {
|
||||
const agents = ['方法老师', '体验老师', '故事老师'].map((name, index) => ({
|
||||
teacher_id: `intro-${index}`, version: 1, is_default: index === 0,
|
||||
definition: { ...definition, config_id: `intro-${index}`, name, description: `${name}的自我介绍`, welcome_message: `${name}的聊天欢迎语` },
|
||||
@@ -133,7 +133,7 @@ describe('individual teacher introductions', () => {
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps round windows independent for projects/accounts and newly delivered teachers', async () => {
|
||||
it('keeps project/account windows independent without reopening for newly delivered teachers', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
const view = await mount();
|
||||
await operation('one');
|
||||
@@ -161,15 +161,10 @@ describe('individual teacher introductions', () => {
|
||||
const added = { ...agents[0], teacher_id: 'new', definition: { ...agents[0].definition, config_id: 'new', name: '新老师' } };
|
||||
api.catalog.mockResolvedValue({ items: [...agents, added] });
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '刷新智能体' })));
|
||||
expect(screen.getByTestId('teacher-welcomes').querySelectorAll('.teacher-welcome-card')).toHaveLength(1);
|
||||
await operation('new-one');
|
||||
await operation('new-two');
|
||||
expect(screen.getByRole('button', { name: '收起新老师的介绍' })).toBeVisible();
|
||||
await operation('new-three');
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not count teacher turns and still collapses the other teachers while consultation is open', async () => {
|
||||
it('keeps the entire group dismissed after teacher turns and closing consultation', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
const topic = { ...checkInTopic(), definition: agents[0].definition, requests: [],
|
||||
conversation: { agentId: agents[0].teacher_id, segmentTurns: 0, discussions: {} } };
|
||||
@@ -181,7 +176,7 @@ describe('individual teacher introductions', () => {
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '提问', exact: true })));
|
||||
}
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与方法老师聊天' })));
|
||||
expect(screen.getByTestId('teacher-welcomes').querySelectorAll('.teacher-welcome-card')).toHaveLength(2);
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与方法老师聊天' })));
|
||||
await operation('one'); await operation('two'); await operation('three');
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与方法老师聊天' })));
|
||||
@@ -198,11 +193,9 @@ describe('individual teacher introductions', () => {
|
||||
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();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '收起全部老师介绍(体验老师)' })));
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-chat-panel')).not.toBeInTheDocument();
|
||||
expect(within(group).getByText('方法老师的自我介绍')).toBeVisible();
|
||||
expect(within(group).getByText('故事老师的自我介绍')).toBeVisible();
|
||||
await advance();
|
||||
expect(api.create).not.toHaveBeenCalled();
|
||||
expect(api.send).not.toHaveBeenCalled();
|
||||
@@ -214,24 +207,24 @@ describe('individual teacher introductions', () => {
|
||||
it('keeps dismissal across reloads for this account/project while leaving other projects and accounts independent', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
const first = await mount();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '收起方法老师的介绍' })));
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '收起全部老师介绍(方法老师)' })));
|
||||
first.unmount();
|
||||
const next = await mount();
|
||||
expect(screen.queryByRole('button', { name: '收起方法老师的介绍' })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '收起体验老师的介绍' })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: '收起全部老师介绍(方法老师)' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
await act(async () => next.rerender(<Harness projectId="other" />));
|
||||
expect(screen.getByRole('button', { name: '收起方法老师的介绍' })).toBeVisible();
|
||||
for (const agent of agents) await act(async () => fireEvent.click(screen.getByRole('button', { name: `收起${agent.definition.name}的介绍` })));
|
||||
expect(screen.getByRole('button', { name: '收起全部老师介绍(方法老师)' })).toBeVisible();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '收起全部老师介绍(方法老师)' })));
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
await act(async () => next.rerender(<Harness />));
|
||||
expect(screen.queryByRole('button', { name: '收起方法老师的介绍' })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '收起体验老师的介绍' })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: '收起全部老师介绍(方法老师)' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
identity.id = 'another-kid';
|
||||
await act(async () => next.rerender(<Harness />));
|
||||
expect(screen.getByRole('button', { name: '收起方法老师的介绍' })).toBeVisible();
|
||||
expect(screen.getByRole('button', { name: '收起全部老师介绍(方法老师)' })).toBeVisible();
|
||||
});
|
||||
|
||||
it('opens the clicked teacher and consumes only their introduction, preserving the others for later', async () => {
|
||||
it('opens the clicked teacher and consumes the whole group', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
await act(async () => fireEvent.click(within(screen.getByTestId('teacher-welcomes')).getByText('故事老师的自我介绍')));
|
||||
@@ -240,27 +233,74 @@ describe('individual teacher introductions', () => {
|
||||
expect(screen.getByTestId('teacher-chat-panel')).not.toHaveTextContent('故事老师的自我介绍');
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与故事老师聊天' })));
|
||||
expect(screen.queryByRole('button', { name: '收起故事老师的介绍' })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '收起方法老师的介绍' })).toBeVisible();
|
||||
expect(screen.getByRole('button', { name: '收起体验老师的介绍' })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: '收起全部老师介绍(故事老师)' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
expect(api.create).not.toHaveBeenCalled();
|
||||
expect(api.sendConversation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('introduces only newly delivered teachers after individually dismissing every introduction, without resetting seen replies or existing receipts on refresh', async () => {
|
||||
it('keeps refreshed and newly delivered teachers dismissed without resetting seen replies', async () => {
|
||||
localStorage.setItem('makelore-teacher-presence:kid:seen', JSON.stringify(['keep-seen']));
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
for (const agent of agents) await act(async () => fireEvent.click(screen.getByRole('button', { name: `收起${agent.definition.name}的介绍` })));
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { 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: '刷新智能体' })));
|
||||
expect(screen.getByTestId('teacher-welcomes')).toHaveTextContent('新老师的云端欢迎语');
|
||||
for (const agent of agents) expect(screen.queryByRole('button', { name: `收起${agent.definition.name}的介绍` })).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
for (const agent of agents) expect(screen.queryByRole('button', { name: `收起全部老师介绍(${agent.definition.name})` })).not.toBeInTheDocument();
|
||||
expect(JSON.parse(localStorage.getItem('makelore-teacher-presence:kid:seen')!)).toEqual(['keep-seen']);
|
||||
expect(api.sendConversation).not.toHaveBeenCalled();
|
||||
expect(api.checkIn).not.toHaveBeenCalled();
|
||||
});
|
||||
it('adds every new teacher to the visible group without resetting the shared count', async () => {
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
await operation('one'); await operation('two');
|
||||
const added = { ...agents[0], teacher_id: 'new', definition: { ...agents[0].definition, config_id: 'new', name: '新老师' } };
|
||||
api.catalog.mockResolvedValue({ items: [...agents, added] });
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '刷新智能体' })));
|
||||
expect(screen.getByTestId('teacher-welcomes').querySelectorAll('.teacher-welcome-card')).toHaveLength(4);
|
||||
await operation('three');
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
const legacyKeys = [
|
||||
'makelore-teacher-presence:kid:project:project:introduced-agents',
|
||||
'makelore-teacher-presence:kid:project:project:project:introduced-agents',
|
||||
];
|
||||
it.each(legacyKeys)('migrates partial dismissal from %s to the entire group without changing old data', async (key) => {
|
||||
const old = JSON.stringify(['intro-1']);
|
||||
localStorage.setItem(key, old);
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
const first = await mount();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
fireEvent.pointerEnter(screen.getByRole('button', { name: '与方法老师聊天' }), { pointerType: 'mouse' });
|
||||
expect(screen.getByRole('tooltip')).toHaveTextContent('方法老师的自我介绍');
|
||||
first.unmount();
|
||||
const next = await mount();
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
expect(localStorage.getItem(key)).toBe(old);
|
||||
await act(async () => next.rerender(<Harness projectId="other" />));
|
||||
expect(screen.getByTestId('teacher-welcomes').querySelectorAll('.teacher-welcome-card')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it.each(legacyKeys)('continues legacy operation counts from %s without discarding history', async (key) => {
|
||||
const roundsKey = key + ':rounds';
|
||||
const rounds = JSON.stringify(Object.fromEntries(agents.map(agent => [agent.teacher_id, {
|
||||
since: Date.now() - 1000, runs: [JSON.stringify(['source', 'one']), JSON.stringify(['source', 'two'])],
|
||||
}])));
|
||||
localStorage.setItem(roundsKey, rounds);
|
||||
api.catalog.mockResolvedValue({ items: agents });
|
||||
await mount();
|
||||
expect(screen.getByTestId('teacher-welcomes').querySelectorAll('.teacher-welcome-card')).toHaveLength(3);
|
||||
await operation('two');
|
||||
expect(screen.getByTestId('teacher-welcomes')).toBeVisible();
|
||||
await operation('three');
|
||||
expect(screen.queryByTestId('teacher-welcomes')).not.toBeInTheDocument();
|
||||
expect(localStorage.getItem(roundsKey)).toBe(rounds);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('manual agent refresh', () => {
|
||||
|
||||
Reference in New Issue
Block a user