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))