收口前端平台组件库能力
新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件 迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome 补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
@@ -98,6 +98,23 @@ function buildSession(
|
||||
};
|
||||
}
|
||||
|
||||
function findNearestClassName(
|
||||
element: HTMLElement,
|
||||
classNamePart: string,
|
||||
): HTMLElement | null {
|
||||
let current: HTMLElement | null = element;
|
||||
|
||||
while (current) {
|
||||
if (current.className.includes(classNamePart)) {
|
||||
return current;
|
||||
}
|
||||
|
||||
current = current.parentElement;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
test('settings header uses a generic title instead of the phone number', () => {
|
||||
renderAccountModal();
|
||||
|
||||
@@ -119,6 +136,27 @@ test('settings header uses a generic title instead of the phone number', () => {
|
||||
expect(screen.getByRole('button', { name: /账号与安全/u })).toBeTruthy();
|
||||
expect(screen.queryByRole('button', { name: /主题外观/u })).toBeNull();
|
||||
expect(screen.queryByRole('button', { name: /账号信息/u })).toBeNull();
|
||||
|
||||
const themeSettingsButton = screen.getByRole('button', { name: /主题设置/u });
|
||||
expect(themeSettingsButton.getAttribute('type')).toBe('button');
|
||||
expect(themeSettingsButton.className).toContain('platform-subpanel');
|
||||
expect(themeSettingsButton.className).toContain('rounded-[1.5rem]');
|
||||
expect(themeSettingsButton.className).toContain('hover:bg-white');
|
||||
});
|
||||
|
||||
test('appearance panel uses PlatformPillBadge for current theme status', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderAccountModal();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /主题设置/u }));
|
||||
|
||||
const appearanceDialog = screen.getByRole('dialog', { name: '主题设置' });
|
||||
const themeStatusBadge = within(appearanceDialog).getByText('平台设置已同步');
|
||||
|
||||
expect(within(appearanceDialog).getByText('当前主题')).toBeTruthy();
|
||||
expect(themeStatusBadge.className).toContain('rounded-full');
|
||||
expect(themeStatusBadge.className).toContain('bg-white/72');
|
||||
});
|
||||
|
||||
test('direct account entry does not render the settings shell as another dialog', () => {
|
||||
@@ -159,6 +197,9 @@ test('account panel uses compact binding cards and keeps logout actions at the b
|
||||
'[data-account-binding-card]',
|
||||
);
|
||||
expect(compactCards).toHaveLength(2);
|
||||
expect(compactCards[0]?.className).toContain('platform-subpanel');
|
||||
expect(compactCards[0]?.className).toContain('rounded-[1rem]');
|
||||
expect(compactCards[0]?.className).toContain('px-3.5 py-3');
|
||||
expect(
|
||||
within(compactCards[0] as HTMLElement).getByRole('button', {
|
||||
name: '更换手机号',
|
||||
@@ -357,6 +398,18 @@ test('account panel includes merged security devices and audit sections', async
|
||||
expect(within(accountDialog).getByText('手机号保护')).toBeTruthy();
|
||||
expect(within(accountDialog).getByText('iPhone 15 Pro')).toBeTruthy();
|
||||
expect(within(accountDialog).getByText('登录成功')).toBeTruthy();
|
||||
const deviceRow = findNearestClassName(
|
||||
within(accountDialog).getByText('iPhone 15 Pro'),
|
||||
'bg-white/72',
|
||||
);
|
||||
const auditRow = findNearestClassName(
|
||||
within(accountDialog).getByText('登录成功'),
|
||||
'bg-white/72',
|
||||
);
|
||||
expect(deviceRow?.className).toContain('rounded-[1rem]');
|
||||
expect(deviceRow?.className).toContain('px-4 py-3');
|
||||
expect(auditRow?.className).toContain('rounded-[1rem]');
|
||||
expect(auditRow?.className).toContain('px-4 py-3');
|
||||
expect(
|
||||
within(accountDialog).getByRole('button', { name: '退出登录' }),
|
||||
).toBeTruthy();
|
||||
@@ -392,7 +445,14 @@ test('current merged session group hides kick action and shows count', async ()
|
||||
await user.click(screen.getByRole('button', { name: /账号与安全/ }));
|
||||
|
||||
const accountDialog = screen.getByRole('dialog', { name: '账号信息' });
|
||||
const sessionCountBadge = within(accountDialog).getByText('2 个会话');
|
||||
const currentDeviceBadge = within(accountDialog).getByText('当前设备');
|
||||
|
||||
expect(within(accountDialog).getByText('2 个会话')).toBeTruthy();
|
||||
expect(sessionCountBadge.className).toContain('rounded-full');
|
||||
expect(sessionCountBadge.className).toContain('bg-white/72');
|
||||
expect(currentDeviceBadge.className).toContain('rounded-full');
|
||||
expect(currentDeviceBadge.className).toContain('border-emerald-200');
|
||||
expect(
|
||||
within(accountDialog).queryByRole('button', { name: '踢下线' }),
|
||||
).toBeNull();
|
||||
@@ -419,8 +479,12 @@ test('remote merged session group can be revoked with loading state', async () =
|
||||
const revokeButton = within(accountDialog).getByRole('button', {
|
||||
name: '处理中...',
|
||||
}) as HTMLButtonElement;
|
||||
const loggedInBadge = within(accountDialog).getByText('已登录');
|
||||
|
||||
expect(revokeButton.disabled).toBe(true);
|
||||
expect(within(accountDialog).getByText('2 个会话')).toBeTruthy();
|
||||
expect(loggedInBadge.className).toContain('rounded-full');
|
||||
expect(loggedInBadge.className).toContain('border-emerald-200');
|
||||
expect(onRevokeSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user