feat(web): add console update panel, system maintenance path and connection settings UI
- Add online update workflow with plan, backup and install states. - Expose maintenance paths, component backup jobs and connection authorization modes. - Align auth and automation screens with the fused control plane.
This commit is contained in:
@@ -8,7 +8,7 @@ import type { SystemMaintenanceDataSource } from './system-maintenance-api-data-
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const defaults = { heartbeatSeconds: 30, offlineSeconds: 90 };
|
||||
const defaults = { heartbeatSeconds: 30, offlineSeconds: 90, authorizationMode: 'auto' };
|
||||
|
||||
function source(overrides: Partial<SystemMaintenanceDataSource> = {}): SystemMaintenanceDataSource {
|
||||
return {
|
||||
@@ -36,6 +36,10 @@ function field(region: HTMLElement, label: string): HTMLInputElement {
|
||||
return node;
|
||||
}
|
||||
|
||||
function modeButton(region: HTMLElement, label: string): HTMLButtonElement {
|
||||
return within(region).getByRole('button', { name: label }) as HTMLButtonElement;
|
||||
}
|
||||
|
||||
describe('ConnectionSettingsPanel', () => {
|
||||
it('loads the saved cadence into the inputs', async () => {
|
||||
const { data, region } = await mount();
|
||||
@@ -44,6 +48,30 @@ describe('ConnectionSettingsPanel', () => {
|
||||
expect(field(region, '离线判定(秒)').value).toBe('90');
|
||||
});
|
||||
|
||||
it('starts new-device authorization in automatic mode', async () => {
|
||||
const { region } = await mount();
|
||||
|
||||
expect(modeButton(region, '自动授权').getAttribute('aria-pressed')).toBe('true');
|
||||
expect(modeButton(region, '人工确认').getAttribute('aria-pressed')).toBe('false');
|
||||
});
|
||||
|
||||
it('saves a selected manual authorization mode and echoes it', async () => {
|
||||
const { data, region } = await mount();
|
||||
await userEvent.click(modeButton(region, '人工确认'));
|
||||
|
||||
const save = within(region).getByRole('button', { name: '保存设置' }) as HTMLButtonElement;
|
||||
expect(save.disabled).toBe(false);
|
||||
await userEvent.click(save);
|
||||
|
||||
expect(data.updateConnection).toHaveBeenCalledWith({
|
||||
heartbeatSeconds: 30,
|
||||
offlineSeconds: 90,
|
||||
authorizationMode: 'manual',
|
||||
});
|
||||
const notice = await within(region).findByRole('status');
|
||||
expect(notice.textContent).toContain('新设备人工确认');
|
||||
});
|
||||
|
||||
it('keeps save disabled until a valid change is made', async () => {
|
||||
const { region } = await mount();
|
||||
const save = within(region).getByRole('button', { name: '保存设置' }) as HTMLButtonElement;
|
||||
@@ -69,9 +97,12 @@ describe('ConnectionSettingsPanel', () => {
|
||||
expect(data.updateConnection).toHaveBeenCalledWith({
|
||||
heartbeatSeconds: 45,
|
||||
offlineSeconds: 180,
|
||||
authorizationMode: 'auto',
|
||||
});
|
||||
const notice = await within(region).findByRole('status');
|
||||
expect(notice.textContent).toContain('连接设置已保存,心跳 45 秒,离线判定 180 秒。');
|
||||
expect(notice.textContent).toContain(
|
||||
'连接设置已保存,新设备自动授权,心跳 45 秒,离线判定 180 秒。',
|
||||
);
|
||||
});
|
||||
|
||||
it('blocks a save when the offline window drops below twice the heartbeat', async () => {
|
||||
@@ -103,7 +134,7 @@ describe('ConnectionSettingsPanel', () => {
|
||||
await userEvent.type(field(region, '离线判定(秒)'), '150');
|
||||
await userEvent.click(within(region).getByRole('button', { name: '保存设置' }));
|
||||
const problem = await within(region).findByRole('alert');
|
||||
expect(problem.textContent).toContain('连接设置未保存,请检查心跳与离线判定的取值范围。');
|
||||
expect(problem.textContent).toContain('连接设置未保存,请检查各项设置的取值范围。');
|
||||
expect(field(region, '心跳间隔(秒)').value).toBe('60');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user