Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Partial<T> support in createContextHelper #7

Open
jagregory opened this issue Apr 27, 2023 · 1 comment
Open

Idea: Partial<T> support in createContextHelper #7

jagregory opened this issue Apr 27, 2023 · 1 comment

Comments

@jagregory
Copy link

I like the createContextHelper shorthand, but I don't find myself able to use it very often due to a particular pattern I use in my tests. I just thought I'd throw the idea out there, but I do have a workaround if this is more of a me thing.

I often create helpers which accept a Partial<T> of the context, and have the helper merge a default value with the partial. Using this type of helper I can keep my test focussed on just the part of the context I need to change, with the rest of the context set to some reasonable default (a fake value or jest.fn() for functions).

My current workaround is pretty simple:

const createPartialContextHelper = <ContextValue,>(
  Context: React.Context<ContextValue>,
  defaultValue: ContextValue
) =>
  createHelper((value?: Partial<ContextValue>) => ({ children }) => (
    <Context.Provider value={{ ...defaultValue, ...value }}>
      {children}
    </Context.Provider>
  ));

Which would be used the same as createContextHelper:

const Context = React.createContext<{ user: User, onEnter: () => void }>(...);

const withMyContext = createPartialContextHelper(Context, {
  user: FakeUser,
  onEnter: () => {},
});

But can be called with a Partial<T>:

it("calls onEnter sometime", () => {
  const onEnter = jest.fn().mockWhatever();

  render(<Something />, {
    wrapper: wrap(withMyContext({ onEnter }))
  })

  ...
})
@camjackson
Copy link
Owner

This is a really nice idea. Currently this is addressed by using composite helpers. With a single call to createHelpers() you can create multiple helpers each of which is dedicated to a separate part of the thing that you're trying to mock.

So for example to create the helpers:

const [withUser, withOnEnter] = createHelpers(
  (
    [user]: [User],
    [onEnter]: [() => void]
  ) =>
    ({ children }) => (
      <Context.Provider value={{
        user: user ?? fakeUser,
        onEnter: onEnter ?? () => {},
      }}>
        {children}
      </Context.Provider>
    )
);

And then you can use them like:

// Just provide a user, use the default onEnter:
render(<Something />, { wrapper: wrap(withUser(someUser)) });

// Just proved a mock onEnter, use the default user:
render(<Something />, { wrapper: wrap(withOnEnter(jest.fn())) });

// Provide both:
render(<Something />, { wrapper: wrap(withUser(someUser), withOnEnter(jest.fn())) });

I do like having smaller, more specific helpers, with names that make it very obvious which part of the complex thing you are interested in mocking. However, your partial approach is a lot simpler. I think there's room for both approaches in the library.

I'd be open to a PR if you want to do one 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants