feat: require cover for first project submission

This commit is contained in:
brother7 committed 2026-08-17 21:27:34 +08:00
1 parent 7e8d9e3811
commit 145a6ce571
7 files changed
+318 -121

No files matched your search

+51 -6
View File
@@ -63,7 +63,9 @@ async function advancePoll() {
});
}
async function submitProjectMetadata() {
const pngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1]);
async function submitProjectMetadata({ includeCover = true }: { includeCover?: boolean } = {}) {
fetchCurrentWorksProjectStatusMock.mockRejectedValueOnce(Object.assign(
new Error('project missing'),
{ statusCode: 404 },
@@ -75,6 +77,17 @@ async function submitProjectMetadata() {
fireEvent.change(screen.getByLabelText(/发布者姓名/), { target: { value: '小明' } });
fireEvent.change(screen.getByLabelText(/发布者年龄/), { target: { value: '12' } });
fireEvent.change(screen.getByLabelText(/项目简介/), { target: { value: '这是一个太空清洁小游戏。' } });
if (includeCover) {
fireEvent.change(screen.getByLabelText(/项目封面/), {
target: { files: [new File([pngBytes], 'space-cover.png', { type: 'image/png' })] },
});
await act(async () => {
await Promise.resolve();
});
expect(screen.getByText('space-cover.png')).toBeVisible();
expect(screen.getByAltText('项目封面预览')).toBeVisible();
expect(screen.getByText('重新选择')).toBeVisible();
}
fireEvent.click(screen.getByRole('button', { name: '提交审核' }));
await act(async () => {
await Promise.resolve();
@@ -95,7 +108,7 @@ describe('ProjectPublishAction', () => {
vi.useRealTimers();
});
it('creates a new project without a cover and locks after Builder succeeds', async () => {
it('creates a new project with a cover DTO and locks after Builder succeeds', async () => {
fetchCurrentWorksProjectStatusMock.mockResolvedValue(projectStatus('succeeded'));
render(<ProjectPublishAction project={project} projectType="mini_game" />);
@@ -104,7 +117,7 @@ describe('ProjectPublishAction', () => {
await flushSubmission();
expect(screen.getByTestId('project-publish-status')).toHaveTextContent(
'无封面作品与本次构建结果已提交,正在等待平台校验。',
'作品封面与本次构建结果已提交,正在等待平台校验。',
);
expect(publishWorksProjectSourceMock).toHaveBeenCalledWith({
@@ -121,9 +134,12 @@ describe('ProjectPublishAction', () => {
age_band: null,
difficulty: null,
},
cover: {
fileName: 'space-cover.png',
mimeType: 'image/png',
dataBase64: 'iVBORw0KGgoB',
},
});
expect(screen.queryByLabelText(/项目封面/)).not.toBeInTheDocument();
expect(publishWorksProjectSourceMock.mock.calls[0]?.[0]).not.toHaveProperty('cover');
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledOnce();
await advancePoll();
@@ -133,7 +149,36 @@ describe('ProjectPublishAction', () => {
);
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledTimes(2);
expect(screen.getByRole('button', { name: '已提交,等待运营审核' })).toBeDisabled();
expect(screen.getByTestId('project-publish-status')).toHaveTextContent('无封面作品已提交');
expect(screen.getByTestId('project-publish-status')).toHaveTextContent('作品与封面已提交');
});
it('blocks first submission until a cover is selected', async () => {
render(<ProjectPublishAction project={project} projectType="mini_game" />);
await submitProjectMetadata({ includeCover: false });
expect(screen.getByRole('alert')).toHaveTextContent('首次提交必须上传');
expect(publishWorksProjectSourceMock).not.toHaveBeenCalled();
expect(screen.getByRole('dialog')).toBeVisible();
});
it('rejects unsupported and oversized cover files before confirmation', async () => {
fetchCurrentWorksProjectStatusMock.mockRejectedValue(Object.assign(new Error('missing'), { statusCode: 404 }));
render(<ProjectPublishAction project={project} projectType="mini_game" />);
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
await Promise.resolve();
});
const input = screen.getByLabelText(/项目封面/);
fireEvent.change(input, { target: { files: [new File(['text'], 'cover.gif', { type: 'image/gif' })] } });
expect(screen.getByRole('alert')).toHaveTextContent('仅支持 PNG、JPEG 或 WebP');
fireEvent.change(input, {
target: { files: [new File([new Uint8Array(10 * 1024 * 1024 + 1)], 'huge.png', { type: 'image/png' })] },
});
expect(screen.getByRole('alert')).toHaveTextContent('不能超过 10 MiB');
expect(publishWorksProjectSourceMock).not.toHaveBeenCalled();
});
it('shows a friendly Builder failure without raw details', async () => {