Merge pull request #2 from weareadaptive/react19-waitFor

Change waitFor timeout and remove uneeded waitFor usage. Upgrade use-…
This commit is contained in:
Roger Veciana i Rovira 2025-01-14 09:56:09 +01:00 committed by GitHub
commit 22ebcf9b7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 118 additions and 201 deletions

19
package-lock.json generated
View File

@ -2864,10 +2864,11 @@
"dev": true
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"dev": true
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/yargs": {
"version": "17.0.33",
@ -6892,7 +6893,7 @@
"use-sync-external-store": "^1.0.0"
},
"devDependencies": {
"@types/use-sync-external-store": "^0.0.3"
"@types/use-sync-external-store": "^0.0.6"
},
"peerDependencies": {
"react": ">=16.8.0",
@ -8321,7 +8322,7 @@
"version": "file:packages/core",
"requires": {
"@rx-state/core": "0.1.4",
"@types/use-sync-external-store": "^0.0.3",
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.0.0"
}
},
@ -8618,9 +8619,9 @@
"dev": true
},
"@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"dev": true
},
"@types/yargs": {

View File

@ -53,9 +53,9 @@
],
"dependencies": {
"@rx-state/core": "0.1.4",
"use-sync-external-store": "^1.0.0"
"use-sync-external-store": "^1.4.0"
},
"devDependencies": {
"@types/use-sync-external-store": "^0.0.3"
"@types/use-sync-external-store": "^0.0.6"
}
}

View File

