diff --git a/package-lock.json b/package-lock.json index afcdf52..ec64c49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/packages/core/package.json b/packages/core/package.json index dfbc4b8..08400f1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -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" } } diff --git a/packages/core/src/bind/connectFactoryObservable.test.tsx b/packages/core/src/bind/connectFactoryObservable.test.tsx index bc9d23f..13575ea 100644 --- a/packages/core/src/bind/connectFactoryObservable.test.tsx +++ b/packages/core/src/bind/connectFactoryObservable.test.tsx @@ -84,17 +84,18 @@ describe("connectFactoryObservable", () => { render() - 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() - 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() - 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) => (
Result {useDelayedNumber(p.input)}
) const TestSuspense: React.FC = () => { @@ -254,50 +251,38 @@ describe("connectFactoryObservable", () => { getDelayedNumber$(0).subscribe() render() - 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", () => { , ) - 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() - 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] + ) 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 => { + (key: number): Observable => { nSubscriptions++ return defer(() => me$(key)).pipe( take(1), diff --git a/packages/core/src/bind/connectObservable.test.tsx b/packages/core/src/bind/connectObservable.test.tsx index 5970e34..96ca5d8 100644 --- a/packages/core/src/bind/connectObservable.test.tsx +++ b/packages/core/src/bind/connectObservable.test.tsx @@ -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() - 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() - 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() - 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() - 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>() + const subject$ = new Subject() const [usePromise, promise$] = bind(subject$, null) const Result: React.FC = () => { const value = usePromise() @@ -371,17 +356,13 @@ describe("connectObservable", () => { render() - 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() - 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", () => { , ) - 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", () => { , ) - - 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() - 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() - 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", () => {