/**
* 授权页:**新到手的待决请求必须自己露出来**。
*
* 这一条来自线上实测的报告:"我点到授权界面,才更新显示授权请求"。
* 复现出来的是:徽标数字实时变了(推送没问题),但新请求的正文看不见 ——
* 因为 `expanded` 一旦被手动点过就冻结成快照,之后新出现的待决会话
* 一直处于折叠状态,直到离开再回来(组件重挂载、默认展开重算)。
*
* 两条判据必须成对:新请求要自动展开,而用户在那之后的手动折叠**不许**被弹开。
*/
import { describe, expect, it, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import PermissionList from '../../src/components/PermissionList';
import { useMailStore } from '../../src/stores/mailStore';
import { useSessionStore } from '../../src/stores/sessionStore';
import { useUIStore } from '../../src/stores/uiStore';
const perm = (sessionId: string, subject: string, id: string) => ({
mail_id: id,
session_id: sessionId,
from_name: 'dsh',
to_name: 'gui-lab',
subject,
body: '工具:bash',
mail_type: 'permission_request' as const,
permission_options: ['同意', '拒绝'],
permission_multi_select: false,
permission_result: '',
status: 'unread',
created_at: '2026-09-13T01:00:00Z',
session_alias: 'sess-' + sessionId,
session_workspace: '/tmp/' + sessionId
});
describe('授权页的自动展开', () => {
beforeEach(() => {
useMailStore.setState({ inbox: [], currentMail: null, accountErrors: [] } as never);
useSessionStore.setState({ current: null } as never);
useUIStore.setState({ viewMode: 'permissions' });
});
it('★ 新到手的待决请求要自动展开(不能等重进页面)', async () => {
// 会话 A 已有待决 → 自动展开
useMailStore.setState({ inbox: [perm('sess-a', 'A 的旧请求', 'm-a1')] } as never);
render();
expect(await screen.findByText('A 的旧请求')).toBeInTheDocument();
// 用户手动折叠一次 —— 这一步把 expanded 冻结成快照(线上就是这样)
// 点会话分组头:按内容找按钮(别名与时间连在同一串文本里,
// 用 getByText 会命中不到;/待决策/ 又会同时命中顶部计数)
const headerA = screen.getAllByRole('button').find(b => (b.textContent || '').includes('sess-sess-a'));
expect(headerA).toBeTruthy();
await userEvent.click(headerA!);
await waitFor(() => expect(screen.queryByText('A 的旧请求')).toBeNull());
// 新请求到达(另一个会话,此前没有待决)
useMailStore.setState({
inbox: [perm('sess-a', 'A 的旧请求', 'm-a1'), perm('sess-b', 'B 的新请求', 'm-b1')]
} as never);
// ★ 必须自己露出来;同时 A 保持用户折叠的状态(不能被弹开)
expect(await screen.findByText('B 的新请求')).toBeInTheDocument();
expect(screen.queryByText('A 的旧请求')).toBeNull();
});
it('★ 同一个已折叠的会话里又来一条新待决 → 也要露出来(实测撞到的就是这种)', async () => {
useMailStore.setState({ inbox: [perm('sess-a', 'A 的旧请求', 'm-a1')] } as never);
render();
expect(await screen.findByText('A 的旧请求')).toBeInTheDocument();
const hdr = screen.getAllByRole('button').find(b => (b.textContent || '').includes('sess-sess-a'));
await userEvent.click(hdr!);
await waitFor(() => expect(screen.queryByText('A 的旧请求')).toBeNull());
// 同一会话又来一条(会话早就在折叠状态里)—— 必须自己打开
useMailStore.setState({
inbox: [perm('sess-a', 'A 的旧请求', 'm-a1'), perm('sess-a', 'A 的新一条', 'm-a2')]
} as never);
expect(await screen.findByText('A 的新一条')).toBeInTheDocument();
});
it('反向对照:没有新邮件时,手动折叠的分组保持折叠(尊重人的操作)', async () => {
useMailStore.setState({ inbox: [perm('sess-a', 'A 的旧请求', 'm-a1')] } as never);
render();
expect(await screen.findByText('A 的旧请求')).toBeInTheDocument();
const hdr = screen.getAllByRole('button').find(b => (b.textContent || '').includes('sess-sess-a'));
await userEvent.click(hdr!);
await waitFor(() => expect(screen.queryByText('A 的旧请求')).toBeNull());
// 只重渲染、没有新邮件 → 不该被弹开
useMailStore.setState({ inbox: [perm('sess-a', 'A 的旧请求', 'm-a1')] } as never);
await new Promise(r => setTimeout(r, 80));
expect(screen.queryByText('A 的旧请求')).toBeNull();
});
});