Files

82 lines
3.1 KiB
React

import React from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { act, cleanup, render, screen, waitFor } from '@testing-library/react'
import axios from 'axios'
import ArtworkComments from './ArtworkComments'
vi.mock('axios', () => ({ default: { get: vi.fn() } }))
vi.mock('../comments/CommentForm', () => ({ default: () => <div data-testid="comment-form" /> }))
vi.mock('../comments/ReactionBar', () => ({ default: () => <div data-testid="reaction-bar" /> }))
vi.mock('../xp/LevelBadge', () => ({ default: () => null }))
vi.mock('../../utils/emojiFlood', () => ({ isFlood: () => false }))
describe('ArtworkComments deferred loading', () => {
let observerCallback
let observerOptions
beforeEach(() => {
axios.get.mockResolvedValue({
data: {
data: [{ id: 1, content: 'Loaded comment', replies: [], user: {}, reactions: {} }],
meta: { current_page: 1, last_page: 1, total: 1 },
},
})
window.IntersectionObserver = vi.fn((callback, options) => {
observerCallback = callback
observerOptions = options
return { observe: vi.fn(), disconnect: vi.fn() }
})
})
afterEach(() => {
cleanup()
vi.restoreAllMocks()
delete window.IntersectionObserver
})
it('does not fetch until the comments section approaches the viewport', async () => {
render(<ArtworkComments artworkId={9357} isLoggedIn={false} />)
expect(observerOptions).toEqual(expect.objectContaining({ rootMargin: '350px 0px' }))
await new Promise((resolve) => setTimeout(resolve, 20))
expect(axios.get).not.toHaveBeenCalled()
await act(async () => observerCallback([{ isIntersecting: true }]))
await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1))
expect(axios.get).toHaveBeenCalledWith(
'/api/artworks/9357/comments?page=1',
expect.objectContaining({ signal: expect.any(AbortSignal) }),
)
expect(screen.getByText('Loaded comment')).not.toBeNull()
})
it('uses the delayed compatibility fallback when IntersectionObserver is unavailable', async () => {
delete window.IntersectionObserver
vi.useFakeTimers()
render(<ArtworkComments artworkId={42} isLoggedIn={false} />)
expect(axios.get).not.toHaveBeenCalled()
await act(async () => vi.advanceTimersByTime(1200))
expect(axios.get).toHaveBeenCalledTimes(1)
vi.useRealTimers()
})
it('resets on artwork changes and ignores stale responses', async () => {
let resolveRequest
axios.get.mockImplementation(() => new Promise((resolve) => { resolveRequest = resolve }))
const { rerender } = render(<ArtworkComments artworkId={1} isLoggedIn={false} />)
await act(async () => observerCallback([{ isIntersecting: true }]))
await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1))
rerender(<ArtworkComments artworkId={2} isLoggedIn={false} />)
await act(async () => {
resolveRequest({ data: { data: [{ id: 1, content: 'stale', replies: [], user: {}, reactions: {} }], meta: {} } })
await Promise.resolve()
})
expect(screen.queryByText('stale')).toBeNull()
})
})