146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
import LoadMoreButton from '@/components/article/LoadMoreButton'
|
|
|
|
process.env.NEXT_PUBLIC_DIRECTUS_URL = 'https://cms.achmad.dev'
|
|
process.env.NEXT_PUBLIC_DIRECTUS_TOKEN = 'test-token'
|
|
|
|
const mockFetch = vi.fn()
|
|
global.fetch = mockFetch
|
|
|
|
beforeEach(() => {
|
|
vi.resetAllMocks()
|
|
mockFetch.mockResolvedValue({
|
|
json: () => Promise.resolve({ data: [] }),
|
|
})
|
|
})
|
|
|
|
describe('LoadMoreButton', () => {
|
|
it('renders the Load more button by default', () => {
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
expect(screen.getByText('Load more')).toBeInTheDocument()
|
|
})
|
|
|
|
it('hides the button when hasMore is false', () => {
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} hasMore={false} />)
|
|
expect(screen.queryByText('Load more')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('shows loading state while fetching', async () => {
|
|
mockFetch.mockImplementation(
|
|
() =>
|
|
new Promise((resolve) =>
|
|
setTimeout(
|
|
() =>
|
|
resolve({
|
|
json: () => Promise.resolve({ data: [] }),
|
|
}),
|
|
100,
|
|
),
|
|
),
|
|
)
|
|
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
expect(await screen.findByText('Loading…')).toBeInTheDocument()
|
|
})
|
|
|
|
it('fetches articles with correct parameters', async () => {
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
await waitFor(() => {
|
|
expect(mockFetch).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
const url = mockFetch.mock.calls[0][0] as string
|
|
expect(url).toContain('/items/articles')
|
|
expect(url).toContain('games')
|
|
expect(url).toContain('offset=12')
|
|
expect(url).toContain('limit=12')
|
|
expect(url).toContain('access_token=test-token')
|
|
})
|
|
|
|
it('appends fetched articles to the display', async () => {
|
|
mockFetch.mockResolvedValue({
|
|
json: () =>
|
|
Promise.resolve({
|
|
data: [
|
|
{
|
|
id: '101',
|
|
title: 'Loaded Article',
|
|
slug: 'loaded-article',
|
|
excerpt: null,
|
|
featured_image: null,
|
|
published_at: null,
|
|
date_created: '2026-05-31T00:00:00Z',
|
|
is_featured: false,
|
|
category: { id: '1', name: 'Games', slug: 'games' },
|
|
},
|
|
],
|
|
}),
|
|
})
|
|
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Loaded Article')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('hides button after last page when fewer results than limit are returned', async () => {
|
|
mockFetch.mockResolvedValue({
|
|
json: () => Promise.resolve({ data: [] }),
|
|
})
|
|
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
expect(screen.getByText('Load more')).toBeInTheDocument()
|
|
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByText('Load more')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('keeps button visible when exactly limit articles are returned', async () => {
|
|
const articles = Array.from({ length: 12 }, (_, i) => ({
|
|
id: String(200 + i),
|
|
title: `Article ${i}`,
|
|
slug: `article-${i}`,
|
|
excerpt: null,
|
|
featured_image: null,
|
|
published_at: null,
|
|
date_created: '2026-05-31T00:00:00Z',
|
|
is_featured: false,
|
|
category: { id: '1', name: 'Games', slug: 'games' },
|
|
}))
|
|
|
|
mockFetch.mockResolvedValue({
|
|
json: () => Promise.resolve({ data: articles }),
|
|
})
|
|
|
|
render(<LoadMoreButton categorySlug="games" initialCount={12} />)
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Load more')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('calls the correct Directus API URL', async () => {
|
|
render(<LoadMoreButton categorySlug="vtubers" initialCount={14} />)
|
|
fireEvent.click(screen.getByText('Load more'))
|
|
|
|
await waitFor(() => {
|
|
expect(mockFetch).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
const url = mockFetch.mock.calls[0][0] as string
|
|
expect(url).toContain('vtubers')
|
|
expect(url).toContain('offset=14')
|
|
})
|
|
})
|