234 lines
6.3 KiB
TypeScript
234 lines
6.3 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const { requestJsonMock } = vi.hoisted(() => ({
|
|
requestJsonMock: vi.fn(),
|
|
}));
|
|
|
|
import {
|
|
clearRpgProfileBrowseHistory,
|
|
listRpgProfileBrowseHistory,
|
|
listRpgProfileSaveArchives,
|
|
resumeRpgProfileSaveArchive,
|
|
submitRpgProfileFeedback,
|
|
syncRpgProfileBrowseHistory,
|
|
upsertRpgProfileBrowseHistory,
|
|
} from './rpgProfileClient';
|
|
|
|
vi.mock('../apiClient', () => ({
|
|
BACKGROUND_AUTH_REQUEST_OPTIONS: {
|
|
authImpact: 'local',
|
|
skipRefresh: true,
|
|
notifyAuthStateChange: false,
|
|
clearAuthOnUnauthorized: false,
|
|
},
|
|
requestJson: requestJsonMock,
|
|
}));
|
|
|
|
describe('rpgProfileClient browse history routes', () => {
|
|
beforeEach(() => {
|
|
requestJsonMock.mockReset();
|
|
requestJsonMock.mockResolvedValue({ entries: [] });
|
|
});
|
|
|
|
it('reads browse history from the profile route', async () => {
|
|
await listRpgProfileBrowseHistory();
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/browse-history',
|
|
expect.objectContaining({ method: 'GET' }),
|
|
'读取浏览历史失败',
|
|
expect.objectContaining({
|
|
authImpact: 'local',
|
|
retry: expect.objectContaining({ maxRetries: 1 }),
|
|
skipRefresh: true,
|
|
notifyAuthStateChange: false,
|
|
clearAuthOnUnauthorized: false,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('writes browse history through the profile route', async () => {
|
|
await upsertRpgProfileBrowseHistory({
|
|
ownerUserId: 'user-1',
|
|
profileId: 'profile-1',
|
|
worldName: '测试世界',
|
|
subtitle: '测试副标题',
|
|
summaryText: '测试摘要',
|
|
coverImageSrc: null,
|
|
themeMode: 'mythic',
|
|
authorDisplayName: '测试作者',
|
|
});
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/browse-history',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
'写入浏览历史失败',
|
|
expect.objectContaining({
|
|
authImpact: 'local',
|
|
retry: expect.objectContaining({
|
|
maxRetries: 1,
|
|
retryUnsafeMethods: true,
|
|
}),
|
|
skipRefresh: true,
|
|
notifyAuthStateChange: false,
|
|
clearAuthOnUnauthorized: false,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('syncs browse history through the profile route', async () => {
|
|
await syncRpgProfileBrowseHistory([
|
|
{
|
|
ownerUserId: 'user-1',
|
|
profileId: 'profile-1',
|
|
worldName: '测试世界',
|
|
subtitle: '测试副标题',
|
|
summaryText: '测试摘要',
|
|
coverImageSrc: null,
|
|
themeMode: 'mythic',
|
|
authorDisplayName: '测试作者',
|
|
},
|
|
]);
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/browse-history',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
'同步浏览历史失败',
|
|
expect.any(Object),
|
|
);
|
|
});
|
|
|
|
it('clears browse history through the profile route', async () => {
|
|
await clearRpgProfileBrowseHistory();
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/browse-history',
|
|
expect.objectContaining({ method: 'DELETE' }),
|
|
'清空浏览历史失败',
|
|
expect.objectContaining({
|
|
retry: expect.objectContaining({
|
|
maxRetries: 1,
|
|
retryUnsafeMethods: true,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('rpgProfileClient save archive routes', () => {
|
|
beforeEach(() => {
|
|
requestJsonMock.mockReset();
|
|
requestJsonMock.mockResolvedValue({ entries: [] });
|
|
});
|
|
|
|
it('reads save archives from the profile route', async () => {
|
|
await listRpgProfileSaveArchives();
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/save-archives',
|
|
expect.objectContaining({ method: 'GET' }),
|
|
'读取存档列表失败',
|
|
expect.objectContaining({
|
|
authImpact: 'local',
|
|
retry: expect.objectContaining({ maxRetries: 1 }),
|
|
skipRefresh: true,
|
|
notifyAuthStateChange: false,
|
|
clearAuthOnUnauthorized: false,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('resumes a save archive through the profile route', async () => {
|
|
requestJsonMock.mockResolvedValueOnce({
|
|
entry: {
|
|
worldKey: 'custom:world-1',
|
|
},
|
|
snapshot: {
|
|
version: 2,
|
|
savedAt: '2026-04-19T10:15:00.000Z',
|
|
bottomTab: 'adventure',
|
|
currentStory: null,
|
|
gameState: {
|
|
worldType: 'CUSTOM',
|
|
},
|
|
},
|
|
});
|
|
|
|
await resumeRpgProfileSaveArchive('custom:world-1');
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/save-archives/custom%3Aworld-1',
|
|
expect.objectContaining({ method: 'POST' }),
|
|
'恢复存档失败',
|
|
expect.objectContaining({
|
|
retry: expect.objectContaining({
|
|
maxRetries: 1,
|
|
retryUnsafeMethods: true,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('rpgProfileClient feedback routes', () => {
|
|
beforeEach(() => {
|
|
requestJsonMock.mockReset();
|
|
requestJsonMock.mockResolvedValue({
|
|
feedback: {
|
|
feedbackId: 'feedback:user-1:1',
|
|
status: 'open',
|
|
createdAt: '2026-05-08T10:00:00Z',
|
|
evidenceItems: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
it('submits profile feedback through the profile route', async () => {
|
|
await submitRpgProfileFeedback({
|
|
description: '图片上传后没有展示预览',
|
|
contactPhone: null,
|
|
evidenceItems: [
|
|
{
|
|
fileName: 'preview.png',
|
|
contentType: 'image/png',
|
|
sizeBytes: 128,
|
|
dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=',
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(requestJsonMock).toHaveBeenCalledWith(
|
|
'/api/profile/feedback',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
'提交反馈失败',
|
|
expect.objectContaining({
|
|
retry: expect.objectContaining({
|
|
maxRetries: 1,
|
|
retryUnsafeMethods: true,
|
|
}),
|
|
}),
|
|
);
|
|
expect(JSON.parse(requestJsonMock.mock.calls[0][1].body)).toEqual({
|
|
description: '图片上传后没有展示预览',
|
|
contactPhone: null,
|
|
evidenceItems: [
|
|
{
|
|
fileName: 'preview.png',
|
|
contentType: 'image/png',
|
|
sizeBytes: 128,
|
|
dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
});
|