Defer artwork comments from initial render
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
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
|
||||
|
||||
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) => {
|
||||
observerCallback = callback
|
||||
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} />)
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user