fix: 修复表单相册上传失败bug,优化FormData构造方式
- 移除uploadFile.ts中不必要的Platform判断逻辑 - 保持原始URI不做修改,让React Native底层处理平台差异 - 添加uploadFile单元测试,覆盖主要上传场景 - 简化代码结构,提高可维护性 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cd1a4f6841
commit
c5641c1d3c
|
|
@ -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(
|
||||
<SafeAreaProvider initialMetrics={{
|
||||
frame: { x: 0, y: 0, width: 375, height: 812 },
|
||||
insets: { top: 44, left: 0, right: 0, bottom: 34 },
|
||||
}}>
|
||||
{component}
|
||||
</SafeAreaProvider>
|
||||
)
|
||||
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(<HomeScreen />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('1234')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
|
||||
{/* 标题栏 */}
|
||||
<TitleBar
|
||||
points={balance}
|
||||
onPointsPress={handlePointsPress}
|
||||
onSearchPress={handleSearchPress}
|
||||
onLayout={handleTitleBarLayout}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
import { uploadFile } from '../uploadFile'
|
||||
import { root } from '@repo/core'
|
||||
import { FileController } from '@repo/sdk'
|
||||
import { handleError } from '@/hooks/use-error'
|
||||
|
||||
jest.mock('@repo/core')
|
||||
jest.mock('@repo/sdk')
|
||||
jest.mock('@/hooks/use-error')
|
||||
|
||||
describe('uploadFile', () => {
|
||||
const mockUploadS3 = jest.fn()
|
||||
const mockHandleError = handleError as jest.MockedFunction<typeof handleError>
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
|
@ -6,13 +6,12 @@ import { handleError } from '@/hooks/use-error'
|
|||
|
||||
export async function uploadFile(params: { uri: string; mimeType?: string; fileName?: string }): Promise<string> {
|
||||
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))
|
||||
|
|
|
|||
Loading…
Reference in New Issue