@ -84,17 +84,18 @@ describe("connectFactoryObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await wait(110)
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
vi.waitFor(
() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
},
{ timeout: 2000 },
)
})
it("synchronously mounts the emitted value if the observable emits synchronously", () => {
@ -114,10 +115,8 @@ describe("connectFactoryObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
it("doesn't mount the fallback element if the subscription is already active", () => {
@ -139,10 +138,8 @@ describe("connectFactoryObservable", () => {
source$.next(1)
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
subscription.unsubscribe()
})
@ -234,7 +231,7 @@ describe("connectFactoryObservable", () => {
const [useDelayedNumber, getDelayedNumber$] = bind((x: number) =>
of(x).pipe(delay(50)),
)
const Result: React.FC<{ input: number }> = (p) => (
const Result: React.FC = (p) => (
<div>Result {useDelayedNumber(p.input)}</div>
)
const TestSuspense: React.FC = () => {
@ -254,50 +251,38 @@ describe("connectFactoryObservable", () => {
getDelayedNumber$(0).subscribe()
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await componentAct(async () => {
await getDelayedNumber$(0).pipe(first()).toPromise()
await wait(0)
})
vi.waitFor(() => {
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
componentAct(() => {
getDelayedNumber$(1).subscribe()
fireEvent.click(screen.getByText(/increase/i))
})
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await componentAct(async () => {
await wait(60)
})
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
componentAct(() => {
getDelayedNumber$(2).subscribe()
fireEvent.click(screen.getByText(/increase/i))
})
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await componentAct(async () => {
await wait(60)
})
vi.waitFor(() => {
expect(screen.queryByText("Result 2")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 2")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
it("shares the source subscription until the refCount has stayed at zero for the grace-period", async () => {
@ -439,7 +424,7 @@ describe("connectFactoryObservable", () => {
.pipe(catchError(() => []))
.subscribe()
const Ok: React.FC<{ ok: boolean }> = ({ ok }) => <>{useOkKo(ok)}</>
const Ok: React.FC = ({ ok }) => <>{useOkKo(ok)}</>
const ErrorComponent = () => {
const [ok, setOk] = useState(true)
@ -459,20 +444,17 @@ describe("connectFactoryObservable", () => {
<ErrorComponent />
</TestErrorBoundary>,
)
vi.waitFor(() => {
expect(screen.queryByText("ALL GOOD")).toBeNull()
expect(screen.queryByText("Loading...")).not.toBeNull()
})
expect(screen.queryByText("ALL GOOD")).toBeNull()
expect(screen.queryByText("Loading...")).not.toBeNull()
await componentAct(async () => {
normal$.next("ALL GOOD")
await wait(50)
})
vi.waitFor(() => {
expect(screen.queryByText("ALL GOOD")).not.toBeNull()
expect(screen.queryByText("Loading...")).toBeNull()
})
expect(screen.queryByText("ALL GOOD")).not.toBeNull()
expect(screen.queryByText("Loading...")).toBeNull()
expect(errorCallback).not.toHaveBeenCalled()
componentAct(() => {
@ -688,9 +670,7 @@ describe("connectFactoryObservable", () => {
render(<Container />)
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).not.toBeNull()
componentAct(() => {
ticks$.next()
})
@ -859,7 +839,7 @@ describe("connectFactoryObservable", () => {
const [, obs$] = bind(
(key: number) => defer(() => obs$(key)).pipe(take(1)),
(key: number) => key,
) as [(key: number) => number, (key: number) => Observable<number>]
) as [(key: number) => number, (key: number) => Observable]
let error = null
obs$(1)
@ -876,7 +856,7 @@ describe("connectFactoryObservable", () => {
it("does not crash when the factory function self-references its enhanced self", () => {
let nSubscriptions = 0
const [, me$] = bind(
(key: number): Observable<number> => {
(key: number): Observable => {
nSubscriptions++
return defer(() => me$(key)).pipe(
take(1),

View File

@ -1,11 +1,13 @@
import {
act,
act as componentAct,
fireEvent,
render,
renderHook,
screen,
} from "@testing-library/react"
import { act, renderHook } from "@testing-library/react"
import React, { FC, StrictMode, Suspense, useEffect, useState } from "react"
import { renderToPipeableStream } from "react-dom/server"
import {
defer,
EMPTY,
@ -26,7 +28,7 @@ import {
startWith,
switchMapTo,
} from "rxjs/operators"
import { describe, it, beforeAll, afterAll, expect, vi } from "vitest"
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"
import {
bind,
sinkSuspense,
@ -34,9 +36,8 @@ import {
SUSPENSE,
useStateObservable,
} from "../"
import { TestErrorBoundary } from "../test-helpers/TestErrorBoundary"
import { renderToPipeableStream } from "react-dom/server"
import { pipeableStreamToObservable } from "../test-helpers/pipeableStreamToObservable"
import { TestErrorBoundary } from "../test-helpers/TestErrorBoundary"
const wait = (ms: number) => new Promise((res) => setTimeout(res, ms))
@ -88,18 +89,18 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await wait(330)
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
await wait(110)
vi.waitFor(
() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
},
{ timeout: 2000 },
)
sub.unsubscribe()
})
@ -118,18 +119,18 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
await wait(110)
vi.waitFor(() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
vi.waitFor(
() => {
expect(screen.queryByText("Result 1")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
},
{ timeout: 2000 },
)
sub.unsubscribe()
})
@ -193,9 +194,7 @@ describe("connectObservable", () => {
const [useNumber] = bind(numberStream, 1)
const [useString] = bind(stringStream, "a")
const BatchComponent: FC<{
onUpdate: () => void
}> = ({ onUpdate }) => {
const BatchComponent: FC = ({ onUpdate }) => {
const number = useNumber()
const string = useString()
useEffect(onUpdate)
@ -272,23 +271,17 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
fireEvent.click(screen.getByText(/Next/i))
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).not.toBeNull()
fireEvent.click(screen.getByText(/Next/i))
vi.waitFor(() => {
expect(screen.queryByText("Result 2")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 2")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
it("keeps in suspense if more than two SUSPENSE are emitted in succesion", async () => {
@ -318,33 +311,25 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 0")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
fireEvent.click(screen.getByText(/Next/i))
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).not.toBeNull()
fireEvent.click(screen.getByText(/Next/i))
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).not.toBeNull()
fireEvent.click(screen.getByText(/Next/i))
vi.waitFor(() => {
expect(screen.queryByText("Result 3")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
expect(screen.queryByText("Result 3")).not.toBeNull()
expect(screen.queryByText("Waiting")).toBeNull()
})
it("doesn't enter suspense if the observable emits a promise", async () => {
const subject$ = new Subject<Promise<any>>()
const subject$ = new Subject<Promise>()
const [usePromise, promise$] = bind(subject$, null)
const Result: React.FC = () => {
const value = usePromise()
@ -371,17 +356,13 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).toBeNull()
expect(screen.queryByText("default")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).toBeNull()
expect(screen.queryByText("default")).not.toBeNull()
act(() => subject$.next(new Promise(() => {})))
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).toBeNull()
expect(screen.queryByText("promise")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).toBeNull()
expect(screen.queryByText("promise")).not.toBeNull()
})
it("correctly unsubscribes when the Subscribe component gets unmounted", async () => {
@ -408,10 +389,8 @@ describe("connectObservable", () => {
render(<TestSuspense />)
vi.waitFor(() => {
expect(screen.queryByText("Result 0")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Result 0")).toBeNull()
expect(screen.queryByText("Waiting")).not.toBeNull()
fireEvent.click(screen.getByText(/NextVal/i))
@ -433,9 +412,7 @@ describe("connectObservable", () => {
await wait(10)
vi.waitFor(() => {
expect(screen.queryByText("Waiting")).not.toBeNull()
})
expect(screen.queryByText("Waiting")).not.toBeNull()
fireEvent.click(screen.getByText(/NextVal/i))
@ -623,20 +600,16 @@ describe("connectObservable", () => {
</TestErrorBoundary>,
)
vi.waitFor(() => {
expect(screen.queryByText("Loading...")).not.toBeNull()
expect(screen.queryByText("ALL GOOD")).toBeNull()
})
expect(screen.queryByText("Loading...")).not.toBeNull()
expect(screen.queryByText("ALL GOOD")).toBeNull()
await componentAct(async () => {
errStream.next("controlled error")
await wait(50)
})
vi.waitFor(() => {
expect(screen.queryByText("Loading...")).toBeNull()
expect(screen.queryByText("ALL GOOD")).toBeNull()
})
expect(screen.queryByText("Loading...")).toBeNull()
expect(screen.queryByText("ALL GOOD")).toBeNull()
expect(errorCallback).toHaveBeenCalledWith(
"controlled error",
expect.any(Object),
@ -655,10 +628,7 @@ describe("connectObservable", () => {
</Subscribe>
</TestErrorBoundary>,
)
vi.waitFor(() => {
expect(screen.queryByText("Loading...")).not.toBeNull()
})
expect(screen.queryByText("Loading...")).not.toBeNull()
await componentAct(async () => {
nextStream.next("ALL GOOD")
@ -669,9 +639,7 @@ describe("connectObservable", () => {
"controlled error",
expect.any(Object),
)
vi.waitFor(() => {
expect(screen.queryByText("ALL GOOD")).not.toBeNull()
})
expect(screen.queryByText("ALL GOOD")).not.toBeNull()
})
it("doesn't throw errors on components that will get unmounted on the next cycle", () => {
@ -768,18 +736,14 @@ describe("connectObservable", () => {
)
expect(errorCallback).not.toHaveBeenCalled()
vi.waitFor(() => {
expect(screen.queryByText("Loading...")).not.toBeNull()
})
expect(screen.queryByText("Loading...")).not.toBeNull()
await componentAct(async () => {
subject.complete()
await wait(100)
})
vi.waitFor(() => {
expect(screen.queryByText("Loading...")).toBeNull()
})
expect(screen.queryByText("Loading...")).toBeNull()
expect(errorCallback).toHaveBeenCalled()
})
@ -882,27 +846,21 @@ describe("connectObservable", () => {
subject$.next(0)
})
vi.waitFor(() => {
expect(queryByText("Result 0")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
expect(queryByText("Result 0")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
act(() => {
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
act(() => {
subject$.next(1)
})
vi.waitFor(() => {
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
it("ignores effects while waiting for the first value", async () => {
@ -920,33 +878,25 @@ describe("connectObservable", () => {
const { queryByText } = render(<TestSuspense />)
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
await act(async () => {
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
await act(async () => {
subject$.next(SUSPENSE)
await wait(10)
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
await act(async () => {
subject$.next(1)
})
vi.waitFor(() => {
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
it("ignores effects after entering suspense", async () => {
@ -968,34 +918,26 @@ describe("connectObservable", () => {
subject$.next(0)
})
vi.waitFor(() => {
expect(queryByText("Result 0")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
expect(queryByText("Result 0")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
await act(async () => {
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
await act(async () => {
subject$.next(SUSPENSE)
await wait(10)
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Waiting")).not.toBeNull()
})
expect(queryByText("Waiting")).not.toBeNull()
await act(async () => {
subject$.next(1)
})
vi.waitFor(() => {
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
expect(queryByText("Result 1")).not.toBeNull()
expect(queryByText("Waiting")).toBeNull()
})
it("emits the default value when an effect is received", () => {
@ -1005,23 +947,17 @@ describe("connectObservable", () => {
const { queryByText } = render(<Result />)
vi.waitFor(() => {
expect(queryByText("Result 10")).not.toBeNull()
})
expect(queryByText("Result 10")).not.toBeNull()
act(() => {
subject$.next(5)
})
vi.waitFor(() => {
expect(queryByText("Result 5")).not.toBeNull()
})
expect(queryByText("Result 5")).not.toBeNull()
act(() => {
subject$.next(SUSPENSE)
})
vi.waitFor(() => {
expect(queryByText("Result 10")).not.toBeNull()
})
expect(queryByText("Result 10")).not.toBeNull()
})
describe("The hook on SSR", () => {