import { DataProvider, getDefaultManagers, useController, useSuspense, } from '@data-client/react'; import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { makeGetTodo, mockTodoState } from '__tests__/concurrentFixtures'; import { expectAllCommitsPriority, ImmediatePriority, makeNotifyStore, NormalPriority, resetCommits, } from '__tests__/reactCommitProbe'; import { captureStream, discoverPendingBoundary, Gate, getHydrateRoot, holdDocumentLoading, installShell, makeGate, pendingMarker, recordConsoleErrors, replayRest, } from '__tests__/streamingHarness'; import React, { startTransition, Suspense, useState, useSyncExternalStore, version, } from 'react'; const LegacyReact = version.startsWith('16') || version.startsWith('17'); const describeConcurrent = LegacyReact ? describe.skip : describe; const isReact18 = version.startsWith('18'); const HYDRATION_UPDATE_BEFORE_FINISH = 'This Suspense boundary received an update before it finished hydrating'; function isHydrationUpdateWarning(args: unknown[]): boolean { return args.some(arg => { const text = typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : ''; return text.includes(HYDRATION_UPDATE_BEFORE_FINISH); }); } /** Drain React 18's intended client-render warning so suite afterEach still requires a clean console. */ function takeHydrationUpdateWarnings(unexpected: unknown[][]): unknown[][] { const taken = unexpected.filter(isHydrationUpdateWarning); unexpected.splice( 0, unexpected.length, ...unexpected.filter(args => !isHydrationUpdateWarning(args)), ); return taken; } const fetchTodo = jest.fn(({ id }: { id: string }) => Promise.resolve({ id, title: `todo ${id}` }), ); const getTodo = makeGetTodo(fetchTodo); const stateAB = mockTodoState(getTodo, ['A', 'B']); function TodoView({ id }: { id: string }) { const todo = useSuspense(getTodo, { id }); return
{todo.title}
; } describeConcurrent('concurrent probe controls', () => { let consoleRecorder: ReturnType; beforeAll(() => { consoleRecorder = recordConsoleErrors(); }); beforeEach(() => { consoleRecorder.unexpected.length = 0; resetCommits(); fetchTodo.mockClear(); }); afterEach(() => { expect(consoleRecorder.unexpected).toEqual([]); }); afterAll(() => { consoleRecorder.restore(); }); it('a subscribed useSyncExternalStore store commits at Immediate priority, even inside startTransition', () => { const store = makeNotifyStore(); function Consumer() { const value = useSyncExternalStore(store.subscribe, store.getSnapshot); return {value}; } const { getByTestId } = render(); resetCommits(); act(() => { store.notify(); }); expect(getByTestId('uses-value').textContent).toBe('1'); expectAllCommitsPriority( 'uSES notify was not Immediate', ImmediatePriority, ); resetCommits(); act(() => { startTransition(() => { store.notify(); }); }); expect(getByTestId('uses-value').textContent).toBe('2'); expectAllCommitsPriority( 'uSES notify inside startTransition was not Immediate', ImmediatePriority, ); }); it('a plain state update outside an event commits at Normal priority', async () => { let setX: (n: number) => void = () => {}; function Sample() { const [x, set] = useState(0); setX = set; return {x}; } render(); resetCommits(); await act(async () => { await Promise.resolve(); setX(1); }); expect(document.querySelector('[data-testid="x"]')?.textContent).toBe('1'); expectAllCommitsPriority('plain setState was not Normal', NormalPriority); }); it('a store publish from a discrete click handler is Immediate by React design', () => { function ClickPublish() { const controller = useController(); return ( ); } const { getByText, getByTestId } = render( loading A

}>
, ); resetCommits(); act(() => { fireEvent.click(getByText('publish')); }); expect(getByTestId('todo-A').textContent).toBe('todo A v2'); expectAllCommitsPriority( 'discrete setResponse was not Immediate', ImmediatePriority, ); }); describe('recreating Suspense elements above a pending boundary', () => { let container: HTMLDivElement; let finishDocument: (() => void) | undefined; let prevActEnv: boolean | undefined; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); prevActEnv = (globalThis as any).IS_REACT_ACT_ENVIRONMENT; (globalThis as any).IS_REACT_ACT_ENVIRONMENT = false; }); afterEach(() => { finishDocument?.(); finishDocument = undefined; (globalThis as any).IS_REACT_ACT_ENVIRONMENT = prevActEnv; container.remove(); }); it('client-renders the pending sibling so the streamed node is not adopted', async () => { const serverGate = makeGate(); const clientGate = makeGate(); let bump = () => {}; function RecreatingTree({ gate }: { gate: ReturnType }) { const [tick, setTick] = useState(0); bump = () => setTick(t => t + 1); return ( <> loading A

}>
loading B

}>
); } function Page({ gate }: { gate: ReturnType }) { return (
); } const { shell, rest } = await captureStream(, { releaseAfterShell: [() => serverGate.release()], }); installShell(container, shell); const pendingB = discoverPendingBoundary(container, 'loading B'); finishDocument = holdDocumentLoading(); const root = getHydrateRoot()(container, ); await waitFor(() => { expect( container.querySelector('[data-testid="todo-A"]'), ).not.toBeNull(); }); expect(pendingMarker(container, pendingB)).toBe(true); act(() => { bump(); }); await waitFor(() => { expect(pendingMarker(container, pendingB)).toBe(false); }); expect(container.textContent).toContain('loading B'); const bStreamedNode = replayRest(container, rest, 'todo-B'); expect(bStreamedNode).toBeDefined(); const visibleB = container.querySelector('[data-testid="todo-B"]'); expect(visibleB === bStreamedNode).toBe(false); root.unmount(); if (isReact18) { // Intended client-render control on 18; other suites still require an empty console. expect( takeHydrationUpdateWarnings(consoleRecorder.unexpected).length, ).toBeGreaterThan(0); } }); }); });