diff --git a/jest.config.js b/jest.config.js index 27aced7..8a5532e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,4 @@ module.exports = { collectCoverageFrom: ["src/**/*.{ts,tsx}"], - coveragePathIgnorePatterns: ["./src/index.ts"], - modulePathIgnorePatterns: ["./src/__oldtests__/"] + coveragePathIgnorePatterns: ["./src/index.ts"] }; diff --git a/src/__tests__/useEvent.tsx b/src/__tests__/useEvent.tsx index acdc7cb..9dc7e24 100644 --- a/src/__tests__/useEvent.tsx +++ b/src/__tests__/useEvent.tsx @@ -4,13 +4,26 @@ import { renderHook } from "@testing-library/react-hooks"; import { useEvent } from "../useEvent"; describe("useEvent()", () => { - test("should ", () => { + test("should bind to events when the hook is called", () => { const listener = jest.fn(); const channel = new PusherChannelMock(); - renderHook(() => + const { unmount } = renderHook(() => useEvent((channel as unknown) as Channel, "event", listener) ); channel.emit("event", {}); expect(listener).toHaveBeenCalledWith({}); + + // test unbinding + unmount(); + channel.emit("event", {}); + expect(listener).toHaveBeenCalledTimes(1); + }); + + test("should not bind when there is no channel", () => { + const listener = jest.fn(); + const channel = new PusherChannelMock(); + renderHook(() => useEvent(undefined, "event", listener)); + channel.emit("event", {}); + expect(listener).not.toHaveBeenCalled(); }); }); diff --git a/src/__tests__/usePresenceChannel.tsx b/src/__tests__/usePresenceChannel.tsx index 7404bd4..8417b11 100644 --- a/src/__tests__/usePresenceChannel.tsx +++ b/src/__tests__/usePresenceChannel.tsx @@ -1,3 +1,4 @@ +import { PusherMock, PusherPresenceChannelMock } from "pusher-js-mock"; import { act, renderHook } from "@testing-library/react-hooks"; import { actAndFlushPromises, @@ -5,9 +6,6 @@ import { renderHookWithProvider } from "../../testUtils"; -import Pusher from "pusher-js"; -import { PusherMock } from "pusher-js-mock"; -import React from "react"; import { __PusherContext } from "../PusherProvider"; import { usePresenceChannel } from "../usePresenceChannel"; @@ -23,7 +21,7 @@ describe("usePresenceChannel()", () => { }); test("should bind to pusher events and unbind on mount", async () => { - const { result, unmount, rerender } = await renderHookWithProvider( + const { result, unmount } = await renderHookWithProvider( () => usePresenceChannel("presence-channel"), makeAuthPusherConfig() ); @@ -39,7 +37,7 @@ describe("usePresenceChannel()", () => { expect(channel.callbacks["pusher:member_removed"]).toHaveLength(0); }); - test("should return new member list when members are added", async () => { + test("should return new member list when members are added and remove them when they unsubscribe", async () => { const { result } = await renderHookWithProvider( () => usePresenceChannel("presence-channel"), makeAuthPusherConfig() @@ -48,15 +46,18 @@ describe("usePresenceChannel()", () => { expect(result.current.members).toEqual({ "my-id": {} }); expect(result.current.myID).toEqual("my-id"); + let otherClient; await act(async () => { - const otherClient = new PusherMock( - "key", - makeAuthPusherConfig("your-id") - ); + otherClient = new PusherMock("key", makeAuthPusherConfig("your-id")); otherClient.subscribe("presence-channel"); await actAndFlushPromises(); }); expect(result.current.members).toEqual({ "my-id": {}, "your-id": {} }); + + await act(async () => { + otherClient.unsubscribe("presence-channel"); + }); + expect(result.current.members).toEqual({ "my-id": {} }); }); }); diff --git a/src/__tests__/useTrigger.tsx b/src/__tests__/useTrigger.tsx new file mode 100644 index 0000000..8c308df --- /dev/null +++ b/src/__tests__/useTrigger.tsx @@ -0,0 +1,81 @@ +import "jest-fetch-mock"; + +import { NO_AUTH_HEADERS_WARNING, useTrigger } from "../useTrigger"; + +import Pusher from "pusher-js"; +import { PusherMock } from "pusher-js-mock"; +import React from "react"; +import { __PusherContext } from "../PusherProvider"; +import { renderHook } from "@testing-library/react-hooks"; + +describe("useTrigger()", () => { + beforeEach(() => { + fetch.resetMocks(); + }); + test("should trigger a fetch event on use and warn about no headers", async () => { + jest.spyOn(console, "warn"); + const wrapper = ({ children }) => ( + <__PusherContext.Provider + value={{ + client: (new PusherMock("key") as unknown) as Pusher, + triggerEndpoint: "http://example.com" + }} + > + {children} + + ); + + const { result } = await renderHook(() => useTrigger("presence-channel"), { + wrapper + }); + + result.current("event", {}); + expect(fetch.mock.calls.length).toBe(1); + expect(console.warn).toHaveBeenCalledWith(NO_AUTH_HEADERS_WARNING); + }); + test("should trigger a fetch event on use and warn", async () => { + jest.spyOn(console, "warn"); + const config = { + auth: { + headers: "Bearer token" + } + }; + + const wrapper = ({ children }) => ( + <__PusherContext.Provider + value={{ + client: (new PusherMock("key", { + auth: { + headers: { + Authorization: "Bearer token" + } + } + }) as unknown) as Pusher, + triggerEndpoint: "http://example.com" + }} + > + {children} + + ); + + const { result } = await renderHook(() => useTrigger("public-channel"), { + wrapper + }); + + result.current("event", {}); + expect(fetch.mock.calls[0]).toEqual([ + "http://example.com", + { + method: "POST", + body: JSON.stringify({ + channelName: "public-channel", + eventName: "event", + data: {} + }), + headers: { + Authorization: "Bearer token" + } + } + ]); + }); +}); diff --git a/src/types.d.ts b/src/types.d.ts index 57b26a4..62e82b2 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,5 +1,6 @@ import Pusher, { Options } from "pusher-js"; import * as React from "react"; +import "jest-fetch-mock"; export interface PusherContextValues { // client?: React.MutableRefObject; diff --git a/src/useEvent.ts b/src/useEvent.ts index 5e1f78e..5fe53b5 100644 --- a/src/useEvent.ts +++ b/src/useEvent.ts @@ -1,6 +1,7 @@ -import { useEffect } from 'react'; -import invariant from 'invariant'; -import { Channel, PresenceChannel } from 'pusher-js'; +import { Channel, PresenceChannel } from "pusher-js"; + +import invariant from "invariant"; +import { useEffect } from "react"; /** * Subscribes to a channel event and registers a callback. @@ -11,21 +12,11 @@ import { Channel, PresenceChannel } from 'pusher-js'; export function useEvent( channel: Channel | PresenceChannel | undefined, eventName: string, - callback: (data?: D) => void, - dependencies?: unknown[] | undefined + callback: (data?: D) => void ) { // error when required arguments aren't passed. - invariant(eventName, 'Must supply eventName and callback to onEvent'); - invariant(callback, 'Must supply callback to onEvent'); - - // deprecate dependencies - useEffect(() => { - if (dependencies) { - console.warn( - 'The useEvent callback is no longer memoized - dependencies are deprecated and its up to you to memoize the callback if you want to.' - ); - } - }, [dependencies]); + invariant(eventName, "Must supply eventName and callback to onEvent"); + invariant(callback, "Must supply callback to onEvent"); // bind and unbind events whenever the channel, eventName or callback changes. useEffect(() => { diff --git a/src/usePresenceChannel.ts b/src/usePresenceChannel.ts index 638860c..40f69c7 100644 --- a/src/usePresenceChannel.ts +++ b/src/usePresenceChannel.ts @@ -58,12 +58,6 @@ export function usePresenceChannel(channelName: string) { channel.bind("pusher:member_added", handleAdd); channel.bind("pusher:member_removed", handleRemove); - // set any members that already existed on the channel - if (channel.members) { - setMembers(channel.members.members); - setMyID(channel.members.myID); - } - // cleanup return () => { channel.unbind( diff --git a/src/useTrigger.ts b/src/useTrigger.ts index ad5cf67..48ee5fd 100644 --- a/src/useTrigger.ts +++ b/src/useTrigger.ts @@ -39,15 +39,16 @@ export function useTrigger(channelName: string) { if (client && client.config?.auth) { fetchOptions.headers = client.config?.auth.headers; } else { - console.warn( - "No auth parameters supplied to . Your events will be unauthenticated." - ); + console.warn(NO_AUTH_HEADERS_WARNING); } - return triggerEndpoint && fetch(triggerEndpoint, fetchOptions); + return fetch(triggerEndpoint, fetchOptions); }, [client, triggerEndpoint, channelName] ); return trigger; } + +export const NO_AUTH_HEADERS_WARNING = + "No auth parameters supplied to . Your events will be unauthenticated."; diff --git a/tsconfig.json b/tsconfig.json index 2261108..5fffec8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ "declaration": false, "noEmit": true }, - "include": ["src"], + "include": ["src", "src/types.d.ts"], "exclude": [ "node_modules", "build",