apollographql / react-apollo

:recycle: React integration for Apollo Client
https://www.apollographql.com/docs/react/
MIT License
6.85k stars 787 forks source link

MockedProvider: Unable to mix @client and server items in a query #3642

Open pdemarino opened 5 years ago

pdemarino commented 5 years ago

Intended outcome:

@client and server elements should all be resolved by MockProvider

Actual outcome:

Queries that mix @client and server elements are not resolved. Errors are generated, such as:

{"graphQLErrors":[],"networkError":{},"message":"Network error: No more mocked responses for the query: query getDog {\n dog @client {\n id\n name\n breed\n }\n visibility {\n value\n }\n}\n, variables: {}"}

How to reproduce the issue:

See this sandbox:

https://codesandbox.io/s/apollo-client-error-template-kpszz?fontsize=14

Version See sandbox.

gh0stl1m commented 5 years ago

I'm having the same issue that you mention above with a query that use the @client directive:

export const GET_APPLICATION_DETAILS = gql`
  query GetApplicationDetails($appId: String!) {
    applicationDetails(appId: $appId) @client
  }
`;

And i notice that the mockedProvider returns undefined when a query uses the @client directive. I try to implement different workarounds mentioned in other issues but it doesn't work, my last attempt was update the library to the latest version because the team merge a change that solves some issues with the @client directive (https://github.com/apollographql/react-apollo/pull/2524) and it was merged to the version 2.5.8 but in the specific case that you use the client directive in a query it always returns undefined even if you pass the resolver prop to the mockedProvider, my test is the following:

  it('Given an application the it must returns all his details', async () => {
    // Arrange
    const app = { ...testApplication };
    const applicationDetailsMock = [
      {
        request: { query: GET_APPLICATION_DETAILS, variables: { appId: app.id } },
        result: {
          data: {
            applicationDetails: testApplicationDetails,
          },
        },
      },
    ];
    const localResolver = { Query: { applicationDetails: () => testApplicationDetails } };
    const wrapper = new MockRender(<ApplicationDetails application={app} />);

    // Act
    const { getByText } = wrapper.render({
      mocks: applicationDetailsMock,
      addTypename: false,
      resolvers: localResolver,
    });

    // Asserts
    await waitForExpect(() => {
      expect(getByText('LastUpdated')).toBeVisible();
      expect(getByText('Runtime Version:')).toBeVisible();
      expect(getByText('Worker size:')).toBeVisible();
      expect(getByText('Workers:')).toBeVisible();
      expect(getByText('Region:')).toBeVisible();
      expect(getByText('Manage application')).toBeVisible();
      expect(getByText('Logs')).toBeVisible();
      expect(getByText('Insight')).toBeVisible();
    });

  });

The class MockRender is an implementation of the MockedProvider as you can see here:

class MockRender {
  constructor(node) {
    this.client = initApollo({
      getContext: () => ({
        environment: {
          id: 'test-id',
          organizationId: 'test-org-id',
          profile: {
            access_token: 'test-token',
          },
        },
      }),
    });

    this.node = node;
  }

  render({ mocks, addTypename, defaultOptions, cache, resolvers } = {}) {
    return render(
      <ApolloProvider client={this.client}>
        <BrowserRouter>
          <MockedProvider
            mocks={mocks}
            addTypename={addTypename}
            defaultOptions={defaultOptions}
            cache={cache}
            resolvers={resolvers}
          >
            {this.node}
          </MockedProvider>
        </BrowserRouter>
      </ApolloProvider>
    );
  }
}
freshollie commented 4 years ago

@gh0stl1m I did some digging into this, and found that the tests for MockProvider on @client fields do work with resolvers, but only if you use HOC. https://github.com/apollographql/react-apollo/blob/f14c243094f85448f3eeb9810b28e4635867ca51/packages/hoc/src/__tests__/MockedProvider.test.tsx#L662

When I wrote the same test with useQuery then as you say, the data resolves as undefined:

   const Something = (): null => {
      const { data } = useQuery(productQuery);

      console.log(data);
      if (data) {
        done();
      }
      return null;
    };

    const resolvers = {
      Product: {
        isInCart() {
          return true;
        }
      }
    };

    render(
      <MockedProvider mocks={productMocks} resolvers={resolvers}>
        <Something />
      </MockedProvider>
    );

Seems this is both related to MockProvider and useQuery hook.

For now, we are just moving our type definitions for client fields to the server, and our local mutations update the cached values, so the query never has to use @client directives on fields. Wish this wasn't a bug though.

mariorcardoso commented 4 years ago

I had a similar issue, it happened when I upgraded from react-apollo 2.5.2 to 2.5.3. It worked fine on version 2.5.2. It must be something introduced in 2.5.3, I'm looking at the release changes but haven't found the reason why this stopped working. In my case, the component was working just fine but the tests were failing. I ended up refactoring the component and splitting the queries (one for @client and one for server) and all works fine. As long as they are not mixed the tests are okay.