UNPKG

tqf

Version:

React Query hooks for Firebase.

404 lines (325 loc) 9.85 kB
import React from "react"; import { QueryClient } from "@tanstack/react-query"; import { renderHook } from "@testing-library/react-hooks"; import { genId, init } from "./helpers"; import { useDatabaseSnapshot, useDatabaseValue } from "../src"; import { act } from "react-test-renderer"; import { Database, DataSnapshot, ref, remove, set, update, } from "@firebase/database"; import { push } from "firebase/database"; describe("Database", () => { let client: QueryClient; let wrapper: React.FC<{ children: React.ReactNode }>; let database: Database; beforeEach(() => { const config = init(); client = config.client; wrapper = config.wrapper; database = config.database; }); afterEach(async () => { client.clear(); }); describe("useDatabaseSnapshot", () => { test("it returns a valid snapshot", async () => { const hookId = genId(); const dbRef = ref(database, "foo"); const { result, waitFor } = renderHook( () => useDatabaseSnapshot(hookId, dbRef), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBeInstanceOf(DataSnapshot); expect(result.current.data.exists()).toBe(false); }); test("it returns a valid snapshot with data", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(dbRef, { foo: "bar" }); const { result, waitFor } = renderHook( () => useDatabaseSnapshot(hookId, dbRef), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBeInstanceOf(DataSnapshot); expect(result.current.data.exists()).toBe(true); expect(result.current.data.val()).toEqual({ foo: "bar" }); }); test("it subscribes to data snapshots", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(dbRef, { foo: "bar" }); const mock = jest.fn(); const { result, waitFor, unmount } = renderHook( () => useDatabaseSnapshot( hookId, dbRef, { subscribe: true }, { onSuccess(snapshot) { mock(snapshot); }, } ), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBeInstanceOf(DataSnapshot); expect(result.current.data.exists()).toBe(true); expect(result.current.data.val()).toEqual({ foo: "bar" }); await act(async () => { await update(dbRef, { bar: "baz" }); }); await waitFor(() => result.current.isSuccess); expect(result.current.data.val()).toEqual({ foo: "bar", bar: "baz" }); unmount(); await act(async () => { await update(dbRef, { foo: "baz" }); }); expect(mock.mock.calls.length).toBe(2); }); test("it should not unsubscribe if there is still a hook listening", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(push(dbRef), 123); const mock1 = jest.fn(); const mock2 = jest.fn(); const hook1 = renderHook( () => useDatabaseSnapshot( hookId, dbRef, { subscribe: true }, { onSuccess(snapshot) { mock1(snapshot); }, } ), { wrapper, } ); const hook2 = renderHook( () => useDatabaseSnapshot( hookId, dbRef, { subscribe: true }, { onSuccess(snapshot) { mock2(snapshot); }, } ), { wrapper, } ); await hook1.waitFor(() => hook1.result.current.isSuccess); await hook2.waitFor(() => hook2.result.current.isSuccess); hook1.unmount(); await act(async () => { await set(push(dbRef), "baz"); }); await hook2.waitFor(() => hook2.result.current.isSuccess, { timeout: 5000, }); expect(mock1.mock.calls.length).toBe(1); expect(mock2.mock.calls.length).toBe(2); }); }); describe("useDatabaseValue", () => { test("it returns null if ref doesn't exist", async () => { const hookId = genId(); const dbRef = ref(database, "foo"); const { result, waitFor } = renderHook( () => useDatabaseValue(hookId, dbRef), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBeNull(); }); test("it returns data if exists", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(dbRef, 10); const { result, waitFor } = renderHook( () => useDatabaseValue<number>(hookId, dbRef), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBe(10); }); test("it returns modified data", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(dbRef, 10); const { result, waitFor } = renderHook( () => useDatabaseValue<number, string>(hookId, dbRef, undefined, { select(value) { return value.toString(); }, }), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toBe("10"); }); test("it returns if ref is a list value", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await Promise.all([ await set(push(dbRef), "foo"), await set(push(dbRef), 123), ]); const { result, waitFor } = renderHook( () => useDatabaseValue(hookId, dbRef, { toArray: true }), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toEqual(["foo", 123]); }); test("it returns if ref is a list value with a nested list", async () => { const hookId = genId(); const dbRef = ref(database, genId()); const nestedRef = push(dbRef); await Promise.all([ await set(push(dbRef), "foo"), await set(push(nestedRef), "foo"), await set(push(nestedRef), 123), ]); const { result, waitFor } = renderHook( () => useDatabaseValue(hookId, dbRef, { toArray: true }), { wrapper, } ); await waitFor(() => result.current.isSuccess); // Nested ref comes first expect(result.current.data).toEqual([["foo", 123], "foo"]); }); test("it returns an object if no array option is set", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(dbRef, { foo: "bar", bar: "baz" }); const { result, waitFor } = renderHook( () => useDatabaseValue(hookId, dbRef, undefined), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toEqual({ foo: "bar", bar: "baz" }); }); test("it subscribes to data changes", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await Promise.all([ await set(push(dbRef), "foo"), await set(push(dbRef), 123), ]); const mock = jest.fn(); const { result, waitFor, unmount } = renderHook( () => useDatabaseValue( hookId, dbRef, { subscribe: true, toArray: true }, { onSuccess(snapshot) { mock(snapshot); }, } ), { wrapper, } ); await waitFor(() => result.current.isSuccess); expect(result.current.data).toEqual(["foo", 123]); await act(async () => { await set(push(dbRef), "baz"); }); await waitFor(() => result.current.isSuccess); expect(result.current.data).toEqual(["foo", 123, "baz"]); unmount(); await act(async () => { await remove(dbRef); }); expect(mock.mock.calls.length).toBe(2); }); test("it should not unsubscribe if there is still a hook listening", async () => { const hookId = genId(); const dbRef = ref(database, genId()); await set(push(dbRef), 123); const mock1 = jest.fn(); const mock2 = jest.fn(); const hook1 = renderHook( () => useDatabaseValue( hookId, dbRef, { subscribe: true }, { onSuccess(data) { mock1(data); }, } ), { wrapper, } ); const hook2 = renderHook( () => useDatabaseValue( hookId, dbRef, { subscribe: true }, { onSuccess(data) { mock2(data); }, } ), { wrapper, } ); await hook1.waitFor(() => hook1.result.current.isSuccess); await hook2.waitFor(() => hook2.result.current.isSuccess); hook1.unmount(); await act(async () => { await set(push(dbRef), "baz"); }); await hook2.waitFor(() => hook2.result.current.isSuccess, { timeout: 5000, }); expect(mock1.mock.calls.length).toBe(1); expect(mock2.mock.calls.length).toBe(2); }); }); });