feat: 优化通道组编辑交互
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { AdminChannel } from '@/api/adminApi';
|
||||
import { RouteConfigModal } from './RouteConfigModal';
|
||||
|
||||
const channel = (id: string, overrides: Partial<AdminChannel> = {}): AdminChannel => ({
|
||||
id,
|
||||
name: `通道${id}`,
|
||||
code: `CODE-${id}`,
|
||||
carriers: ['mobile'],
|
||||
sendRegion: '广东省',
|
||||
gatewayHost: '127.0.0.1',
|
||||
gatewayPort: 7890,
|
||||
account: 'test',
|
||||
srcId: '106',
|
||||
rateLimitPerSecond: 1,
|
||||
unitPrice: 325,
|
||||
status: 'active',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('RouteConfigModal', () => {
|
||||
it('keeps historical priority and exposes costs while permitting a disconnected active channel', async () => {
|
||||
const onSubmit = vi.fn(() => null);
|
||||
const current = channel('current', { connectionStates: [] });
|
||||
render(
|
||||
<RouteConfigModal
|
||||
channels={[current]}
|
||||
carrier="mobile"
|
||||
modal={{ type: 'national', mode: 'edit', route: { id: 'route-1', channelId: current.id, priority: 20 } }}
|
||||
occupiedChannelIds={[current.id]}
|
||||
nextPriority={30}
|
||||
onClose={vi.fn()}
|
||||
onSubmit={onSubmit}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByRole('spinbutton', { name: /优先级/ })).toHaveValue(20);
|
||||
expect(screen.getByText('0.0325')).toBeVisible();
|
||||
expect(screen.getByText('暂无连接回写')).toBeVisible();
|
||||
expect(screen.getByRole('radio')).toBeEnabled();
|
||||
await userEvent.click(screen.getByRole('button', { name: /^确认$/ }));
|
||||
expect(onSubmit).toHaveBeenCalledWith({ id: 'route-1', channelId: 'current', priority: 20 });
|
||||
});
|
||||
|
||||
it('shows unavailable reasons and keeps the selected channel while searching', async () => {
|
||||
const channels = [
|
||||
channel('available'),
|
||||
channel('occupied'),
|
||||
channel('deleted', { status: 'deleted' }),
|
||||
channel('telecom', { carriers: ['telecom'] }),
|
||||
channel('disabled', { status: 'disabled' }),
|
||||
];
|
||||
render(
|
||||
<RouteConfigModal
|
||||
channels={channels}
|
||||
carrier="mobile"
|
||||
modal={{ type: 'national', mode: 'create' }}
|
||||
occupiedChannelIds={['occupied']}
|
||||
nextPriority={30}
|
||||
onClose={vi.fn()}
|
||||
onSubmit={() => null}
|
||||
/>,
|
||||
);
|
||||
for (const id of ['occupied', 'deleted', 'telecom'])
|
||||
expect(screen.getByRole('radio', { name: new RegExp(`CODE-${id}`) })).toBeDisabled();
|
||||
expect(screen.getByText('已在当前通道组中配置')).toBeVisible();
|
||||
expect(screen.getByText('通道已删除')).toBeVisible();
|
||||
expect(screen.getByText('不支持移动')).toBeVisible();
|
||||
expect(screen.getByText('已停用')).toBeVisible();
|
||||
expect(screen.getByRole('radio', { name: /CODE-disabled/ })).toBeEnabled();
|
||||
await userEvent.click(screen.getByRole('radio', { name: /CODE-available/ }));
|
||||
await userEvent.type(screen.getByRole('textbox', { name: '搜索通道' }), 'CODE-telecom');
|
||||
expect(screen.getAllByRole('radio')).toHaveLength(1);
|
||||
expect(screen.getByText('已选:通道available(CODE-available)')).toBeVisible();
|
||||
});
|
||||
|
||||
it('disables region mismatches but preserves a historical province option', () => {
|
||||
render(
|
||||
<RouteConfigModal
|
||||
channels={[channel('other', { sendRegion: '江苏省' })]}
|
||||
carrier="mobile"
|
||||
modal={{ type: 'province', mode: 'edit', route: { id: 'p-1', channelId: 'missing', province: '浙江省' } }}
|
||||
occupiedChannelIds={[]}
|
||||
nextPriority={10}
|
||||
onClose={vi.fn()}
|
||||
onSubmit={() => null}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('浙江省')).toBeVisible();
|
||||
expect(screen.getByRole('radio')).toBeDisabled();
|
||||
expect(screen.getByText('通道地区与所选省份不匹配')).toBeVisible();
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('原通道不存在,请重新选择');
|
||||
});
|
||||
|
||||
it('retains form values after parent conflict rejection and rejects a fractional priority', async () => {
|
||||
const onSubmit = vi.fn(() => '同一通道组内全国通道优先级不能重复');
|
||||
const onClose = vi.fn();
|
||||
render(
|
||||
<RouteConfigModal
|
||||
channels={[channel('1')]}
|
||||
carrier="mobile"
|
||||
modal={{ type: 'national', mode: 'create' }}
|
||||
occupiedChannelIds={[]}
|
||||
nextPriority={20}
|
||||
onClose={onClose}
|
||||
onSubmit={onSubmit}
|
||||
/>,
|
||||
);
|
||||
await userEvent.click(screen.getByRole('radio'));
|
||||
await userEvent.click(screen.getByRole('button', { name: /^确认$/ }));
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('优先级不能重复');
|
||||
expect(screen.getByRole('radio')).toBeChecked();
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
const input = screen.getByRole('spinbutton', { name: /优先级/ });
|
||||
fireEvent.change(input, { target: { value: '1.5' } });
|
||||
await userEvent.click(screen.getByRole('button', { name: /^确认$/ }));
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('整数');
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
fireEvent.change(input, { target: { value: '-10' } });
|
||||
await userEvent.click(screen.getByRole('button', { name: /^确认$/ }));
|
||||
expect(onSubmit).toHaveBeenLastCalledWith(expect.objectContaining({ priority: -10 }));
|
||||
});
|
||||
|
||||
it('uses the shared unsaved guard when dismissing a changed selection', async () => {
|
||||
const onClose = vi.fn();
|
||||
render(
|
||||
<RouteConfigModal
|
||||
channels={[channel('1')]}
|
||||
carrier="mobile"
|
||||
modal={{ type: 'national', mode: 'create' }}
|
||||
occupiedChannelIds={[]}
|
||||
nextPriority={10}
|
||||
onClose={onClose}
|
||||
onSubmit={() => null}
|
||||
/>,
|
||||
);
|
||||
await userEvent.click(screen.getByRole('radio'));
|
||||
await userEvent.click(screen.getByRole('button', { name: /^取消$/ }));
|
||||
expect(screen.getByRole('alertdialog')).toHaveTextContent('放弃未保存的修改');
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
await userEvent.click(screen.getByRole('button', { name: '继续编辑' }));
|
||||
expect(screen.getByRole('radio')).toBeChecked();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user