mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 00:26:37 +08:00
[功能] 重构代理路由页面的单元测试,优化fetch模拟和输入验证逻辑
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -40,78 +40,117 @@ describe('ProxyRoutesPage', () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildBaseFetchStub({
|
||||||
|
managedDomains,
|
||||||
|
origins = [],
|
||||||
|
certificates = [{ id: 1, name: 'example-cert', not_after: null }],
|
||||||
|
matchResult,
|
||||||
|
}: {
|
||||||
|
managedDomains: Array<Record<string, unknown>>;
|
||||||
|
origins?: Array<Record<string, unknown>>;
|
||||||
|
certificates?: Array<Record<string, unknown>>;
|
||||||
|
matchResult?: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
return vi.fn((input: RequestInfo | URL) => {
|
||||||
|
const url = String(input);
|
||||||
|
|
||||||
|
if (url.includes('/proxy-routes/')) {
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(JSON.stringify({ success: true, message: '', data: [] })),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.includes('/managed-domains/match?')) {
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: '',
|
||||||
|
data:
|
||||||
|
matchResult ??
|
||||||
|
{
|
||||||
|
domain: 'a.example.com',
|
||||||
|
matched: true,
|
||||||
|
candidate: {
|
||||||
|
managed_domain_id: 2,
|
||||||
|
domain: 'a.example.com',
|
||||||
|
match_type: 'exact',
|
||||||
|
certificate_id: 1,
|
||||||
|
certificate_name: 'example-cert',
|
||||||
|
},
|
||||||
|
candidates: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.includes('/managed-domains/')) {
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: '',
|
||||||
|
data: managedDomains,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.includes('/origins/')) {
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: '',
|
||||||
|
data: origins,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.includes('/tls-certificates/')) {
|
||||||
|
return Promise.resolve(
|
||||||
|
new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: '',
|
||||||
|
data: certificates,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(new Error(`Unhandled fetch: ${url}`));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('shows wildcard subdomain input after selecting a wildcard website', async () => {
|
it('shows wildcard subdomain input after selecting a wildcard website', async () => {
|
||||||
stubMatchMedia();
|
stubMatchMedia();
|
||||||
|
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn((input: RequestInfo | URL) => {
|
buildBaseFetchStub({
|
||||||
const url = String(input);
|
managedDomains: [
|
||||||
|
{
|
||||||
if (url.includes('/proxy-routes/')) {
|
id: 1,
|
||||||
return Promise.resolve(
|
domain: '*.example.com',
|
||||||
new Response(
|
cert_id: 1,
|
||||||
JSON.stringify({ success: true, message: '', data: [] }),
|
enabled: true,
|
||||||
),
|
remark: '',
|
||||||
);
|
created_at: '2026-03-20T08:00:00Z',
|
||||||
}
|
updated_at: '2026-03-20T08:00:00Z',
|
||||||
|
},
|
||||||
if (url.includes('/managed-domains/')) {
|
{
|
||||||
return Promise.resolve(
|
id: 2,
|
||||||
new Response(
|
domain: 'a.example.com',
|
||||||
JSON.stringify({
|
cert_id: 1,
|
||||||
success: true,
|
enabled: true,
|
||||||
message: '',
|
remark: '',
|
||||||
data: [
|
created_at: '2026-03-20T08:00:00Z',
|
||||||
{
|
updated_at: '2026-03-20T08:00:00Z',
|
||||||
id: 1,
|
},
|
||||||
domain: '*.example.com',
|
],
|
||||||
cert_id: 1,
|
|
||||||
enabled: true,
|
|
||||||
remark: '',
|
|
||||||
created_at: '2026-03-20T08:00:00Z',
|
|
||||||
updated_at: '2026-03-20T08:00:00Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
domain: 'a.example.com',
|
|
||||||
cert_id: 1,
|
|
||||||
enabled: true,
|
|
||||||
remark: '',
|
|
||||||
created_at: '2026-03-20T08:00:00Z',
|
|
||||||
updated_at: '2026-03-20T08:00:00Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.includes('/origins/')) {
|
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: '',
|
|
||||||
data: [],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.includes('/tls-certificates/')) {
|
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: '',
|
|
||||||
data: [{ id: 1, name: 'example-cert', not_after: null }],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject(new Error(`Unhandled fetch: ${url}`));
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,23 +160,17 @@ describe('ProxyRoutesPage', () => {
|
|||||||
await user.click(await screen.findByRole('button', { name: '新增规则' }));
|
await user.click(await screen.findByRole('button', { name: '新增规则' }));
|
||||||
const dialog = await screen.findByRole('dialog', { name: '新增规则' });
|
const dialog = await screen.findByRole('dialog', { name: '新增规则' });
|
||||||
|
|
||||||
await user.selectOptions(
|
await user.click(
|
||||||
within(dialog).getAllByRole('combobox')[0],
|
within(dialog).getByRole('button', { name: /Select Target Domain/ }),
|
||||||
'*.example.com',
|
|
||||||
);
|
);
|
||||||
|
await user.click(await screen.findByText('*.example.com'));
|
||||||
|
|
||||||
expect(await screen.findByPlaceholderText('ai')).toBeInTheDocument();
|
expect(await screen.findByPlaceholderText('e.g. ai')).toBeInTheDocument();
|
||||||
expect(
|
expect(screen.getByText('*.example.com')).toBeInTheDocument();
|
||||||
screen.getByText(
|
|
||||||
'当前网站为通配符 *.example.com,这里只需填写前缀,例如 ai。',
|
|
||||||
),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
|
|
||||||
await user.type(screen.getByPlaceholderText('ai'), 'ai');
|
await user.type(screen.getByPlaceholderText('e.g. ai'), 'ai');
|
||||||
|
|
||||||
expect(
|
expect(screen.getByText('ai.example.com')).toBeInTheDocument();
|
||||||
screen.getByText('当前将生成规则域名 ai.example.com'),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses exact website directly without showing subdomain input', async () => {
|
it('uses exact website directly without showing subdomain input', async () => {
|
||||||
@@ -145,64 +178,18 @@ describe('ProxyRoutesPage', () => {
|
|||||||
|
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
'fetch',
|
'fetch',
|
||||||
vi.fn((input: RequestInfo | URL) => {
|
buildBaseFetchStub({
|
||||||
const url = String(input);
|
managedDomains: [
|
||||||
|
{
|
||||||
if (url.includes('/proxy-routes/')) {
|
id: 2,
|
||||||
return Promise.resolve(
|
domain: 'a.example.com',
|
||||||
new Response(
|
cert_id: 1,
|
||||||
JSON.stringify({ success: true, message: '', data: [] }),
|
enabled: true,
|
||||||
),
|
remark: '',
|
||||||
);
|
created_at: '2026-03-20T08:00:00Z',
|
||||||
}
|
updated_at: '2026-03-20T08:00:00Z',
|
||||||
|
},
|
||||||
if (url.includes('/managed-domains/')) {
|
],
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: '',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
domain: 'a.example.com',
|
|
||||||
cert_id: 1,
|
|
||||||
enabled: true,
|
|
||||||
remark: '',
|
|
||||||
created_at: '2026-03-20T08:00:00Z',
|
|
||||||
updated_at: '2026-03-20T08:00:00Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.includes('/origins/')) {
|
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: '',
|
|
||||||
data: [],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.includes('/tls-certificates/')) {
|
|
||||||
return Promise.resolve(
|
|
||||||
new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: '',
|
|
||||||
data: [{ id: 1, name: 'example-cert', not_after: null }],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject(new Error(`Unhandled fetch: ${url}`));
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -212,16 +199,71 @@ describe('ProxyRoutesPage', () => {
|
|||||||
await user.click(await screen.findByRole('button', { name: '新增规则' }));
|
await user.click(await screen.findByRole('button', { name: '新增规则' }));
|
||||||
const dialog = await screen.findByRole('dialog', { name: '新增规则' });
|
const dialog = await screen.findByRole('dialog', { name: '新增规则' });
|
||||||
|
|
||||||
await user.selectOptions(
|
await user.click(
|
||||||
within(dialog).getAllByRole('combobox')[0],
|
within(dialog).getByRole('button', { name: /Select Target Domain/ }),
|
||||||
'a.example.com',
|
);
|
||||||
|
await user.click(await screen.findByText('a.example.com'));
|
||||||
|
|
||||||
|
expect(screen.queryByPlaceholderText('e.g. ai')).not.toBeInTheDocument();
|
||||||
|
expect((await screen.findAllByText('a.example.com')).length).toBeGreaterThan(0);
|
||||||
|
expect(await screen.findByText('Select Certificate')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows origin autocomplete suggestions and empty state', async () => {
|
||||||
|
stubMatchMedia();
|
||||||
|
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
buildBaseFetchStub({
|
||||||
|
managedDomains: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
domain: '*.example.com',
|
||||||
|
cert_id: 1,
|
||||||
|
enabled: true,
|
||||||
|
remark: '',
|
||||||
|
created_at: '2026-03-20T08:00:00Z',
|
||||||
|
updated_at: '2026-03-20T08:00:00Z',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
origins: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Main Backend',
|
||||||
|
address: '192.168.1.45',
|
||||||
|
remark: '',
|
||||||
|
route_count: 1,
|
||||||
|
created_at: '2026-03-20T08:00:00Z',
|
||||||
|
updated_at: '2026-03-20T08:00:00Z',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.queryByPlaceholderText('ai')).not.toBeInTheDocument();
|
renderProxyRoutesPage();
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.click(await screen.findByRole('button', { name: '新增规则' }));
|
||||||
|
|
||||||
|
const domainTrigger = await screen.findByRole('button', {
|
||||||
|
name: /Select Target Domain/,
|
||||||
|
});
|
||||||
|
await user.click(domainTrigger);
|
||||||
|
await user.click(await screen.findByText('*.example.com'));
|
||||||
|
|
||||||
|
const addressInput = screen.getByPlaceholderText('192.168.1.45');
|
||||||
|
await user.click(addressInput);
|
||||||
|
await user.type(addressInput, '192.');
|
||||||
|
|
||||||
|
expect(await screen.findByText('192.168.1.45')).toBeInTheDocument();
|
||||||
|
expect(await screen.findByText('(Main Backend)')).toBeInTheDocument();
|
||||||
|
expect(await screen.findByText('Local')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await user.clear(addressInput);
|
||||||
|
await user.type(addressInput, '10.10.');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText(
|
await screen.findByText('未发现匹配资产,请手动输入'),
|
||||||
'当前将直接使用 a.example.com 作为规则域名,无需再填写网站名。',
|
|
||||||
),
|
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user