You must register a useQuery hook via the `GraphQLHooksProvider`

I made a custom hook and I’m trying to write a test for it:

hook

import { useQuery } from '@redwoodjs/web';

import { firebaseClient } from 'src/auth';

const GET_TENANT = gql`
  query getTenant($domain: String!) {
    tenantByDomain(domain: $domain) {
      identityPlatformId
      domain
    }
  }
`;

const useTenant = () => {
  const firebaseAuth = firebaseClient?.firebaseAuth;
  const auth = firebaseAuth.getAuth();
  const subdomain = window.location.host.split('.')[0];
  const tenant = useQuery(GET_TENANT, { variables: { domain: subdomain } });

  return {
    auth,
    tenantId: tenant?.data?.tenantByDomain?.identityPlatformId,
  };
};

export default useTenant;

test

import { renderHook } from '@testing-library/react-hooks';

import useTenant from './use-tenant';

describe('useTenant', () => {
  it('gets tenantId', () => {
    const expectedTenantId = 'test-tenant-id';

    mockGraphQLQuery('getTenant', (_variables) => {
      return { tenantId: expectedTenantId };
    });

    const { result } = renderHook(() => useTenant());

    expect(result.current).toEqual(expectedTenantId);
  });
});

I’m getting the error

You must register a useQuery hook via the GraphQLHooksProvider

According to the documentation I found here Testing | RedwoodJS Docs I would have thought all I needed was mockGraphQLQuery but I guess I’m missing something else. Any ideas?

Hi @LarkSoftware I’ve never tried to do that before, but I wonder if beforeQuery might work here.

See: Cells | RedwoodJS Docs

beforeQuery is a lifecycle hook. The best way to think about it is as an API for configuring Apollo Client’s Query component (so you might want to check out Apollo’s docs for it).

By default, beforeQuery gives any props passed from the parent component to Query so that they’re available as variables for QUERY. It’ll also set the fetch policy to ‘cache-and-network’ since we felt it matched the behavior users want most of the time.

So, perhaps:

// The Cell will take no props: <Cell />
export const beforeQuery = () => {
  return {
    variables: { domain: window.location.host.split('.')[0] }
   }
}

Also, are you implementing Firebase auth on your own or using the RedwoodJS Firebase setup?

Hi @LarkSoftware I’ve never tried to do that before, but I wonder if beforeQuery might work here.

Just to be clear, my custom hook works great. It is the test I’m trying to write for it that I’m having trouble with. For some reason the testing framework doesn’t like my use of useQuery it want’s me to register it somehow. I thought mockGraphQlQuery would take care of that for me, but it does not seem to. I do not get this error when I run the hook in the browser, only when I try to create a test for it.

Yes, I got that new error too.

Seems like you can wrap the component in your test with

        <GraphQLHooksProvider
          useMutation={jest.fn().mockReturnValue([jest.fn(), {}])}
          useQuery={jest.fn().mockReturnValue({ data: {} })}
        >...</GraphQLHooksProvider>

But not sure if that’s the right way to do?

1 Like

I was just coming here to post that I have made it past my initial error:

You must register a useQuery hook via the GraphQLHooksProvider

The problem was that I was importing renderHook from the wrong place.

// wrong
import { renderHook } from '@testing-library/react-hooks';
// right
import { renderHook } from '@redwoodjs/testing/web';

Now things are looking much better except my mockGraphQLQuery is getting fired after the expect call. This means the result has not been populated by the time it gets evaluated. I’m still trying to track this down but I’m done for today. I’ll take another look tomorrow.

Actually I finally got it working. Here is what I came up with:

import { renderHook, waitFor } from '@redwoodjs/testing/web';

jest.mock('../auth');

import useTenant from './use-tenant';

describe('useTenant', () => {
  it('gets tenantId', async () => {
    const expectedTenantId = 'test-tenant-id';

    mockGraphQLQuery('getTenant', (variables) => {
      return {
        tenantByDomain: {
          identityPlatformId: expectedTenantId,
          domain: variables?.domain,
        },
      };
    });

    const { result } = renderHook(() => useTenant());

    await waitFor(() => {
      return expect(result?.current?.tenantId).toEqual(expectedTenantId);
    });
  });
});

Thank you to everyone that offered help.

1 Like

This works for Storybook where I’m seeing the error, but am also not seeing it in any of the RedwoodJS tutorials, so it doesn’t quite seem like it should need to be done in every story that is for a component with a GraphQL mutation in it…

This lead me to this, which worked for me.

<GraphQLHooksProvider
          useMutation={jest.fn().mockReturnValue([jest.fn(), {}])}
          useQuery={jest.fn().mockReturnValue({ data: {} })}
          useSubscription={jest.fn().mockReturnValue({ data: {} })}
>
        ...
</GraphQLHooksProvider>
1 Like

The details in this thread about node_module nesting could be at play here as well:

Does anyone’s project have multiple instances of @redwoodjs/web? Especially in @redwoodjs/testing:

ls ./node_modules/@redwoodjs/testing/node_modules/
?