-1

I get the below error while trying to unit test a component which uses usePreloadedQuery:

Error: A React component suspended while rendering, but no fallback UI was specified.

Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.

I have a parent component that receives a query reference, and it is using usePreloadedQuery to retrieve the data as shown below:

type Props = {
   queryReference: PreloadedQuery<parentComponentQuery>,
};

export const ParentComponent = ({ queryReference }: Props) => {
  const { data } = usePreloadedQuery(
    graphql`
        query parentComponentQuery($someId: ID!) {
            something(someId: $someId) {
                    ...childComponentFragment
                }
            }
        }
    `,
    queryReference,
  );

  return (
      <div>
         <ChildComponent fragmentRef={data.childData} />
      </div>
   )
}

The queryReference here is created via call to loadQuery, which is handled by an internal hook, that is a wrapper around react-resource-router's useResource for relay resources.


I'm trying to test this component using Jest and Enzyme with the following code:

let queryReference;
const mockRelayPayload = () => {
  const environment = createMockEnvironment();
  environment.mock.queueOperationResolver((operation) =>
    MockPayloadGenerator.generate(operation, {
        Something: () => ({
            // ... data
        }),
    }),
  );
  environment.mock.queuePendingOperation(QUERY, {});

  act(() => {
     queryReference = loadQuery(environment, QUERY, {}); // fire mocked API call
  });

  return environment;
};

it('should render correctly', () => {
    const environment = mockRelayPayload();
    const wrapper = mount(
        <RelayEnvironmentProvider environment={environment}>
            {/*<Suspense fallback={<div>Loading... </div>}>*/}
               <ParentComponent  queryReference={queryReference}/>
            {/*</Suspense>*/}
        </RelayEnvironmentProvider>
    );
    
    expect(wrapper.find(Wrapper)).toExist();
});

According to the docs Testing Relay Components:

in order for the usePreloadedQuery hook to not suspend, one must call these functions:

  • queueOperationResolver(resolver)
  • queuePendingOperation(query, variables)
  • preloadQuery(mockEnvironment, query, variables) with the same query and variables that were passed to queuePendingOperation. preloadQuery must be called after queuePendingOperation

I'm doing the first two steps, but I’m not able to import preloadQuery from react-relay, based on github source/flowtypes looks like it is deprecated/removed. So instead I'm using loadQuery as in

act(() => {
     queryReference = loadQuery(environment, QUERY, {}); // fire mocked API call
});

But it throws the error mentioned earlier.

As you can tell from the commented code, I've also tried using <Suspense> as suggested in the error, unfortunately it throws Error: Enzyme Internal Error: unknown node with tag 2 from enzyme-adapter-react-16

T J
  • 42,762
  • 13
  • 83
  • 138

1 Answers1

0

Finally found the issue after days of struggle, it was here:

  environment.mock.queuePendingOperation(QUERY, {});

  act(() => {
     queryReference = loadQuery(environment, QUERY, {});
  });

This should be

environment.mock.queuePendingOperation(QUERY, { someId: "value" });

let queryReference;
act(() => {
    queryReference = loadQuery(environment, QUERY, { someId: "value" }); 
});

It seems like when you use queueOperationResolver, it'll only resolve if the query "signature" exactly matches the query and variables you pass to queuePendingOperation and loadQuery.

In my case I was passing an empty variables object {} instead of { someId: "value" } so the resolver wasn't resolving the query at all.

Unfortunately the error message thrown here isn't very useful, instead of saying the query is never resolved it just tells you to use <Suspense> which is a bit misleading.

T J
  • 42,762
  • 13
  • 83
  • 138