Files
Genarrative/apps/admin-web/src/pages/AdminGrayReleaseConfigPage.test.tsx
T
kdletters b72e2163d7
Project CI / Frontend tests (push) Successful in 29s
Project CI / Repository checks (push) Successful in 39s
Project CI / Native shell tests (push) Successful in 2m10s
Project CI / Backend tests (push) Failing after 2m50s
恢复后台灰度发布控制面
恢复灰度发布导航、权限、前端契约与页面编译链
解除灰度页面对旧创作入口配置的依赖
补充路由、API、页面回归测试及当前架构文档
2026-07-23 14:23:18 +08:00

220 lines
6.9 KiB
TypeScript

/* @vitest-environment jsdom */
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, expect, test, vi } from 'vitest';
import {
getAdminFeatureGateConfig,
upsertAdminFeatureGateConfig,
} from '../api/adminApiClient';
import type { AdminFeatureGateConfigResponse } from '../api/adminApiTypes';
import { AdminGrayReleaseConfigPage } from './AdminGrayReleaseConfigPage';
vi.mock('../api/adminApiClient', () => ({
formatAdminApiError: vi.fn((error: unknown) =>
error instanceof Error ? error.message : '请求失败',
),
getAdminFeatureGateConfig: vi.fn(),
isAdminApiError: vi.fn(() => false),
upsertAdminFeatureGateConfig: vi.fn(),
}));
const configResponse: AdminFeatureGateConfigResponse = {
gates: [
{
gateKey: 'editor.new-toolbar',
enabled: true,
rolloutPercent: 25,
allowUserIds: ['user-1'],
allowUserTags: ['beta'],
denyUserIds: ['blocked-1'],
description: '新版编辑器工具条',
updatedAt: '2026-07-07T01:00:00Z',
},
{
gateKey: 'image.generator.v2',
enabled: false,
rolloutPercent: 5,
allowUserIds: ['artist-1', 'artist-2'],
allowUserTags: ['internal', 'trial'],
denyUserIds: [],
description: '图片生成链路',
updatedAt: '2026-07-07T02:00:00Z',
},
],
};
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(getAdminFeatureGateConfig).mockResolvedValue(configResponse);
vi.mocked(upsertAdminFeatureGateConfig).mockResolvedValue(configResponse);
});
test('灰度发布页加载并展示 gate 列表', async () => {
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
expect(
await screen.findByRole('button', { name: 'editor.new-toolbar' }),
).toBeTruthy();
expect(
screen.getByRole('button', { name: 'image.generator.v2' }),
).toBeTruthy();
expect(screen.getByText('25%')).toBeTruthy();
expect(getAdminFeatureGateConfig).toHaveBeenCalledWith('admin-token');
});
test('灰度发布页可选择已有 gate 编辑', async () => {
const user = userEvent.setup();
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await user.click(
await screen.findByRole('button', { name: 'image.generator.v2' }),
);
expect((screen.getByLabelText('Gate Key') as HTMLInputElement).value).toBe(
'image.generator.v2',
);
expect((screen.getByLabelText('启用') as HTMLInputElement).checked).toBe(
false,
);
expect((screen.getByLabelText('灰度比例') as HTMLInputElement).value).toBe(
'5',
);
expect(
(screen.getByLabelText('允许用户 ID') as HTMLTextAreaElement).value,
).toBe('artist-1\nartist-2');
expect(
(screen.getByLabelText('允许用户标签') as HTMLTextAreaElement).value,
).toBe('internal\ntrial');
expect(
(screen.getByLabelText('拒绝用户 ID') as HTMLTextAreaElement).value,
).toBe('');
});
test('灰度发布页选择新 target 时重置旧 gate 规则', async () => {
const user = userEvent.setup();
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await user.click(
await screen.findByRole('button', { name: 'editor.new-toolbar' }),
);
await user.selectOptions(screen.getByLabelText('Gate Key 前缀'), [
'image-editor',
]);
expect((screen.getByLabelText('Gate Key') as HTMLInputElement).value).toBe(
'image-editor:agent-sidebar',
);
expect((screen.getByLabelText('启用') as HTMLInputElement).checked).toBe(
false,
);
expect((screen.getByLabelText('灰度比例') as HTMLInputElement).value).toBe(
'0',
);
expect(
(screen.getByLabelText('允许用户 ID') as HTMLTextAreaElement).value,
).toBe('');
expect(
(screen.getByLabelText('允许用户标签') as HTMLTextAreaElement).value,
).toBe('');
expect(
(screen.getByLabelText('拒绝用户 ID') as HTMLTextAreaElement).value,
).toBe('');
expect((screen.getByLabelText('描述') as HTMLTextAreaElement).value).toBe(
'画布 Agent 入口灰度',
);
});
test('灰度发布页可通过功能入口生成画布 Agent Gate Key', async () => {
const user = userEvent.setup();
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await screen.findByRole('button', { name: 'editor.new-toolbar' });
await user.selectOptions(screen.getByLabelText('Gate Key 前缀'), [
'image-editor',
]);
expect((screen.getByLabelText('Gate Key') as HTMLInputElement).value).toBe(
'image-editor:agent-sidebar',
);
expect(
(screen.getByLabelText('Gate Key 目标') as HTMLSelectElement).value,
).toBe('agent-sidebar');
expect((screen.getByLabelText('描述') as HTMLTextAreaElement).value).toBe(
'画布 Agent 入口灰度',
);
});
test('灰度发布页保存时转换数组和百分比', async () => {
const user = userEvent.setup();
vi.mocked(upsertAdminFeatureGateConfig).mockResolvedValueOnce({
gates: [
...configResponse.gates,
{
gateKey: 'homepage.feed-redesign',
enabled: true,
rolloutPercent: 42,
allowUserIds: ['user-a', 'user-b'],
allowUserTags: ['beta', 'staff'],
denyUserIds: ['blocked-a'],
description: '首页信息流',
updatedAt: '2026-07-07T03:00:00Z',
},
],
});
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await screen.findByRole('button', { name: 'editor.new-toolbar' });
fireEvent.change(screen.getByLabelText('Gate Key'), {
target: { value: 'homepage.feed-redesign' },
});
await user.click(screen.getByLabelText('启用'));
fireEvent.change(screen.getByLabelText('灰度比例'), {
target: { value: '42' },
});
fireEvent.change(screen.getByLabelText('允许用户 ID'), {
target: { value: ' user-a \n\n user-b ' },
});
fireEvent.change(screen.getByLabelText('允许用户标签'), {
target: { value: ' beta \n staff ' },
});
fireEvent.change(screen.getByLabelText('拒绝用户 ID'), {
target: { value: ' blocked-a \n ' },
});
fireEvent.change(screen.getByLabelText('描述'), {
target: { value: ' 首页信息流 ' },
});
await user.click(screen.getByRole('button', { name: '保存配置' }));
await user.click(screen.getByRole('button', { name: '确认' }));
await waitFor(() => {
expect(upsertAdminFeatureGateConfig).toHaveBeenCalledWith('admin-token', {
gateKey: 'homepage.feed-redesign',
enabled: true,
rolloutPercent: 42,
allowUserIds: ['user-a', 'user-b'],
allowUserTags: ['beta', 'staff'],
denyUserIds: ['blocked-a'],
description: '首页信息流',
});
});
});
test('灰度发布页无 token 时不请求配置', () => {
render(<AdminGrayReleaseConfigPage token="" onUnauthorized={vi.fn()} />);
expect(getAdminFeatureGateConfig).not.toHaveBeenCalled();
expect(upsertAdminFeatureGateConfig).not.toHaveBeenCalled();
});