import React from 'react' import { render, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import useLazyProfileWorldHistory from './useLazyProfileWorldHistory' function Harness({ username = 'alice', initial = null, enabled = false }) { const state = useLazyProfileWorldHistory({ username, initialWorldHistory: initial, enabled, }) return {JSON.stringify(state)} } describe('useLazyProfileWorldHistory', () => { beforeEach(() => vi.restoreAllMocks()) it('does not request on Posts until a consuming tab is active', () => { const fetchMock = vi.spyOn(globalThis, 'fetch') render() expect(fetchMock).not.toHaveBeenCalled() }) it('requests once on first consuming-tab activation and reuses the result', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, json: async () => ({ summary: { available: true }, entries: [{ id: 'world-1' }] }), }) const view = render() view.rerender() await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)) view.rerender() view.rerender() expect(fetchMock).toHaveBeenCalledTimes(1) await waitFor(() => expect(JSON.parse(view.getByTestId('state').textContent).loaded).toBe(true)) }) it('uses direct Worlds/Achievements SSR data without a lazy request', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch') const initial = { summary: { available: true }, entries: [{ id: 'world-1' }] } const view = render() view.rerender() view.rerender() await waitFor(() => expect(JSON.parse(view.getByTestId('state').textContent).data).toEqual(initial)) expect(fetchMock).not.toHaveBeenCalled() }) it('treats an empty response as loaded', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, json: async () => ({ summary: { available: false }, entries: [] }) }) const view = render() await waitFor(() => expect(JSON.parse(view.getByTestId('state').textContent).loaded).toBe(true)) expect(fetchMock).toHaveBeenCalledTimes(1) }) it('resets on profile navigation and ignores the stale response', async () => { let resolveA const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(() => new Promise((resolve) => { resolveA = resolve })) const view = render() await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)) view.rerender() resolveA({ ok: true, json: async () => ({ summary: { available: true }, entries: [{ id: 'alice' }] }) }) await waitFor(() => expect(JSON.parse(view.getByTestId('state').textContent).data).toBe(null)) }) it('does not loop after an error and permits an explicit retry', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch') .mockResolvedValueOnce({ ok: false }) .mockResolvedValueOnce({ ok: true, json: async () => ({ summary: { available: false }, entries: [] }) }) const view = render() await waitFor(() => expect(JSON.parse(view.getByTestId('state').textContent).error).toBe(true)) expect(fetchMock).toHaveBeenCalledTimes(1) // The hook's returned retry is intentionally exercised through a small rerender activation. view.rerender() view.rerender() await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2)) }) })