feat(web): protect phone data and polish release UI

This commit is contained in:
Codex
2026-07-30 20:16:50 +08:00
parent 6ec8e4f6a6
commit d60cff1dd8
19 changed files with 800 additions and 396 deletions
+104 -4
View File
@@ -23,7 +23,12 @@ const snapshot: FleetSnapshot = {
summary: {
version: '1.1.6',
freshness: 'fresh',
resources: { cpuPercent: 24, memoryPercent: 51, maxTemperatureCelsius: 42 },
resources: {
cpuPercent: 24,
memoryPercent: 51,
maxTemperatureCelsius: 42,
phoneNumbers: ['+852 5550 0100'],
},
},
},
],
@@ -33,6 +38,95 @@ const snapshot: FleetSnapshot = {
afterEach(cleanup);
describe('FleetPage card navigation', () => {
it('masks both phone locations and reveals them only for the selected card', async () => {
const privacySnapshot: FleetSnapshot = {
instances: [
...snapshot.instances,
{
id: 'beta',
name: 'Beta modem',
url: 'http://192.168.1.3',
tags: [],
revision: 1,
},
],
statuses: new Map([
...snapshot.statuses,
[
'beta',
{
reachable: true,
authenticated: true,
summary: {
version: '1.2.0',
freshness: 'fresh' as const,
resources: { phoneNumbers: ['13812345678'] },
},
},
],
]),
};
const messagesDataSource = {
load: vi.fn(async (instanceId: string) =>
instanceId === 'alpha'
? {
latest: {
id: 'sms-1',
direction: 'incoming',
phoneNumber: '13900139000',
content: 'status',
timestamp: '2026-07-19T08:30:00.000Z',
},
}
: {},
),
};
render(<FleetPage initialData={privacySnapshot} messagesDataSource={messagesDataSource} />);
const alpha = screen.getByRole('article', { name: 'Alpha modem 实例概览' });
const beta = screen.getByRole('article', { name: 'Beta modem 实例概览' });
expect(within(alpha).getByText('+852 •••• 0100')).toBeTruthy();
expect(within(beta).getByText('138 •••• 5678')).toBeTruthy();
expect(await within(alpha).findByText('139 •••• 9000')).toBeTruthy();
expect(within(alpha).queryByText('+852 5550 0100')).toBeNull();
expect(within(alpha).queryByText('13900139000')).toBeNull();
const reveal = within(alpha).getByRole('button', { name: '显示 Alpha modem 手机号' });
expect(reveal.getAttribute('aria-pressed')).toBe('false');
fireEvent.click(reveal);
expect(within(alpha).getByText('+852 5550 0100')).toBeTruthy();
expect(within(alpha).getByText('13900139000')).toBeTruthy();
expect(within(beta).getByText('138 •••• 5678')).toBeTruthy();
expect(within(beta).queryByText('13812345678')).toBeNull();
expect(within(alpha).getByRole('button', { name: '隐藏 Alpha modem 手机号' })).toBeTruthy();
});
it('does not show a privacy toggle when neither phone location has a number', () => {
const noNumbers: FleetSnapshot = {
...snapshot,
statuses: new Map([
[
'alpha',
{
...snapshot.statuses.get('alpha')!,
summary: {
...snapshot.statuses.get('alpha')!.summary,
resources: { cpuPercent: 24, memoryPercent: 51 },
},
},
],
]),
};
render(<FleetPage initialData={noNumbers} />);
const card = screen.getByRole('article', { name: 'Alpha modem 实例概览' });
expect(within(card).getByText('暂未获取')).toBeTruthy();
expect(within(card).queryByRole('button', { name: /手机号/ })).toBeNull();
});
it('makes the whole instance card open its dashboard while ops stay separate and config stays out of the card', () => {
render(<FleetPage initialData={snapshot} />);
@@ -47,6 +141,11 @@ describe('FleetPage card navigation', () => {
expect(screen.getByRole('region', { name: '实例状态摘要' }).textContent).toMatch(
/\s*1.*线\s*1.*\s*0/s,
);
expect(screen.queryByText('NODE DIRECTORY')).toBeNull();
expect(screen.queryByText(/LIVE NODE MATRIX/u)).toBeNull();
expect(screen.queryByText('已纳入统一管理')).toBeNull();
expect(screen.queryByText('连接与认证正常')).toBeNull();
expect(screen.queryByText('离线、认证或未知')).toBeNull();
expect(screen.queryByRole('region', { name: '节点资源健康' })).toBeNull();
expect(within(card).getByText('SimAdmin 1.1.6')).toBeTruthy();
expect(
@@ -147,11 +246,12 @@ describe('FleetPage search and filter toolbar', () => {
const search = screen.getByRole('search', { name: '实例搜索与筛选' });
expect(within(search).getByPlaceholderText('搜索名称、ID、标签、源地址…')).toBeTruthy();
expect(within(search).getByText(/显示/).textContent).toMatch(/显示\s*1\s*\/\s*1/);
expect(within(search).queryByText(/显示/)).toBeNull();
expect(document.querySelector('.fleet-result-count')?.textContent).toMatch(/显示\s*1\s*\/\s*1/);
fireEvent.click(within(search).getByRole('button', { name: /离线/ }));
expect(screen.queryByRole('article', { name: 'Alpha modem 实例概览' })).toBeNull();
expect(within(search).getByText(/显示/).textContent).toMatch(/显示\s*0\s*\/\s*1/);
expect(document.querySelector('.fleet-result-count')?.textContent).toMatch(/显示\s*0\s*\/\s*1/);
expect(within(search).getByRole('button', { name: /移除筛选 状态:离线/ })).toBeTruthy();
fireEvent.change(within(search).getByPlaceholderText('搜索名称、ID、标签、源地址…'), {
@@ -161,7 +261,7 @@ describe('FleetPage search and filter toolbar', () => {
fireEvent.click(within(search).getByRole('button', { name: '清除全部筛选' }));
expect(screen.getByRole('article', { name: 'Alpha modem 实例概览' })).toBeTruthy();
expect(within(search).getByText(/显示/).textContent).toMatch(/显示\s*1\s*\/\s*1/);
expect(document.querySelector('.fleet-result-count')?.textContent).toMatch(/显示\s*1\s*\/\s*1/);
});
it('filters the matrix from the tag group row without moving search out of the sidebar', () => {