merge: integrate remote learning module safely
This commit is contained in:
commit
f7171a471a
125 files changed
+11451
-12132
No files matched your search
@@ -32,6 +32,7 @@ function projectStatus(buildStatus: string, buildErrorCode: string | null = null
|
||||
app_id: 'space-cleaner',
|
||||
title: 'space-cleaner',
|
||||
summary: 'A project',
|
||||
status: 'draft',
|
||||
},
|
||||
latest_version: {
|
||||
id: 'version-1',
|
||||
@@ -62,6 +63,26 @@ async function advancePoll() {
|
||||
});
|
||||
}
|
||||
|
||||
async function submitProjectMetadata() {
|
||||
fetchCurrentWorksProjectStatusMock.mockRejectedValueOnce(Object.assign(
|
||||
new Error('project missing'),
|
||||
{ statusCode: 404 },
|
||||
));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await Promise.resolve();
|
||||
});
|
||||
fireEvent.change(screen.getByLabelText(/发布者姓名/), { target: { value: '小明' } });
|
||||
fireEvent.change(screen.getByLabelText(/发布者年龄/), { target: { value: '12' } });
|
||||
fireEvent.change(screen.getByLabelText(/项目简介/), { target: { value: '这是一个太空清洁小游戏。' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交审核' }));
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
describe('ProjectPublishAction', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
@@ -74,16 +95,16 @@ describe('ProjectPublishAction', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('submits automatic metadata and locks after Builder succeeds', async () => {
|
||||
it('creates a new project without a cover and locks after Builder succeeds', async () => {
|
||||
fetchCurrentWorksProjectStatusMock.mockResolvedValue(projectStatus('succeeded'));
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
expect(screen.getByRole('button', { name: '正在生成本次构建结果…' })).toBeDisabled();
|
||||
await submitProjectMetadata();
|
||||
expect(screen.getByRole('button', { name: '正在等待云端检查…' })).toBeDisabled();
|
||||
await flushSubmission();
|
||||
|
||||
expect(screen.getByTestId('project-publish-status')).toHaveTextContent(
|
||||
'本次构建结果已通过本地预览检查并上传,正在等待平台校验。',
|
||||
'无封面作品与本次构建结果已提交,正在等待平台校验。',
|
||||
);
|
||||
|
||||
expect(publishWorksProjectSourceMock).toHaveBeenCalledWith({
|
||||
@@ -91,22 +112,28 @@ describe('ProjectPublishAction', () => {
|
||||
project: {
|
||||
app_id: 'makelore-prj-space-cleaner',
|
||||
title: 'space-cleaner',
|
||||
summary: 'space-cleaner,由 Makelore 创建的作品。',
|
||||
summary: '这是一个太空清洁小游戏。',
|
||||
description: null,
|
||||
cover_url: null,
|
||||
creator_name: '小明',
|
||||
creator_age: 12,
|
||||
category: 'mini_game',
|
||||
age_band: null,
|
||||
difficulty: null,
|
||||
},
|
||||
});
|
||||
expect(fetchCurrentWorksProjectStatusMock).not.toHaveBeenCalled();
|
||||
expect(screen.queryByLabelText(/项目封面/)).not.toBeInTheDocument();
|
||||
expect(publishWorksProjectSourceMock.mock.calls[0]?.[0]).not.toHaveProperty('cover');
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledOnce();
|
||||
|
||||
await advancePoll();
|
||||
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledWith(
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenLastCalledWith(
|
||||
'makelore-prj-space-cleaner',
|
||||
);
|
||||
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('shows a friendly Builder failure without raw details', async () => {
|
||||
@@ -115,7 +142,7 @@ describe('ProjectPublishAction', () => {
|
||||
);
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
await advancePoll();
|
||||
|
||||
@@ -134,7 +161,7 @@ describe('ProjectPublishAction', () => {
|
||||
);
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
await advancePoll();
|
||||
|
||||
@@ -157,7 +184,7 @@ describe('ProjectPublishAction', () => {
|
||||
fetchCurrentWorksProjectStatusMock.mockResolvedValue(projectStatus('succeeded'));
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
|
||||
expect(screen.getByTestId('project-publish-binding-warning')).toHaveTextContent(
|
||||
@@ -167,7 +194,7 @@ describe('ProjectPublishAction', () => {
|
||||
|
||||
await advancePoll();
|
||||
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledOnce();
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledTimes(2);
|
||||
expect(screen.getByRole('button', { name: '已提交,等待运营审核' })).toBeDisabled();
|
||||
expect(screen.getByTestId('project-publish-binding-warning')).toBeVisible();
|
||||
});
|
||||
@@ -178,7 +205,7 @@ describe('ProjectPublishAction', () => {
|
||||
fetchCurrentWorksProjectStatusMock.mockResolvedValue(status);
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
await act(async () => {
|
||||
await vi.runAllTimersAsync();
|
||||
@@ -188,7 +215,7 @@ describe('ProjectPublishAction', () => {
|
||||
expect(failure).toHaveTextContent('构建时间比平时更长');
|
||||
expect(failure).toHaveTextContent('不需要重复提交');
|
||||
expect(screen.getByRole('button', { name: '已提交,请稍后查看' })).toBeDisabled();
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledTimes(300);
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledTimes(301);
|
||||
});
|
||||
|
||||
it('sanitizes local and HTTP failures before rendering', async () => {
|
||||
@@ -198,14 +225,31 @@ describe('ProjectPublishAction', () => {
|
||||
));
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
|
||||
const failure = screen.getByTestId('project-publish-failure');
|
||||
expect(failure).toHaveTextContent('发布服务暂时不可用');
|
||||
expect(failure).not.toHaveTextContent('Traceback');
|
||||
expect(failure).not.toHaveTextContent('INTERNAL_DATABASE_FAILURE');
|
||||
expect(fetchCurrentWorksProjectStatusMock).not.toHaveBeenCalled();
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('explains a first-create metadata race without claiming that existing metadata was saved', async () => {
|
||||
publishWorksProjectSourceMock.mockRejectedValue(Object.assign(
|
||||
new Error('private current metadata token=secret'),
|
||||
{ code: 'PROJECT_METADATA_CONFLICT', statusCode: 409 },
|
||||
));
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
|
||||
const failure = screen.getByTestId('project-publish-failure');
|
||||
expect(failure).toHaveTextContent('作品资料已发生变化');
|
||||
expect(failure).toHaveTextContent('本次没有提交版本');
|
||||
expect(failure).toHaveTextContent('重新点击提交审核');
|
||||
expect(failure).not.toHaveTextContent('token=secret');
|
||||
});
|
||||
|
||||
it('turns a local package error into instructions for the user', async () => {
|
||||
@@ -215,7 +259,7 @@ describe('ProjectPublishAction', () => {
|
||||
));
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
|
||||
const failure = screen.getByTestId('project-publish-failure');
|
||||
@@ -223,20 +267,109 @@ describe('ProjectPublishAction', () => {
|
||||
expect(failure).toHaveTextContent('请让开发助手修复当前项目模板');
|
||||
expect(failure).not.toHaveTextContent('D:/repo');
|
||||
expect(failure).not.toHaveTextContent('PROJECT_FILE_MISSING');
|
||||
expect(fetchCurrentWorksProjectStatusMock).not.toHaveBeenCalled();
|
||||
expect(fetchCurrentWorksProjectStatusMock).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('keeps generated metadata within the server limits', async () => {
|
||||
const longName = `作品-${'x'.repeat(250)}`;
|
||||
render(<ProjectPublishAction project={{ ...project, name: longName }} projectType="mini_program" />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await submitProjectMetadata();
|
||||
await flushSubmission();
|
||||
|
||||
const input = publishWorksProjectSourceMock.mock.calls[0]?.[0];
|
||||
expect(input.project.app_id).toBe('makelore-prj-space-cleaner');
|
||||
expect(input.project.title).toHaveLength(160);
|
||||
expect(input.project.summary).toBe(`${input.project.title},由 Makelore 创建的作品。`);
|
||||
expect(input.project.summary).toBe('这是一个太空清洁小游戏。');
|
||||
expect(input.project.creator_name).toBe('小明');
|
||||
expect(input.project.creator_age).toBe(12);
|
||||
expect(input.project.category).toBe('mini_program');
|
||||
});
|
||||
|
||||
it('submits an existing draft as version-only without editable metadata or replacement cover', async () => {
|
||||
const draftStatus = projectStatus('queued');
|
||||
draftStatus.project.title = '平台上的草稿标题';
|
||||
draftStatus.project.summary = '平台上的草稿简介';
|
||||
Object.assign(draftStatus.project, {
|
||||
cover_url: 'coverimage/draft.jpg',
|
||||
creator_name: '草稿作者',
|
||||
creator_age: 10,
|
||||
category: 'mini_game',
|
||||
});
|
||||
fetchCurrentWorksProjectStatusMock.mockResolvedValue(draftStatus);
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(screen.getByRole('heading', { name: '提交已有草稿的新版本' })).toBeVisible();
|
||||
expect(screen.getByText(/当前发布契约不支持安全修改已有作品资料/)).toBeVisible();
|
||||
expect(screen.getByText(/沿用平台现有作品资料与封面/)).toBeVisible();
|
||||
expect(screen.queryByLabelText(/项目名称/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/项目封面/)).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交新版本' }));
|
||||
await flushSubmission();
|
||||
|
||||
expect(publishWorksProjectSourceMock).toHaveBeenCalledWith({
|
||||
projectId: project.id,
|
||||
project: expect.objectContaining({
|
||||
app_id: 'space-cleaner',
|
||||
title: '平台上的草稿标题',
|
||||
summary: '平台上的草稿简介',
|
||||
cover_url: 'coverimage/draft.jpg',
|
||||
creator_name: '草稿作者',
|
||||
status: 'draft',
|
||||
}),
|
||||
});
|
||||
expect(publishWorksProjectSourceMock.mock.calls[0]?.[0]).not.toHaveProperty('cover');
|
||||
expect(screen.getByTestId('project-publish-status')).toHaveTextContent(
|
||||
'现有作品资料与封面保持不变',
|
||||
);
|
||||
});
|
||||
|
||||
it('submits a published project as a version without requesting replacement metadata or cover', async () => {
|
||||
const publishedStatus = projectStatus('succeeded');
|
||||
publishedStatus.project.status = 'published';
|
||||
publishedStatus.project.title = '已发布的太空清洁队';
|
||||
publishedStatus.project.summary = '平台上现有的简介';
|
||||
Object.assign(publishedStatus.project, {
|
||||
description: '平台上现有的详细介绍',
|
||||
cover_url: 'coverimage/existing.jpg',
|
||||
creator_name: '原作者',
|
||||
creator_age: 11,
|
||||
category: 'mini_game',
|
||||
age_band: '6-12岁',
|
||||
difficulty: '入门',
|
||||
});
|
||||
fetchCurrentWorksProjectStatusMock.mockResolvedValue(publishedStatus);
|
||||
render(<ProjectPublishAction project={project} projectType="mini_game" />);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '一键提交审核' }));
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(screen.getByRole('heading', { name: '提交已发布作品的新版本' })).toBeVisible();
|
||||
expect(screen.getByText(/现有名称、简介、作者信息和封面都会保持不变/)).toBeVisible();
|
||||
expect(screen.queryByLabelText(/项目封面/)).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交新版本' }));
|
||||
await flushSubmission();
|
||||
|
||||
expect(publishWorksProjectSourceMock).toHaveBeenCalledWith({
|
||||
projectId: project.id,
|
||||
project: expect.objectContaining({
|
||||
app_id: 'space-cleaner',
|
||||
title: '已发布的太空清洁队',
|
||||
summary: '平台上现有的简介',
|
||||
cover_url: 'coverimage/existing.jpg',
|
||||
creator_name: '原作者',
|
||||
}),
|
||||
});
|
||||
expect(publishWorksProjectSourceMock.mock.calls[0]?.[0]).not.toHaveProperty('cover');
|
||||
expect(screen.getByTestId('project-publish-status')).toHaveTextContent(
|
||||
'现有作品资料与封面保持不变',
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user