This commit is contained in:
imeepos
2026-01-28 15:57:40 +08:00
parent 7d73cfbc3e
commit efd4aba8c1
12 changed files with 676 additions and 262 deletions

View File

@@ -35,6 +35,14 @@ jest.mock('@/components/icon', () => ({
jest.mock('@/components/ErrorState', () => 'ErrorState')
jest.mock('@/components/LoadingState', () => 'LoadingState')
// Mock home blocks
jest.mock('@/components/blocks/home', () => ({
TitleBar: 'TitleBar',
HeroSlider: 'HeroSlider',
TabNavigation: 'TabNavigation',
TemplateCard: 'TemplateCard',
}))
// Mock react-native RefreshControl (directly from react-native)
jest.mock('react-native', () =>
Object.assign({}, jest.requireActual('react-native'), {
@@ -108,6 +116,69 @@ jest.mock('@/hooks/use-user-balance', () => ({
})),
}))
jest.mock('@/hooks/use-categories-with-tags', () => ({
useCategoriesWithTags: jest.fn(() => ({
load: jest.fn(),
data: {
categories: [
{
id: 'cat1',
name: 'Test Category',
},
],
},
loading: false,
error: null,
})),
}))
jest.mock('@/hooks/use-category-templates', () => ({
useCategoryTemplates: jest.fn(() => ({
templates: [
{
id: 'template1',
title: 'Test Template',
webpPreviewUrl: 'https://example.com/preview.webp',
previewUrl: 'https://example.com/preview.jpg',
coverImageUrl: 'https://example.com/cover.png',
aspectRatio: '1:1',
},
],
loading: false,
loadingMore: false,
execute: jest.fn(),
loadMore: jest.fn(),
hasMore: false,
})),
}))
jest.mock('@/hooks/use-sticky-tabs', () => ({
useStickyTabs: jest.fn(() => ({
isSticky: false,
tabsHeight: 0,
titleBarHeightRef: { current: 0 },
handleScroll: jest.fn(),
handleTabsLayout: jest.fn(),
handleTitleBarLayout: jest.fn(),
})),
}))
jest.mock('@/hooks/use-tab-navigation', () => ({
useTabNavigation: jest.fn(() => ({
activeIndex: 0,
selectedCategoryId: 'cat1',
tabs: [{ id: 'cat1', name: 'Test Category' }],
selectTab: jest.fn(),
selectCategoryById: jest.fn(),
})),
}))
jest.mock('@/hooks/use-template-filter', () => ({
useTemplateFilter: jest.fn(({ templates }) => ({
filteredTemplates: templates,
})),
}))
const renderWithProviders = (component: React.ReactElement) => {
return render(component)
}
@@ -152,4 +223,132 @@ describe('HomeScreen', () => {
expect(getByText('1234')).toBeTruthy()
})
})
describe('Loading States', () => {
it('should show only one loading indicator when categories are loading', async () => {
const { useCategoriesWithTags } = require('@/hooks/use-categories-with-tags')
const { useCategoryTemplates } = require('@/hooks/use-category-templates')
useCategoriesWithTags.mockReturnValue({
load: jest.fn(),
data: null,
loading: true,
error: null,
})
useCategoryTemplates.mockReturnValue({
templates: [],
loading: false,
loadingMore: false,
execute: jest.fn(),
loadMore: jest.fn(),
hasMore: false,
})
const { UNSAFE_getAllByType } = renderWithProviders(<HomeScreen />)
await waitFor(() => {
// 应该只有一个 LoadingState 组件
const loadingStates = UNSAFE_getAllByType('LoadingState' as any)
expect(loadingStates.length).toBe(1)
})
})
it('should show only one loading indicator when templates are loading', async () => {
const { useCategoriesWithTags } = require('@/hooks/use-categories-with-tags')
const { useCategoryTemplates } = require('@/hooks/use-category-templates')
useCategoriesWithTags.mockReturnValue({
load: jest.fn(),
data: {
categories: [{ id: 'cat1', name: 'Test Category' }],
},
loading: false,
error: null,
})
useCategoryTemplates.mockReturnValue({
templates: [],
loading: true,
loadingMore: false,
execute: jest.fn(),
loadMore: jest.fn(),
hasMore: false,
})
const { UNSAFE_queryAllByType } = renderWithProviders(<HomeScreen />)
await waitFor(() => {
// 应该只有一个 ActivityIndicator
const activityIndicators = UNSAFE_queryAllByType('ActivityIndicator' as any)
expect(activityIndicators.length).toBeLessThanOrEqual(1)
})
})
it('should NOT show multiple loading indicators simultaneously', async () => {
const { useCategoriesWithTags } = require('@/hooks/use-categories-with-tags')
const { useCategoryTemplates } = require('@/hooks/use-category-templates')
// 模拟两个都在加载的情况
useCategoriesWithTags.mockReturnValue({
load: jest.fn(),
data: null,
loading: true,
error: null,
})
useCategoryTemplates.mockReturnValue({
templates: [],
loading: true,
loadingMore: false,
execute: jest.fn(),
loadMore: jest.fn(),
hasMore: false,
})
const { UNSAFE_queryAllByType } = renderWithProviders(<HomeScreen />)
await waitFor(() => {
// 即使两个都在加载,也应该只显示一个 loading
const loadingStates = UNSAFE_queryAllByType('LoadingState' as any)
const activityIndicators = UNSAFE_queryAllByType('ActivityIndicator' as any)
// 总共的 loading 指示器不应该超过 1 个
const totalLoadingIndicators = loadingStates.length + activityIndicators.length
expect(totalLoadingIndicators).toBeLessThanOrEqual(1)
})
})
it('should show unified loading state during initial load', async () => {
const { useCategoriesWithTags } = require('@/hooks/use-categories-with-tags')
const { useCategoryTemplates } = require('@/hooks/use-category-templates')
useCategoriesWithTags.mockReturnValue({
load: jest.fn(),
data: null,
loading: true,
error: null,
})
useCategoryTemplates.mockReturnValue({
templates: [],
loading: true,
loadingMore: false,
execute: jest.fn(),
loadMore: jest.fn(),
hasMore: false,
})
const { UNSAFE_queryAllByType } = renderWithProviders(<HomeScreen />)
await waitFor(() => {
// 应该只显示一个统一的 loading 状态
const allLoadingComponents = [
...UNSAFE_queryAllByType('LoadingState' as any),
...UNSAFE_queryAllByType('ActivityIndicator' as any),
]
expect(allLoadingComponents.length).toBe(1)
})
})
})
})