import { fireEvent, render, screen } from '@testing-library/react'; import { NextIntlClientProvider } from 'next-intl'; import type { ReactElement } from 'react'; import { expect, it, vi } from 'vitest'; import type { WAFIPGroup, WAFRuleNode } from '@/lib/services/openflare'; import mainZh from '@/messages/zh-CN.json'; import securityZh from '@/messages/fragments/security.zh-CN.json'; import { NodeProperties } from './node-properties'; const messages = { ...mainZh, ...securityZh }; function renderWithIntl(ui: ReactElement) { return render( {ui} , ); } it('hides match and block until UA check is enabled', () => { const node: WAFRuleNode = { id: 'ua', type: 'ua_check', position: { x: 0, y: 0 }, config: { require_ua: false, browsers: [], operating_systems: [], match_mode: 'or', block_common_bots: false, block_abnormal_ua: false, block_custom_ua: false, custom_ua_patterns: [], }, }; const onChange = vi.fn(); const { rerender } = renderWithIntl( , ); expect( screen.queryByRole('switch', { name: /屏蔽常见爬虫/ }), ).not.toBeInTheDocument(); expect(screen.queryByLabelText('浏览器')).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('switch', { name: /开启 UA 检查/ })); expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ require_ua: true }), }), ); rerender( , ); expect( screen.getByRole('switch', { name: /屏蔽常见爬虫/ }), ).toBeInTheDocument(); expect( screen.getByRole('switch', { name: /屏蔽非正常/ }), ).toBeInTheDocument(); expect( screen.getByRole('switch', { name: /屏蔽自定义/ }), ).toBeInTheDocument(); expect(screen.getAllByLabelText('说明').length).toBeGreaterThan(0); }); it('edits display name for configurable nodes', () => { const node: WAFRuleNode = { id: 'match', type: 'ip_match', position: { x: 0, y: 0 }, config: { ips: [], cidrs: [], ip_group_ids: [] }, }; const onChange = vi.fn(); renderWithIntl( , ); fireEvent.change(screen.getByLabelText('显示名称'), { target: { value: '内网放行' }, }); expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ label: '内网放行' }), ); }); it('hides display name for system nodes', () => { const node: WAFRuleNode = { id: 'start', type: 'start', position: { x: 0, y: 0 }, config: {}, }; renderWithIntl( , ); expect(screen.queryByLabelText('显示名称')).not.toBeInTheDocument(); expect(screen.getByText('系统节点无需配置。')).toBeInTheDocument(); }); it('edits IP group config through a typed multi-select', async () => { const node: WAFRuleNode = { id: 'match', type: 'ip_match', position: { x: 0, y: 0 }, config: { ips: [], cidrs: [], ip_group_ids: [] }, }; const group = { id: 7, name: '办公室出口' } as WAFIPGroup; const onChange = vi.fn(); renderWithIntl( , ); fireEvent.click(screen.getByRole('button', { name: 'IP 组' })); fireEvent.click(await screen.findByText('办公室出口')); expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ ip_group_ids: [7] }), }), ); }); it('associates numeric property labels and constrains server ranges', () => { const node: WAFRuleNode = { id: 'pow', type: 'pow', position: { x: 0, y: 0 }, config: { algorithm: 'fast', difficulty: 4, session_ttl: 60, challenge_ttl: 30, }, }; renderWithIntl( , ); expect(screen.getByLabelText('难度')).toHaveAttribute('min', '1'); expect(screen.getByLabelText('难度')).toHaveAttribute('max', '16'); expect(screen.getByLabelText('会话 TTL(秒)')).toHaveAttribute('min', '60'); }); it('creates any normalized valid geography code', async () => { const node: WAFRuleNode = { id: 'geo', type: 'geo_match', position: { x: 0, y: 0 }, config: { countries: [], regions: [] }, }; const onChange = vi.fn(); renderWithIntl( , ); fireEvent.click(screen.getByRole('button', { name: '国家代码' })); fireEvent.change(await screen.findByPlaceholderText('搜索名称或代码'), { target: { value: 'nz' }, }); fireEvent.click(screen.getByRole('button', { name: '添加代码' })); expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ config: expect.objectContaining({ countries: ['NZ'] }), }), ); }); it('shows localized country names together with their codes', async () => { const node: WAFRuleNode = { id: 'geo', type: 'geo_match', position: { x: 0, y: 0 }, config: { countries: [], regions: [] }, }; renderWithIntl( , ); fireEvent.click(screen.getByRole('button', { name: '国家代码' })); expect(await screen.findByText('中国')).toBeInTheDocument(); expect(screen.getByText('CN')).toBeInTheDocument(); expect(screen.getByText(/共 249 个国家\/地区/)).toBeInTheDocument(); });