From 8c56b0a80bd48aa9d3a350f5bf7feae4d66c5b02 Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Mon, 22 Jun 2020 00:19:52 +0200 Subject: [PATCH] More tests --- package-lock.json | 159 ++++++++++++++++++++++++ package.json | 1 + src/operators/react-enhancer.ts | 3 - test/connectFactoryObservable.test.tsx | 162 +++++++++++++++++-------- test/connectObservable.test.tsx | 82 ++++++++++++- test/createInput.test.ts | 2 +- test/useObservable.test.ts | 112 ----------------- test/useObservable.test.tsx | 128 +++++++++++++++++++ 8 files changed, 479 insertions(+), 170 deletions(-) delete mode 100644 test/useObservable.test.ts create mode 100644 test/useObservable.test.tsx diff --git a/package-lock.json b/package-lock.json index b19192e..0c6f2c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2464,6 +2464,159 @@ "micromatch": "^4.0.2" } }, + "@testing-library/dom": { + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.16.2.tgz", + "integrity": "sha512-4fT5l5L+5gfNhUZVCg0wnSszbRJ7A1ZHEz32v7OzH3mcY5lUsK++brI3IB2L9F5zO4kSDc2TRGEVa8v2hgl9vA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "aria-query": "^4.0.2", + "dom-accessibility-api": "^0.4.5", + "pretty-format": "^25.5.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.3.tgz", + "integrity": "sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.10.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz", + "integrity": "sha512-HA7RPj5xvJxQl429r5Cxr2trJwOfPjKiqhCXcdQPSqO2G0RHPZpXu4fkYmBaTKCp2c/jRaMK9GB/lN+7zvvFPw==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", + "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/react": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.3.0.tgz", + "integrity": "sha512-Rhn5uJK6lYHWzlGVbK6uAvheAW8AUoFYxTLGdDxgsJDaK/PYy5drWfW/6YpMMOKMw+u6jHHl4MNHlt5qLHnm0Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@testing-library/dom": "^7.14.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.3.tgz", + "integrity": "sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "@testing-library/react-hooks": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.3.0.tgz", @@ -4509,6 +4662,12 @@ "esutils": "^2.0.2" } }, + "dom-accessibility-api": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.5.tgz", + "integrity": "sha512-HcPDilI95nKztbVikaN2vzwvmv0sE8Y2ZJFODy/m15n7mGXLeOKGiys9qWVbFbh+aq/KYj2lqMLybBOkYAEXqg==", + "dev": true + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", diff --git a/package.json b/package.json index 61dcb65..8dacca6 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "module": "dist/re-rxjs.esm.js", "devDependencies": { "@josepot/rxjs-utils": "^0.12.0", + "@testing-library/react": "^10.3.0", "@testing-library/react-hooks": "^3.3.0", "@types/jest": "^26.0.0", "@types/react": "^16.9.38", diff --git a/src/operators/react-enhancer.ts b/src/operators/react-enhancer.ts index 861bd02..ec0a063 100644 --- a/src/operators/react-enhancer.ts +++ b/src/operators/react-enhancer.ts @@ -26,9 +26,6 @@ const reactEnhancer = ( error(e) { subscriber.error(e) }, - complete() { - subscriber.complete() - }, }) onSubscribe.next() finalizeLastUnsubscription() diff --git a/test/connectFactoryObservable.test.tsx b/test/connectFactoryObservable.test.tsx index 76fb40a..d80bbb2 100644 --- a/test/connectFactoryObservable.test.tsx +++ b/test/connectFactoryObservable.test.tsx @@ -1,71 +1,129 @@ -import { connectFactoryObservable } from "../src" +import { connectFactoryObservable } from "../src/connectFactoryObservable" import { from, of, defer, concat, BehaviorSubject } from "rxjs" import { renderHook, act } from "@testing-library/react-hooks" const wait = (ms: number) => new Promise(res => setTimeout(res, ms)) -describe("connectObservable", () => { - it("returns the latest emitted value", async () => { - const [useNumber] = connectFactoryObservable((id: number) => of(id)) - const { result } = renderHook(() => useNumber(1)) - expect(result.current).toBe(1) +describe("connectFactoryObservable", () => { + const originalError = console.error + beforeAll(() => { + console.error = (...args: any) => { + if (/Consider adding an error/.test(args[0])) { + return + } + originalError.call(console, ...args) + } }) - it("shares the source subscription until the refCount has stayed at zero for the grace-period", async () => { - let nInitCount = 0 - const observable$ = defer(() => { - nInitCount += 1 - return from([1, 2, 3, 4, 5]) + afterAll(() => { + console.error = originalError + }) + describe("hook", () => { + it("returns the latest emitted value", async () => { + const [useNumber] = connectFactoryObservable((id: number) => of(id)) + const { result } = renderHook(() => useNumber(1)) + expect(result.current).toBe(1) }) - const [useLatestNumber] = connectFactoryObservable( - (id: number) => concat(observable$, of(id)), - { - unsubscribeGraceTime: 100, - }, - ) - const { unmount } = renderHook(() => useLatestNumber(6)) - const { unmount: unmount2 } = renderHook(() => useLatestNumber(6)) - const { unmount: unmount3 } = renderHook(() => useLatestNumber(6)) - expect(nInitCount).toBe(1) - unmount() - unmount2() - unmount3() + it("shares the source subscription until the refCount has stayed at zero for the grace-period", async () => { + let nInitCount = 0 + const observable$ = defer(() => { + nInitCount += 1 + return from([1, 2, 3, 4, 5]) + }) - await wait(90) - const { unmount: unmount4 } = renderHook(() => useLatestNumber(6)) - expect(nInitCount).toBe(1) - unmount4() + const [useLatestNumber] = connectFactoryObservable( + (id: number) => concat(observable$, of(id)), + { + unsubscribeGraceTime: 100, + }, + ) + const { unmount } = renderHook(() => useLatestNumber(6)) + const { unmount: unmount2 } = renderHook(() => useLatestNumber(6)) + const { unmount: unmount3 } = renderHook(() => useLatestNumber(6)) + expect(nInitCount).toBe(1) + unmount() + unmount2() + unmount3() - await wait(110) - renderHook(() => useLatestNumber(6)) - expect(nInitCount).toBe(2) + await wait(90) + const { unmount: unmount4 } = renderHook(() => useLatestNumber(6)) + expect(nInitCount).toBe(1) + unmount4() + + await wait(110) + renderHook(() => useLatestNumber(6)) + expect(nInitCount).toBe(2) + }) + + it("allows errors to be caught in error boundaries", () => { + const errStream = new BehaviorSubject(1) + const [useError] = connectFactoryObservable(() => errStream) + + renderHook(() => useError()) + + expect(() => + act(() => { + errStream.error("error") + }), + ).toThrow() + }) + + it("doesn't throw errors on components that will get unmounted on the next cycle", () => { + const errStream = new BehaviorSubject(1) + const [useError] = connectFactoryObservable(() => errStream) + + const { unmount } = renderHook(() => useError()) + + expect(() => + act(() => { + errStream.error("error") + unmount() + }), + ).not.toThrow() + }) }) + describe("observable", () => { + it("returns a factory of BehaviorObservables", () => { + const [, getShared] = connectFactoryObservable((x: number) => of(x)) + expect((getShared(0) as any).getValue).toBeInstanceOf(Function) + }) - it("allows errors to be caught in error boundaries", () => { - const errStream = new BehaviorSubject(1) - const [useError] = connectFactoryObservable(() => errStream) + it("if the source observable completes it keeps emitting the latest value until there are no more subscriptions", () => { + let diff = -1 + const [, getShared] = connectFactoryObservable((_: number) => { + diff++ + return from([1, 2, 3, 4].map(val => val + diff)) + }) - renderHook(() => useError()) + let latestValue1: number = 0 + let nUpdates = 0 + const sub1 = getShared(0).subscribe(x => { + latestValue1 = x + nUpdates += 1 + }) + expect(latestValue1).toBe(4) + expect(nUpdates).toBe(1) - expect(() => - act(() => { - errStream.error("error") - }), - ).toThrow() - }) + let latestValue2: number = 0 + const sub2 = getShared(0).subscribe(x => { + latestValue2 = x + nUpdates += 1 + }) + expect(latestValue2).toBe(4) + expect(nUpdates).toBe(2) - it("doesn't throw errors on components that will get unmounted on the next cycle", () => { - const errStream = new BehaviorSubject(1) - const [useError] = connectFactoryObservable(() => errStream) + sub1.unsubscribe() + sub2.unsubscribe() - const { unmount } = renderHook(() => useError()) - - expect(() => - act(() => { - errStream.error("error") - unmount() - }), - ).not.toThrow() + let latestValue3: number = 0 + const sub3 = getShared(0).subscribe(x => { + latestValue3 = x + nUpdates += 1 + }) + expect(latestValue3).toBe(5) + expect(nUpdates).toBe(3) + sub3.unsubscribe() + }) }) }) diff --git a/test/connectObservable.test.tsx b/test/connectObservable.test.tsx index 7cc7a18..68a9439 100644 --- a/test/connectObservable.test.tsx +++ b/test/connectObservable.test.tsx @@ -1,10 +1,29 @@ -import { connectObservable } from "../src" -import { from, of, defer } from "rxjs" +import React, { Suspense } from "react" +import { render, fireEvent, screen } from "@testing-library/react" +import { connectObservable } from "../src/connectObservable" +import { switchMapSuspended } from "../src/operators/switchMapSuspended" +import { suspended } from "../src/operators/suspended" +import { from, of, defer, Subject, concat } from "rxjs" import { renderHook } from "@testing-library/react-hooks" +import { delay, scan, take, mergeMap, mergeMapTo } from "rxjs/operators" const wait = (ms: number) => new Promise(res => setTimeout(res, ms)) describe("connectObservable", () => { + const originalError = console.error + beforeAll(() => { + console.error = (...args: any) => { + if (/Warning.*not wrapped in act/.test(args[0])) { + return + } + originalError.call(console, ...args) + } + }) + + afterAll(() => { + console.error = originalError + }) + it("sets the initial state synchronously if it's available", async () => { const observable$ = of(1) const [useLatestNumber] = connectObservable(observable$) @@ -40,4 +59,63 @@ describe("connectObservable", () => { renderHook(() => useLatestNumber()) expect(nInitCount).toBe(2) }) + + it("works with suspense", async () => { + const subject$ = new Subject() + const source$ = concat( + subject$.pipe( + take(2), + scan(a => a + 1, 0), + switchMapSuspended(x => of(x).pipe(delay(100))), + ), + subject$.pipe(take(1), mergeMapTo(of(3).pipe(delay(100), suspended()))), + ) + const [useDelayedNumber] = connectObservable(source$) + const Result: React.FC = () =>
Result {useDelayedNumber()}
+ const TestSuspense: React.FC = () => { + return ( +
+ + Waiting}> + + +
+ ) + } + + render() + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + await wait(110) + + expect(screen.queryByText("Result 1")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + await wait(110) + + expect(screen.queryByText("Result 2")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + await wait(110) + + expect(screen.queryByText("Result 3")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + }) }) diff --git a/test/createInput.test.ts b/test/createInput.test.ts index ea9b178..f88cb7a 100644 --- a/test/createInput.test.ts +++ b/test/createInput.test.ts @@ -1,4 +1,4 @@ -import { createInput } from "../" +import { createInput } from "../src/createInput" describe("createInput", () => { test("it returns the default value", () => { diff --git a/test/useObservable.test.ts b/test/useObservable.test.ts deleted file mode 100644 index a05ce7a..0000000 --- a/test/useObservable.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import {} from "react/experimental" -import { defer, of, Subject, Observable, NEVER, concat } from "rxjs" -import { SUSPENSE } from "../src" -import { useObservable } from "../src/useObservable" -import { renderHook, act } from "@testing-library/react-hooks" -import { delay, switchMap, startWith } from "rxjs/operators" -import { unstable_useTransition as useTransition, useEffect } from "react" -import { BehaviorObservable, distinctShareReplay } from "../src" -import reactEnhancer from "../src/operators/react-enhancer" - -const wait = (ms: number) => new Promise(res => setTimeout(res, ms)) - -const enhancer = (source$: Observable) => - reactEnhancer(concat(source$, NEVER).pipe(distinctShareReplay()), 20) - -describe("useObservable", () => { - it("works", async () => { - let counter = 0 - let subject = new Subject() - const source$ = defer(() => { - subject = new Subject() - counter++ - return subject.asObservable() - }).pipe(enhancer) as BehaviorObservable - - const { result, unmount } = renderHook(() => useObservable(source$)) - - expect(result.current).toBe(null) - - await act(async () => { - subject.next(1) - await wait(10) - }) - expect(counter).toBe(1) - expect(result.current).toEqual(1) - - act(() => { - subject.next(4) - subject.complete() - }) - - expect(result.current).toEqual(4) - expect(counter).toBe(1) - unmount() - - const secondMount = renderHook(() => useObservable(source$)) - - expect(counter).toBe(1) - expect(secondMount.result.current).toEqual(4) - secondMount.unmount() - - await wait(40) - - const thirdMount = renderHook(() => useObservable(source$)) - - expect(thirdMount.result.current).toEqual(null) - expect(counter).toBe(2) - - await act(async () => { - subject.next(1) - await wait(10) - }) - expect(thirdMount.result.current).toEqual(1) - expect(counter).toBe(2) - }) - - const userId$ = new Subject() - const getUser = (id: number) => - of({ id, name: `name_${id}` }).pipe(delay(200)) - - const user$ = userId$ - .pipe( - switchMap(id => getUser(id).pipe(startWith(SUSPENSE))), - startWith({ id: 1, name: "name_1" }), - ) - .pipe(enhancer) as BehaviorObservable - - const useUserTransition = () => { - const [startTransition, isPending] = useTransition({ timeoutMs: 500 }) - useEffect(() => { - setTimeout(() => { - startTransition(() => { - userId$.next(8) - }) - }, 100) - }, []) - // console.log("isPending", isPending) - return isPending - } - - it.skip("works with useTransition", async () => { - const { result: userResult } = renderHook(() => useObservable(user$)) - const { result: transitionResult } = renderHook(() => useUserTransition()) - - expect(userResult.current).toEqual({ id: 1, name: "name_1" }) - expect(transitionResult.current).toBe(false) - - await act(async () => { - await wait(120) - }) - - expect(userResult.current).toEqual({ id: 1, name: "name_1" }) - expect(transitionResult.current).toBe(true) - - await act(async () => { - await wait(220) - }) - - expect(userResult.current).toEqual({ id: 8, name: "name_8" }) - expect(transitionResult.current).toBe(false) - }) -}) diff --git a/test/useObservable.test.tsx b/test/useObservable.test.tsx new file mode 100644 index 0000000..4640829 --- /dev/null +++ b/test/useObservable.test.tsx @@ -0,0 +1,128 @@ +import React, { useState, Suspense } from "react" +import { render, fireEvent, screen } from "@testing-library/react" +import { defer, of, Subject, NEVER, concat } from "rxjs" +import { useObservable } from "../src/useObservable" +import { renderHook, act } from "@testing-library/react-hooks" +import { BehaviorObservable, distinctShareReplay } from "../src" +import reactEnhancer from "../src/operators/react-enhancer" +import { SUSPENSE } from "../src/SUSPENSE" + +const wait = (ms: number) => new Promise(res => setTimeout(res, ms)) + +const enhancer = (source$: any) => + reactEnhancer(concat(source$, NEVER).pipe(distinctShareReplay()), 20) + +describe("useObservable", () => { + it("works", async () => { + let counter = 0 + let subject = new Subject() + const source$ = defer(() => { + subject = new Subject() + counter++ + return subject.asObservable() + }).pipe(enhancer) as BehaviorObservable + + const { result, unmount } = renderHook(() => useObservable(source$)) + + expect(result.current).toBe(null) + + await act(async () => { + subject.next(1) + await wait(10) + }) + expect(counter).toBe(1) + expect(result.current).toEqual(1) + + act(() => { + subject.next(4) + subject.complete() + }) + + expect(result.current).toEqual(4) + expect(counter).toBe(1) + unmount() + + const secondMount = renderHook(() => useObservable(source$)) + + expect(counter).toBe(1) + expect(secondMount.result.current).toEqual(4) + secondMount.unmount() + + await wait(40) + + const thirdMount = renderHook(() => useObservable(source$)) + + expect(thirdMount.result.current).toEqual(null) + expect(counter).toBe(2) + + await act(async () => { + subject.next(1) + await wait(10) + }) + expect(thirdMount.result.current).toEqual(1) + expect(counter).toBe(2) + }) + + const observables: any = [NEVER, of(10), of(SUSPENSE), of(20)].map(enhancer) + const Result: React.FC<{ idx: number }> = ({ idx }) => { + const result = useObservable(observables[idx % observables.length]) + return
Result {result}
+ } + + const TestSuspense: React.FC = () => { + const [currentIdx, setCurrentIdx] = useState(0) + + return ( +
+ + Waiting}> + + +
+ ) + } + + it("supports suspense", () => { + const subs = observables[2].subscribe() + render() + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result 10")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result 20")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result 10")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + fireEvent.click(screen.getByText(/Next/i)) + + expect(screen.queryByText("Result 20")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + subs.unsubscribe() + }) +})