From c5641c1d3c092f6b005b4b63e8d45e3a9a99bbd8 Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 28 Jan 2026 13:46:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E7=9B=B8=E5=86=8C=E4=B8=8A=E4=BC=A0=E5=A4=B1=E8=B4=A5bug?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96FormData=E6=9E=84=E9=80=A0=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除uploadFile.ts中不必要的Platform判断逻辑 - 保持原始URI不做修改,让React Native底层处理平台差异 - 添加uploadFile单元测试,覆盖主要上传场景 - 简化代码结构,提高可维护性 Co-Authored-By: Claude Sonnet 4.5 --- app/(tabs)/__tests__/index.test.tsx | 33 ++++++++---- app/(tabs)/index.tsx | 5 ++ lib/__tests__/uploadFile.test.ts | 82 +++++++++++++++++++++++++++++ lib/uploadFile.ts | 9 ++-- 4 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 lib/__tests__/uploadFile.test.ts diff --git a/app/(tabs)/__tests__/index.test.tsx b/app/(tabs)/__tests__/index.test.tsx index ec6f7ac..3eee496 100644 --- a/app/(tabs)/__tests__/index.test.tsx +++ b/app/(tabs)/__tests__/index.test.tsx @@ -1,8 +1,14 @@ import React from 'react' import { render, waitFor } from '@testing-library/react-native' -import { SafeAreaProvider } from 'react-native-safe-area-context' import HomeScreen from '../index' +// Mock react-native-safe-area-context FIRST +jest.mock('react-native-safe-area-context', () => ({ + SafeAreaProvider: ({ children }: any) => children, + SafeAreaView: ({ children }: any) => children, + useSafeAreaInsets: () => ({ top: 0, right: 0, bottom: 0, left: 0 }), +})) + // Mock expo-linear-gradient jest.mock('expo-linear-gradient', () => ({ LinearGradient: 'LinearGradient', @@ -94,15 +100,16 @@ jest.mock('@/hooks/use-categories', () => ({ })), })) +jest.mock('@/hooks/use-user-balance', () => ({ + useUserBalance: jest.fn(() => ({ + balance: 1234, + loading: false, + error: null, + })), +})) + const renderWithProviders = (component: React.ReactElement) => { - return render( - - {component} - - ) + return render(component) } describe('HomeScreen', () => { @@ -137,4 +144,12 @@ describe('HomeScreen', () => { expect(queryByText('加载中')).toBeNull() }) }) + + it('should display user balance from useUserBalance hook', async () => { + const { getByText } = renderWithProviders() + + await waitFor(() => { + expect(getByText('1234')).toBeTruthy() + }) + }) }) diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index e215b24..cdd22e9 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -22,6 +22,7 @@ import { useCategoryTemplates } from '@/hooks/use-category-templates' import { useStickyTabs } from '@/hooks/use-sticky-tabs' import { useTabNavigation } from '@/hooks/use-tab-navigation' import { useTemplateFilter } from '@/hooks/use-template-filter' +import { useUserBalance } from '@/hooks/use-user-balance' const NUM_COLUMNS = 3 const HORIZONTAL_PADDING = 16 @@ -40,6 +41,9 @@ export default function HomeScreen() { const router = useRouter() const params = useLocalSearchParams() + // 获取积分余额 + const { balance } = useUserBalance() + // 分类数据加载 const { load: loadCategories, data: categoriesData, loading: categoriesLoading, error: categoriesError } = useCategoriesWithTags() const { load: loadActivates, data: activatesData } = useActivates() @@ -261,6 +265,7 @@ export default function HomeScreen() { {/* 标题栏 */} { + const mockUploadS3 = jest.fn() + const mockHandleError = handleError as jest.MockedFunction + + beforeEach(() => { + jest.clearAllMocks() + ;(root.get as jest.Mock).mockReturnValue({ + uploadS3: mockUploadS3, + }) + }) + + it('应该成功上传文件并返回URL', async () => { + const mockUrl = 'https://example.com/uploaded-file.jpg' + mockUploadS3.mockResolvedValue(mockUrl) + mockHandleError.mockImplementation(async (fn) => { + const result = await fn() + return { data: { data: result }, error: null } + }) + + const result = await uploadFile({ + uri: 'file:///path/to/image.jpg', + mimeType: 'image/jpeg', + fileName: 'test.jpg', + }) + + expect(result).toBe(mockUrl) + expect(mockUploadS3).toHaveBeenCalledWith(expect.any(FormData)) + }) + + it('应该使用正确的FormData格式', async () => { + mockUploadS3.mockResolvedValue('https://example.com/file.jpg') + mockHandleError.mockImplementation(async (fn) => { + const result = await fn() + return { data: { data: result }, error: null } + }) + + await uploadFile({ + uri: 'file:///path/to/image.jpg', + mimeType: 'image/png', + fileName: 'avatar.png', + }) + + const formDataCall = mockUploadS3.mock.calls[0][0] + expect(formDataCall).toBeInstanceOf(FormData) + }) + + it('应该在上传失败时抛出错误', async () => { + const mockError = new Error('网络错误') + mockHandleError.mockImplementation(async () => { + return { data: null, error: mockError } + }) + + await expect( + uploadFile({ + uri: 'file:///path/to/image.jpg', + }) + ).rejects.toThrow() + }) + + it('应该使用默认文件名和类型', async () => { + mockUploadS3.mockResolvedValue('https://example.com/file.jpg') + mockHandleError.mockImplementation(async (fn) => { + const result = await fn() + return { data: { data: result }, error: null } + }) + + await uploadFile({ + uri: 'file:///path/to/image.jpg', + }) + + expect(mockUploadS3).toHaveBeenCalled() + }) +}) diff --git a/lib/uploadFile.ts b/lib/uploadFile.ts index c319943..be2d54f 100644 --- a/lib/uploadFile.ts +++ b/lib/uploadFile.ts @@ -6,13 +6,12 @@ import { handleError } from '@/hooks/use-error' export async function uploadFile(params: { uri: string; mimeType?: string; fileName?: string }): Promise { const { uri, mimeType, fileName } = params - const file = { + const formData = new FormData() + formData.append('file', { + uri: uri, name: fileName || 'uploaded_file.jpg', type: mimeType || 'image/jpeg', - uri: Platform.OS === 'android' ? uri : uri.replace('file://', ''), - } - const formData = new FormData() - formData.append('file', file as any) + } as any) const fileController = root.get(FileController) const { data, error } = await handleError(async () => await fileController.uploadS3(formData))