From 4e81f0e6b2490c3eba0d9125ec6190d401e9a6c4 Mon Sep 17 00:00:00 2001 From: scottrippey Date: Wed, 6 Jan 2021 09:43:38 -0700 Subject: [PATCH] feat: Added `Provider.FromValue`, to enable easy dependency mocking --- src/index.tsx | 22 ++++++++-- test/index.test.tsx | 100 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 64a4fad..9d34576 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -24,10 +24,21 @@ type Hooks< // const [Provider, useContextValue] = constate(useValue) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type ConstateTuple[]> = [ - React.FC, + HookedProvider, ...Hooks ]; +// const [Provider, useContextValue] = constate(useValue) +// ^^^^^^^^ +type HookedProvider = React.FC> & { + FromValue: ValueProvider; +}; + +// const [Provider, useContextValue] = constate(useValue) +// ... +// ^^^^^^^^^ +type ValueProvider = React.FC>; + const isDev = process.env.NODE_ENV !== "production"; const NO_PROVIDER = {}; @@ -62,8 +73,7 @@ function constate[]>( createContext(); } - const Provider: React.FC = ({ children, ...props }) => { - const value = useValue(props as Props); + const Providers: ValueProvider = ({ children, value }) => { let element = children as React.ReactElement; for (let i = 0; i < contexts.length; i += 1) { const context = contexts[i]; @@ -74,9 +84,15 @@ function constate[]>( } return element; }; + const Provider: HookedProvider = ({ children, ...props }) => { + const value = useValue(props as Props); + return Providers({ value, children }); + }; + Provider.FromValue = Providers; if (isDev && useValue.name) { Provider.displayName = `${useValue.name}.Provider`; + Providers.displayName = `${useValue.name}.Provider`; } return [Provider, ...hooks]; diff --git a/test/index.test.tsx b/test/index.test.tsx index f380b9a..c096385 100644 --- a/test/index.test.tsx +++ b/test/index.test.tsx @@ -175,3 +175,103 @@ test("displayName with named useValue with selectors", () => { ); expect(Provider.displayName).toBe("useCounter.Provider"); }); + +describe("FromValue", () => { + test("no selectors", () => { + const [CounterProvider, useCounterContext] = constate(useCounter); + const Increment = () => { + const { increment } = useCounterContext(); + return ; + }; + const Decrement = () => { + const { decrement } = useCounterContext(); + return ; + }; + const Count = () => { + const { count } = useCounterContext(); + return
{count}
; + }; + const mockContextValue = { + count: 10, + increment: jest.fn(), + decrement: jest.fn(), + }; + const MockApp = () => ( + + + + + + ); + const { getByText } = render(); + expect(getByText("10")).toBeDefined(); + + fireEvent.click(getByText("Increment")); + expect(getByText("10")).toBeDefined(); + expect(mockContextValue.increment).toHaveBeenCalledTimes(1); + + fireEvent.click(getByText("Decrement")); + expect(getByText("10")).toBeDefined(); + expect(mockContextValue.decrement).toHaveBeenCalledTimes(1); + }); + + test("single selector", () => { + const [CounterProvider, useCount] = constate( + useCounter, + (value) => value.count + ); + const Count = () => { + const count = useCount(); + return
{count}
; + }; + const mockContextValue = { + count: 10, + increment: jest.fn(), + decrement: jest.fn(), + }; + const App = () => ( + + + + ); + const { getByText } = render(); + expect(getByText("10")).toBeDefined(); + }); + + test("two selectors", () => { + const [CounterProvider, useCount, useIncrement] = constate( + useCounter, + (value) => value.count, + (value) => value.increment + ); + const Increment = () => { + const increment = useIncrement(); + return ; + }; + const Count = () => { + const count = useCount(); + return
{count}
; + }; + const mockContextValue = { + count: 10, + increment: jest.fn(), + decrement: jest.fn(), + }; + const MockApp = () => ( + + + + + ); + const { getByText } = render(); + expect(getByText("10")).toBeDefined(); + + fireEvent.click(getByText("Increment")); + expect(getByText("10")).toBeDefined(); + expect(mockContextValue.increment).toHaveBeenCalledTimes(1); + + fireEvent.click(getByText("Increment")); + expect(getByText("10")).toBeDefined(); + expect(mockContextValue.increment).toHaveBeenCalledTimes(2); + }); +});