blitz icon indicating copy to clipboard operation
blitz copied to clipboard

getQueryKey does not work in jest

Open kamkry opened this issue 3 years ago • 7 comments

What is the problem?

When I try to use the getQueryKey function in a test environment, I get Cannot read property 'apiUrl' of undefined error. From what I see it is caused by a non-existent _meta prop on the resolver. An easy solution that comes to mind is to just add optional chaining like this:

// getQueryKey
return getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._meta?.apiUrl, params)

but maybe it's a bigger problem than this. Thanks for your help in advance!

Paste all your error logs here:

TypeError: Cannot read property 'apiUrl' of undefined
    at getQueryKey (node_modules/@blitzjs/core/dist/blitzjs-core.cjs.dev.js:325:67)

Paste all relevant code snippets here:

 getQueryKey(getCurrentUser);

What are detailed steps to reproduce this?

Just run frontend jest test on a component that is using getQueryKey function

Run blitz -v and paste the output here:

macOS Big Sur | darwin-x64 | Node: v14.17.0

blitz: 0.33.1 (global)
blitz: 0.38.1 (local)

  Package manager: yarn 
  System:
    OS: macOS 11.0.1
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 2.14 GB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.17.0 - ~/.nvm/versions/node/v14.17.0/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 6.14.13 - ~/.nvm/versions/node/v14.17.0/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  npmPackages:
    @prisma/client: 2.26.0 => 2.26.0 
    blitz: 0.38.1 => 0.38.1 
    prisma: 2.26.0 => 2.26.0 
    react: 18.0.0-alpha-568dc3532 => 18.0.0-alpha-568dc3532 
    react-dom: 18.0.0-alpha-568dc3532 => 18.0.0-alpha-568dc3532 
    typescript: 4.4.0-dev.20210702 => 4.4.0-dev.20210702 

kamkry avatar Jul 10 '21 14:07 kamkry

Thank you for bringing this up.

I think you're right, but I think the actual problem with that file is that as of https://github.com/blitz-js/blitz/blob/fcb2782b332900ebecbf8d27050a25cc7c8b7c0b/packages/core/src/utils/react-query-utils.ts#L91 the Type system is lying to you, claiming that the resolver is enhanced when (in the context of running Jest) it isn't an enhanced resolver at all.

That seems like a bigger problem, because if you can't trust the type system, then it loses its value.

I think it would be better if when the utility is validating a resolver in a test environment, instead of turning TypeScript into a liar, it appended some mock _meta information to the resolver if it isn't already present.

Something like:

if (!('_meta' in queryFn) && !isNotInUserTestEnvironment()) {
  queryFn["_meta"] = {
     name: queryFn.name || "testResolver",
     type: "query", // should be "query" or "mutation" but the checks are disabled for tests so I don't think it matters
     filePath: "filePath",
     apiUrl: "apiUrl",
  }

  return queryFn as EnhancedResolverRpcClient<TInput, TResult>;
}

MrLeebo avatar Jul 14 '21 23:07 MrLeebo

Heads up: the _meta implementation is changing in https://github.com/blitz-js/blitz/pull/2516 so no one work on this until that is merged.

flybayer avatar Jul 16 '21 23:07 flybayer

This bug causes all useQuery calls that are at the top level of any BlitzPage component to throw the error Cannot read property 'apiUrl' of undefined. Is there a recommended solution until #2516 is done? My workaround is to wrap every top-level useQuery in its own hook so that I can mock the custom hook instead of useQuery.

jaydev avatar Jul 27 '21 23:07 jaydev

@jaydev if you need, we can merge a PR to fix this before #2516 is done.

flybayer avatar Jul 29 '21 18:07 flybayer

@flybayer That would be super swell! Let me know if there's any way I can help out here.

jaydev avatar Jul 30 '21 00:07 jaydev

@jaydev sorry for the delay here. You're welcome to make a PR for it if you want!

flybayer avatar Aug 04 '21 15:08 flybayer

Maybe this will help someone

I tired to mock

 import { useQuery } from 'blitz';

but it didn't work.

So I tried to mock

import { useQuery } from '@blitzjs/core';

instead and it worked!

import { useQuery } from '@blitzjs/core';

jest.mock('@blitzjs/core', () => {
  return {
    // @ts-ignore
    ...jest.requireActual('@blitzjs/core'),
    useQuery: jest.fn(),
  };
});

and then in test


it('should do something', () => {
  (useQuery as jest.Mock).mockReturnValue([
        null,
        {
          refetch: jest.fn(() => {
            return Promise.resolve({ data: 'data' });
          }),
        },
   ]);
})

jayu avatar Dec 08 '21 10:12 jayu