test: restore project verification suite
This commit is contained in:
@@ -6,6 +6,7 @@ export const USER_PROFILE_STORAGE_KEY = 'user_profile';
|
||||
export const ADMIN_PROFILE_STORAGE_KEY = 'admin_profile';
|
||||
export const LEGACY_USER_PROFILE_STORAGE_KEY = USER_PROFILE_STORAGE_KEY;
|
||||
export const FIRST_USE_DOC_REMINDER_KEY = 'boosapi_first_use_docs_reminder_seen_v1';
|
||||
export const LEGACY_FIRST_USE_DOC_REMINDER_KEY = 'metapi_first_use_docs_reminder_seen_v1';
|
||||
|
||||
type StorageLike = {
|
||||
getItem?: (key: string) => string | null;
|
||||
@@ -28,4 +29,5 @@ export function clearAppInstallationState(storage?: StorageLike | null): void {
|
||||
target.removeItem(USER_PROFILE_STORAGE_KEY);
|
||||
target.removeItem(ADMIN_PROFILE_STORAGE_KEY);
|
||||
target.removeItem(FIRST_USE_DOC_REMINDER_KEY);
|
||||
target.removeItem(LEGACY_FIRST_USE_DOC_REMINDER_KEY);
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ export function useIsMobile(breakpoint = MOBILE_BREAKPOINT) {
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
const targetWindow = window;
|
||||
const query = breakpoint === MOBILE_BREAKPOINT
|
||||
? MOBILE_MEDIA_QUERY
|
||||
: `(max-width: ${breakpoint}px)`;
|
||||
const media = typeof window.matchMedia === 'function' ? window.matchMedia(query) : null;
|
||||
const update = () => setIsMobile(media ? media.matches : window.innerWidth <= breakpoint);
|
||||
const media = typeof targetWindow.matchMedia === 'function' ? targetWindow.matchMedia(query) : null;
|
||||
const update = () => setIsMobile(media ? media.matches : targetWindow.innerWidth <= breakpoint);
|
||||
update();
|
||||
|
||||
if (media && typeof media.addEventListener === 'function') {
|
||||
@@ -24,14 +25,14 @@ export function useIsMobile(breakpoint = MOBILE_BREAKPOINT) {
|
||||
};
|
||||
}
|
||||
|
||||
const addResizeListener = typeof window.addEventListener === 'function';
|
||||
const removeResizeListener = typeof window.removeEventListener === 'function';
|
||||
const addResizeListener = typeof targetWindow.addEventListener === 'function';
|
||||
const removeResizeListener = typeof targetWindow.removeEventListener === 'function';
|
||||
if (!addResizeListener) return;
|
||||
|
||||
window.addEventListener('resize', update);
|
||||
targetWindow.addEventListener('resize', update);
|
||||
return () => {
|
||||
if (removeResizeListener) {
|
||||
window.removeEventListener('resize', update);
|
||||
targetWindow.removeEventListener('resize', update);
|
||||
}
|
||||
};
|
||||
}, [breakpoint]);
|
||||
|
||||
@@ -13,6 +13,7 @@ const { apiMock } = vi.hoisted(() => ({
|
||||
getAccounts: vi.fn(),
|
||||
getAccountsSnapshot: vi.fn(),
|
||||
getAccountTokens: vi.fn(),
|
||||
getSites: vi.fn(),
|
||||
getDownstreamApiKeyOverview: vi.fn(),
|
||||
getDownstreamApiKeyTrend: vi.fn(),
|
||||
createDownstreamApiKey: vi.fn(),
|
||||
@@ -166,6 +167,10 @@ beforeEach(() => {
|
||||
},
|
||||
},
|
||||
]);
|
||||
apiMock.getSites.mockResolvedValue([
|
||||
{ id: 201, name: '站点A', status: 'active' },
|
||||
{ id: 202, name: '站点B', status: 'active' },
|
||||
]);
|
||||
apiMock.getAccountTokens.mockResolvedValue([
|
||||
{
|
||||
id: 301,
|
||||
|
||||
@@ -214,7 +214,7 @@ describe('ImportExport', () => {
|
||||
success: true,
|
||||
config: {
|
||||
enabled: true,
|
||||
fileUrl: 'https://dav.example.com/backups/metapi.json',
|
||||
fileUrl: 'https://dav.example.com/backups/boosapi.json',
|
||||
username: 'alice',
|
||||
exportType: 'all',
|
||||
autoSyncEnabled: true,
|
||||
@@ -231,7 +231,7 @@ describe('ImportExport', () => {
|
||||
success: true,
|
||||
config: {
|
||||
enabled: true,
|
||||
fileUrl: 'https://dav.example.com/backups/metapi.json',
|
||||
fileUrl: 'https://dav.example.com/backups/boosapi.json',
|
||||
username: 'alice',
|
||||
exportType: 'all',
|
||||
autoSyncEnabled: true,
|
||||
@@ -246,7 +246,7 @@ describe('ImportExport', () => {
|
||||
});
|
||||
apiMock.exportBackupToWebdav.mockResolvedValue({
|
||||
success: true,
|
||||
fileUrl: 'https://dav.example.com/backups/metapi.json',
|
||||
fileUrl: 'https://dav.example.com/backups/boosapi.json',
|
||||
exportType: 'all',
|
||||
});
|
||||
apiMock.importBackupFromWebdav.mockResolvedValue({
|
||||
@@ -459,7 +459,7 @@ describe('ImportExport', () => {
|
||||
|
||||
const fileUrlInput = root!.root.findAll((node) => (
|
||||
node.type === 'input'
|
||||
&& node.props.placeholder === 'https://dav.example.com/backups/metapi.json'
|
||||
&& node.props.placeholder === 'https://dav.example.com/backups/boosapi.json'
|
||||
)).at(-1);
|
||||
const cronInput = root!.root.findAll((node) => (
|
||||
node.type === 'input'
|
||||
@@ -507,7 +507,7 @@ describe('ImportExport', () => {
|
||||
|
||||
const fileUrlInput = root!.root.findAll((node) => (
|
||||
node.type === 'input'
|
||||
&& node.props.placeholder === 'https://dav.example.com/backups/metapi.json'
|
||||
&& node.props.placeholder === 'https://dav.example.com/backups/boosapi.json'
|
||||
)).at(-1);
|
||||
|
||||
expect(fileUrlInput).toBeTruthy();
|
||||
@@ -538,7 +538,7 @@ describe('ImportExport', () => {
|
||||
success: true,
|
||||
config: {
|
||||
enabled: true,
|
||||
fileUrl: 'https://dav.example.com/backups/metapi.json',
|
||||
fileUrl: 'https://dav.example.com/backups/boosapi.json',
|
||||
username: 'alice',
|
||||
exportType: 'all',
|
||||
autoSyncEnabled: true,
|
||||
|
||||
@@ -789,7 +789,7 @@ describe('Models marketplace text', () => {
|
||||
|
||||
expect(String(latencyBadge.props.className || '')).toContain('badge-muted');
|
||||
expect(collectText(latencyBadge)).toContain('延迟');
|
||||
expect(collectText(latencyBadge)).toContain('—');
|
||||
expect(collectText(latencyBadge)).toContain('暂无');
|
||||
expect(collectText(root!.root)).not.toContain('680ms');
|
||||
} finally {
|
||||
root?.unmount();
|
||||
|
||||
@@ -520,7 +520,7 @@ describe('ProxyLogs server-driven page', () => {
|
||||
});
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(globalThis.localStorage.setItem).toHaveBeenCalledWith('metapi.proxyLogs.debugTracePanelExpanded', 'false');
|
||||
expect(globalThis.localStorage.setItem).toHaveBeenCalledWith('boosapi.proxyLogs.debugTracePanelExpanded', 'false');
|
||||
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
@@ -547,7 +547,7 @@ describe('ProxyLogs server-driven page', () => {
|
||||
&& node.props['data-debug-trace-panel-body'] === true
|
||||
));
|
||||
|
||||
expect(globalThis.localStorage.getItem).toHaveBeenCalledWith('metapi.proxyLogs.debugTracePanelExpanded');
|
||||
expect(globalThis.localStorage.getItem).toHaveBeenCalledWith('boosapi.proxyLogs.debugTracePanelExpanded');
|
||||
expect(restoredToggleButton.props['aria-expanded']).toBe(false);
|
||||
expect(String(restoredPanelBody.props.className || '')).not.toContain('is-open');
|
||||
} finally {
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('Dashboard mobile layout', () => {
|
||||
|
||||
expect(source).toContain('import { useIsMobile } from "../components/useIsMobile.js";');
|
||||
expect(source).toContain('const isMobile = useIsMobile()');
|
||||
expect(source).toContain('gridTemplateColumns: isMobile ? "1fr" : "1fr 1fr"');
|
||||
expect(source).toContain('className="dashboard-chart-grid"');
|
||||
expect(source).toContain('gridTemplateColumns: isMobile ? "1fr" : "1fr 300px"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('modelTesterSession', () => {
|
||||
name: 'draft.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
dataUrl: 'data:application/pdf;base64,JVBERi0x',
|
||||
fileId: 'file-metapi-draft',
|
||||
fileId: 'file-boosapi-draft',
|
||||
status: 'uploaded',
|
||||
errorMessage: null,
|
||||
},
|
||||
@@ -243,7 +243,7 @@ describe('modelTesterSession', () => {
|
||||
it('creates user conversation messages that preserve uploaded file references', () => {
|
||||
const message = createConversationUserMessage('请总结附件', [
|
||||
{
|
||||
fileId: 'file-metapi-123',
|
||||
fileId: 'file-boosapi-123',
|
||||
filename: 'paper.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
@@ -254,7 +254,7 @@ describe('modelTesterSession', () => {
|
||||
expect(message.parts).toEqual([
|
||||
{
|
||||
type: 'input_file',
|
||||
fileId: 'file-metapi-123',
|
||||
fileId: 'file-boosapi-123',
|
||||
filename: 'paper.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
@@ -295,7 +295,7 @@ describe('modelTesterSession', () => {
|
||||
parts: [
|
||||
{
|
||||
type: 'input_file',
|
||||
fileId: 'file-metapi-456',
|
||||
fileId: 'file-boosapi-456',
|
||||
filename: 'appendix.txt',
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
@@ -304,7 +304,7 @@ describe('modelTesterSession', () => {
|
||||
|
||||
expect(files).toEqual([
|
||||
{
|
||||
fileId: 'file-metapi-456',
|
||||
fileId: 'file-boosapi-456',
|
||||
filename: 'appendix.txt',
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
@@ -322,7 +322,7 @@ describe('modelTesterSession', () => {
|
||||
const files = await resolveConversationReplayFiles?.(
|
||||
[
|
||||
{
|
||||
fileId: 'file-metapi-123',
|
||||
fileId: 'file-boosapi-123',
|
||||
filename: 'brief.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
@@ -331,10 +331,10 @@ describe('modelTesterSession', () => {
|
||||
loader,
|
||||
);
|
||||
|
||||
expect(loader).toHaveBeenCalledWith('file-metapi-123');
|
||||
expect(loader).toHaveBeenCalledWith('file-boosapi-123');
|
||||
expect(files).toEqual([
|
||||
{
|
||||
fileId: 'file-metapi-123',
|
||||
fileId: 'file-boosapi-123',
|
||||
filename: 'brief.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
data: 'data:application/pdf;base64,JVBERi0x',
|
||||
@@ -564,7 +564,7 @@ describe('modelTesterSession', () => {
|
||||
parts: [
|
||||
{
|
||||
type: 'input_file' as const,
|
||||
fileId: 'file-metapi-123',
|
||||
fileId: 'file-boosapi-123',
|
||||
filename: 'brief.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
data: 'data:application/pdf;base64,JVBERi0x',
|
||||
@@ -592,7 +592,7 @@ describe('modelTesterSession', () => {
|
||||
{
|
||||
type: 'file',
|
||||
file: {
|
||||
file_id: 'file-metapi-123',
|
||||
file_id: 'file-boosapi-123',
|
||||
filename: 'brief.pdf',
|
||||
mime_type: 'application/pdf',
|
||||
},
|
||||
@@ -625,7 +625,7 @@ describe('modelTesterSession', () => {
|
||||
{ type: 'input_text', text: 'reuse native file id' },
|
||||
{
|
||||
type: 'input_file',
|
||||
file_id: 'file-metapi-123',
|
||||
file_id: 'file-boosapi-123',
|
||||
filename: 'brief.pdf',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -105,7 +105,7 @@ describe('Settings model availability probe confirmation', () => {
|
||||
|
||||
const toggleLabel = root.root.find((node) => (
|
||||
node.type === 'label'
|
||||
&& collectText(node).includes('允许 metapi 后台主动批量测活')
|
||||
&& collectText(node).includes('允许 BoosAPI 后台主动批量测活')
|
||||
));
|
||||
const toggle = toggleLabel.findByType('input');
|
||||
expect(toggle.props.checked).toBe(false);
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('Settings proxy transport', () => {
|
||||
|
||||
const websocketToggleLabel = root.root.find((node) => (
|
||||
node.type === 'label'
|
||||
&& collectText(node).includes('允许 metapi 到 Codex 上游使用 WebSocket')
|
||||
&& collectText(node).includes('允许 BoosAPI 到 Codex 上游使用 WebSocket')
|
||||
));
|
||||
const websocketToggle = websocketToggleLabel.findByType('input');
|
||||
expect(websocketToggle.props.checked).toBe(false);
|
||||
|
||||
@@ -264,19 +264,19 @@ describe('Sites create redirect', () => {
|
||||
const platformSelect = selects.at(-1);
|
||||
expect(platformSelect?.props.options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ label: '阿里云 CodingPlan / OpenAI' }),
|
||||
expect.objectContaining({ label: '阿里云 CodingPlan / Claude' }),
|
||||
expect.objectContaining({ label: '智谱 Coding Plan / OpenAI' }),
|
||||
expect.objectContaining({ label: '智谱 Coding Plan / Claude' }),
|
||||
expect.objectContaining({ label: 'DeepSeek / OpenAI' }),
|
||||
expect.objectContaining({ label: 'DeepSeek / Claude' }),
|
||||
expect.objectContaining({ label: 'Moonshot(Kimi) / OpenAI' }),
|
||||
expect.objectContaining({ label: 'Moonshot(Kimi) / Claude' }),
|
||||
expect.objectContaining({ label: 'MiniMax / OpenAI' }),
|
||||
expect.objectContaining({ label: 'MiniMax / Claude' }),
|
||||
expect.objectContaining({ label: 'ModelScope / OpenAI' }),
|
||||
expect.objectContaining({ label: 'ModelScope / Claude' }),
|
||||
expect.objectContaining({ label: '豆包 Coding Plan / OpenAI' }),
|
||||
expect.objectContaining({ label: '阿里云 Coding Plan (OpenAI)' }),
|
||||
expect.objectContaining({ label: '阿里云 Coding Plan (Claude)' }),
|
||||
expect.objectContaining({ label: '智谱清言 Coding Plan (OpenAI)' }),
|
||||
expect.objectContaining({ label: '智谱清言 Coding Plan (Claude)' }),
|
||||
expect.objectContaining({ label: 'DeepSeek (OpenAI)' }),
|
||||
expect.objectContaining({ label: 'DeepSeek (Claude)' }),
|
||||
expect.objectContaining({ label: 'Moonshot (OpenAI)' }),
|
||||
expect.objectContaining({ label: 'Moonshot (Claude)' }),
|
||||
expect.objectContaining({ label: 'MiniMax (OpenAI)' }),
|
||||
expect.objectContaining({ label: 'MiniMax (Claude)' }),
|
||||
expect.objectContaining({ label: 'ModelScope (OpenAI)' }),
|
||||
expect.objectContaining({ label: 'ModelScope (Claude)' }),
|
||||
expect.objectContaining({ label: '豆包 Coding Plan (OpenAI)' }),
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
@@ -366,11 +366,12 @@ describe('Sites create redirect', () => {
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(urlInput.props.value).toBe('https://open.bigmodel.cn/api/coding/paas/v4');
|
||||
expect(JSON.stringify(root.toJSON())).toContain('智谱 Coding Plan / OpenAI');
|
||||
expect(JSON.stringify(root.toJSON())).toContain('智谱清言 Coding Plan (OpenAI)');
|
||||
|
||||
const presetAlerts = root.root.findAll((node) => (
|
||||
typeof node.props.className === 'string'
|
||||
&& node.props.className.includes('alert alert-info')
|
||||
&& collectText(node).includes('已应用官方预设')
|
||||
));
|
||||
expect(presetAlerts).toHaveLength(1);
|
||||
expect(collectText(presetAlerts[0]!)).toContain('已应用官方预设');
|
||||
@@ -645,7 +646,7 @@ describe('Sites create redirect', () => {
|
||||
));
|
||||
expect(platformSelect?.props.value).toBe('preset:deepseek-openai');
|
||||
expect(collectText(presetAlert)).toContain('已应用官方预设');
|
||||
expect(collectText(presetAlert)).toContain('DeepSeek / OpenAI');
|
||||
expect(collectText(presetAlert)).toContain('DeepSeek (OpenAI)');
|
||||
} finally {
|
||||
root?.unmount();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user