import React from 'react'
import { render, fireEvent, waitFor } from '@testing-library/react-native'
import { View } from 'react-native'
import GenerationRecordScreen from './generationRecord'
jest.mock('expo-router', () => ({
useRouter: jest.fn(() => ({
back: jest.fn(),
push: jest.fn(),
})),
}))
jest.mock('react-i18next', () => ({
useTranslation: jest.fn(() => ({
t: (key: string) => key,
})),
}))
jest.mock('expo-status-bar', () => ({
StatusBar: 'StatusBar',
}))
jest.mock('react-native-safe-area-context', () => ({
SafeAreaView: ({ children }: { children: React.ReactNode }) => (
{children}
),
}))
jest.mock('expo-image', () => ({
Image: 'Image',
}))
jest.mock('@/components/icon', () => ({
LeftArrowIcon: () => null,
DeleteIcon: () => null,
EditIcon: () => null,
ChangeIcon: () => null,
WhiteStarIcon: () => null,
}))
jest.mock('@/components/ui/delete-confirm-dialog', () => ({
DeleteConfirmDialog: () => null,
}))
jest.mock('@/components/LoadingState', () => ({
__esModule: true,
default: ({ testID }: any) => ,
}))
jest.mock('@/components/ErrorState', () => ({
__esModule: true,
default: ({ testID }: any) => ,
}))
jest.mock('@/components/PaginationLoader', () => ({
__esModule: true,
default: ({ testID }: any) => ,
}))
// Mock react-native RefreshControl (directly from react-native)
jest.mock('react-native', () =>
Object.assign({}, jest.requireActual('react-native'), {
RefreshControl: ({ children }: { children: React.ReactNode }) => <>{children}>,
})
)
jest.mock('@/hooks', () => ({
useTemplateGenerations: jest.fn(),
}))
import { useTemplateGenerations } from '@/hooks'
const mockUseTemplateGenerations = useTemplateGenerations as jest.MockedFunction
describe('GenerationRecordScreen', () => {
const mockExecute = jest.fn()
const mockRefetch = jest.fn()
const mockLoadMore = jest.fn()
beforeEach(() => {
jest.clearAllMocks()
})
it('shows loading state on initial load', () => {
mockUseTemplateGenerations.mockReturnValue({
generations: [],
loading: true,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: true,
data: undefined,
})
const { getByTestId } = render()
expect(getByTestId('loading-state')).toBeTruthy()
})
it('shows error state when error occurs', () => {
mockUseTemplateGenerations.mockReturnValue({
generations: [],
loading: false,
loadingMore: false,
error: { message: 'Failed to load' } as any,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: false,
data: undefined,
})
const { getByTestId } = render()
expect(getByTestId('error-state')).toBeTruthy()
})
it('renders generation list when data is loaded', () => {
const mockGenerations = [
{
id: '1',
template: { title: 'Test Template' },
resultUrl: ['https://example.com/result.jpg'],
originalUrl: 'https://example.com/original.jpg',
},
]
mockUseTemplateGenerations.mockReturnValue({
generations: mockGenerations as any,
loading: false,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: true,
data: { data: mockGenerations } as any,
})
const { getByText } = render()
expect(getByText('Test Template')).toBeTruthy()
})
it('shows pagination loader when loading more', () => {
const mockGenerations = [
{
id: '1',
template: { title: 'Test Template' },
resultUrl: ['https://example.com/result.jpg'],
},
]
mockUseTemplateGenerations.mockReturnValue({
generations: mockGenerations as any,
loading: false,
loadingMore: true,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: true,
data: { data: mockGenerations } as any,
})
const { getByTestId } = render()
expect(getByTestId('pagination-loader')).toBeTruthy()
})
it('calls execute on mount', () => {
mockUseTemplateGenerations.mockReturnValue({
generations: [],
loading: false,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: false,
data: undefined,
})
render()
expect(mockExecute).toHaveBeenCalledWith({ page: 1, limit: 20 })
})
it('calls refetch on pull to refresh', async () => {
const mockGenerations = [
{
id: '1',
template: { title: 'Test Template' },
resultUrl: ['https://example.com/result.jpg'],
},
]
mockUseTemplateGenerations.mockReturnValue({
generations: mockGenerations as any,
loading: false,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: true,
data: { data: mockGenerations } as any,
})
const { getByTestId } = render()
const flatList = getByTestId('generation-list')
fireEvent(flatList, 'refresh')
await waitFor(() => {
expect(mockRefetch).toHaveBeenCalledWith({ page: 1, limit: 20 })
})
})
it('calls loadMore when reaching end of list', async () => {
const mockGenerations = [
{
id: '1',
template: { title: 'Test Template' },
resultUrl: ['https://example.com/result.jpg'],
},
]
mockUseTemplateGenerations.mockReturnValue({
generations: mockGenerations as any,
loading: false,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: true,
data: { data: mockGenerations } as any,
})
const { getByTestId } = render()
const flatList = getByTestId('generation-list')
fireEvent(flatList, 'endReached')
await waitFor(() => {
expect(mockLoadMore).toHaveBeenCalledWith({ limit: 20 })
})
})
it('does not call loadMore when hasMore is false', async () => {
const mockGenerations = [
{
id: '1',
template: { title: 'Test Template' },
resultUrl: ['https://example.com/result.jpg'],
},
]
mockUseTemplateGenerations.mockReturnValue({
generations: mockGenerations as any,
loading: false,
loadingMore: false,
error: null,
execute: mockExecute,
refetch: mockRefetch,
loadMore: mockLoadMore,
hasMore: false,
data: { data: mockGenerations } as any,
})
const { getByTestId } = render()
const flatList = getByTestId('generation-list')
fireEvent(flatList, 'endReached')
await waitFor(() => {
expect(mockLoadMore).not.toHaveBeenCalled()
})
})
})