82 lines
3.1 KiB
React
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()
|
|
})
|
|
})
|