Files
Genarrative/apps/admin-web/src/pages/AdminGameDistributionReviewPage.test.tsx
T
kdletters 093f832ef9
Project CI / AI game creator shell Rust crates (push) Successful in 1m29s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m2s
Project CI / Backend tests (push) Successful in 3m48s
Project CI / Frontend tests (push) Successful in 2m4s
Project CI / Native shell tests (push) Successful in 5m57s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m15s
Project CI / Repository checks (push) Successful in 2m10s
Project CI / AI game creator shell web tests (push) Successful in 1m35s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m38s
优化后台游戏审核操作弹窗
移除游戏审核页的发行地址提示

将拒绝理由与下架原因改为点击按钮后输入

补充审核与安全下架交互测试
2026-09-23 22:50:36 +08:00

233 lines
7.2 KiB
TypeScript

/* @vitest-environment jsdom */
import {
fireEvent,
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import { beforeEach, expect, test, vi } from 'vitest';
import {
listAdminGameDistributionReviews,
reviewAdminGameDistributionVersion,
suspendAdminGameDistributionGame,
} from '../api/adminApiClient';
import type { AdminGameDistributionReviewEntry } from '../api/adminApiTypes';
import { AdminGameDistributionReviewPage } from './AdminGameDistributionReviewPage';
vi.mock('../api/adminApiClient', () => ({
isAdminApiError: vi.fn(
(error: unknown) =>
typeof error === 'object' &&
error !== null &&
'status' in error &&
typeof error.status === 'number',
),
formatAdminApiError: vi.fn((error: unknown) =>
error instanceof Error ? error.message : '请求失败',
),
listAdminGameDistributionReviews: vi.fn(),
reviewAdminGameDistributionVersion: vi.fn(),
suspendAdminGameDistributionGame: vi.fn(),
}));
const entry: AdminGameDistributionReviewEntry = {
versionId: 'version-1',
gameId: 'game_1',
versionNumber: 2,
packageSha256: 'a'.repeat(64),
packageBytes: 2048,
status: 'pending_review',
publicationRevision: 4,
reviewReason: null,
createdAt: '2026-09-20T08:00:00Z',
updatedAt: '2026-09-20T08:00:00Z',
};
beforeEach(() => {
vi.mocked(listAdminGameDistributionReviews).mockReset();
vi.mocked(reviewAdminGameDistributionVersion).mockReset();
vi.mocked(suspendAdminGameDistributionGame).mockReset();
vi.mocked(listAdminGameDistributionReviews).mockResolvedValue({
entries: [entry],
nextCursor: null,
});
});
test('通过审核只提交当前 publicationRevision 并刷新列表', async () => {
vi.mocked(reviewAdminGameDistributionVersion).mockResolvedValue({
version: { ...entry, status: 'published' },
replayed: false,
});
render(
<AdminGameDistributionReviewPage
token="admin-token"
onUnauthorized={vi.fn()}
/>,
);
await screen.findByText('game_1');
expect(screen.queryByLabelText('发行入口')).toBeNull();
expect(screen.queryByText('通过后由系统分配发行地址')).toBeNull();
expect(screen.queryByLabelText('拒绝理由')).toBeNull();
expect(screen.queryByLabelText('下架原因')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '通过' }));
await waitFor(() =>
expect(reviewAdminGameDistributionVersion).toHaveBeenCalledTimes(1),
);
const [token, versionId, idempotencyKey, payload] =
vi.mocked(reviewAdminGameDistributionVersion).mock.calls[0] ?? [];
expect(token).toBe('admin-token');
expect(versionId).toBe('version-1');
expect(String(idempotencyKey)).toContain('version-1');
expect(payload).toEqual({
decision: 'approve',
expectedPublicationRevision: 4,
});
await waitFor(() =>
expect(vi.mocked(listAdminGameDistributionReviews)).toHaveBeenCalledTimes(
2,
),
);
});
test('点击拒绝后填写理由再提交审核接口', async () => {
vi.mocked(reviewAdminGameDistributionVersion).mockResolvedValue({
version: { ...entry, status: 'rejected', reviewReason: '运行时报错' },
replayed: false,
});
render(
<AdminGameDistributionReviewPage
token="admin-token"
onUnauthorized={vi.fn()}
/>,
);
await screen.findByText('game_1');
fireEvent.click(screen.getByRole('button', { name: '拒绝' }));
const dialog = await screen.findByRole('dialog');
const reasonInput = within(dialog).getByRole('textbox', {
name: '拒绝理由',
});
fireEvent.click(within(dialog).getByRole('button', { name: '确认拒绝' }));
expect(await within(dialog).findByText('拒绝审核必须填写理由')).toBeTruthy();
expect(reviewAdminGameDistributionVersion).not.toHaveBeenCalled();
fireEvent.change(reasonInput, { target: { value: '运行时报错' } });
fireEvent.click(within(dialog).getByRole('button', { name: '确认拒绝' }));
await waitFor(() =>
expect(reviewAdminGameDistributionVersion).toHaveBeenCalledTimes(1),
);
const [, versionId, , payload] =
vi.mocked(reviewAdminGameDistributionVersion).mock.calls[0] ?? [];
expect(versionId).toBe('version-1');
expect(payload).toEqual({
decision: 'reject',
expectedPublicationRevision: 4,
reviewReason: '运行时报错',
});
});
test('安全下架需要先填写原因,再二次确认并携带公开修订号', async () => {
vi.mocked(suspendAdminGameDistributionGame).mockResolvedValue({
game: {
id: 'game_1',
title: '测试游戏',
status: 'suspended',
publicationRevision: 5,
},
replayed: false,
});
render(
<AdminGameDistributionReviewPage
token="admin-token"
onUnauthorized={vi.fn()}
/>,
);
await screen.findByText('game_1');
fireEvent.click(screen.getByRole('button', { name: '安全下架' }));
const reasonDialog = await screen.findByRole('dialog');
fireEvent.change(
within(reasonDialog).getByRole('textbox', { name: '下架原因' }),
{
target: { value: '盗用素材' },
},
);
fireEvent.click(
within(reasonDialog).getByRole('button', { name: '继续下架' }),
);
await screen.findByText('确认操作');
const confirmDialog = screen.getByRole('dialog');
fireEvent.click(within(confirmDialog).getByRole('button', { name: '确认' }));
await waitFor(() =>
expect(suspendAdminGameDistributionGame).toHaveBeenCalledTimes(1),
);
const [token, gameId, idempotencyKey, payload] =
vi.mocked(suspendAdminGameDistributionGame).mock.calls[0] ?? [];
expect(token).toBe('admin-token');
expect(gameId).toBe('game_1');
expect(String(idempotencyKey)).toContain('game_1');
expect(payload).toEqual({
expectedPublicationRevision: 4,
reason: '盗用素材',
});
expect(await screen.findByText(/已安全下架/u)).toBeTruthy();
});
test('取消理由输入时不做审核操作', async () => {
render(
<AdminGameDistributionReviewPage
token="admin-token"
onUnauthorized={vi.fn()}
/>,
);
await screen.findByText('game_1');
fireEvent.click(screen.getByRole('button', { name: '拒绝' }));
const dialog = await screen.findByRole('dialog');
fireEvent.click(within(dialog).getByRole('button', { name: '取消' }));
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
expect(reviewAdminGameDistributionVersion).not.toHaveBeenCalled();
});
test('取消安全下架确认时不下架', async () => {
render(
<AdminGameDistributionReviewPage
token="admin-token"
onUnauthorized={vi.fn()}
/>,
);
await screen.findByText('game_1');
fireEvent.click(screen.getByRole('button', { name: '安全下架' }));
const reasonDialog = await screen.findByRole('dialog');
fireEvent.change(
within(reasonDialog).getByRole('textbox', { name: '下架原因' }),
{
target: { value: '盗用素材' },
},
);
fireEvent.click(
within(reasonDialog).getByRole('button', { name: '继续下架' }),
);
await screen.findByText('确认操作');
const confirmDialog = screen.getByRole('dialog');
fireEvent.click(within(confirmDialog).getByRole('button', { name: '取消' }));
await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
expect(suspendAdminGameDistributionGame).not.toHaveBeenCalled();
});