React-apollo: MockedProvider with data.fetchMore

Created on 12 Apr 2019  ·  2Comments  ·  Source: apollographql/react-apollo

Intended outcome

I need MockedProvider to return mocked results when a query is re-executed with data.fetchMore

With the following mock:

Feed.mocks.js

import feed from 'feedQuery'; // this is the same query used by the component

[{
request: { // this mock works correctly, on mount
      query: feed,
      variables: {
        limit: 10,
        offset: 0,
        orderBy: 'id_DESC',
      },
},
response:{
// accurate response data, matched query exactly
}},
 { // this is the fetchMore mock
    request: {
      query: feed,
      variables: {
        offset: 0,
        limit: 10,
        orderBy: 'id_ASC',
      },
    },

    result: {
// exact same results as above, but in reverse
// This mock does not work
}
}];

And the following test:

Feed.test.js

import { render, waitForElement, fireEvent } from 'react-testing-library';
import { MockedProvider } from 'react-apollo/test-utils';
import FeedTable from 'FeedTable';
import FEED_MOCk from 'Feed.mocks'
let container;
let getId;

beforeAll(() => {
    ({ container } = render(
      <MockedProvider mocks={FEED_MOCK} addTypename={false}>
        <FeedTable />
      </MockedProvider>
    ));

    getId = id => container.querySelectorAll('tr')[id].childNodes[1].innerHTML;
  });

// Passes ✓ - loading state
 it('should render skeleton while loading', () => {
    expect(container.querySelectorAll('.skeleton__heading').length).toBe(14);
    expect(container.querySelectorAll('.skeleton__text')).toMatchSnapshot();
  });

// Passes ✓ - this moves the table passed its initial loading state,
//  proof that the initial query mock works
it('should render 10 rows by default', async () => {
    await waitForElement(() => container.querySelector('.table-loaded'));
    const numberOfRows = (await waitForElement(() => container.querySelectorAll('tr'))).length;
    expect(numberOfRows).toBe(11); // 1 Header Row + 10 Data Rows = 11;
  });

// Passes ✓ - actually pulling out the Mock ID's
  it('rows should be in descending order by ID by default ', async () => {
    expect(getId(1)).toBe('10');
    expect(getId(2)).toBe('9');
  });

// Fails ✖ - Throw error indicated below at : Error Message
  it('clicking id header should change order to ID Ascending', async () => {
    expect(getId(1)).toBe('10');
    expect(getId(2)).toBe('9');
    const idHeader = container.querySelector('button--table-sort');

    fireEvent.click(idHeader);
    await waitForElement(() =>
      container.querySelctor(
        'selector-indicating-table-loaded)'
      )
    );
    expect(getId(1)).toBe('1');
    expect(getId(1)).toBe('2');
  });

What should happen?
The test that fails, should pass. I should receive my mocked results, which are identical to the first mock, but in reverse ie. Ascending order.

Actual outcome:

I get the no more mocked responses error

Error Message

 Network error: No more mocked responses for the query: query myQuery($limit: Int!, $offset: Int!, $orderBy: OrderBy!, $filter: String) {
      feed(limit: $limit, offset: $offset, orderBy: $orderBy, filter: $filter) {
        myFeed {
          id
        }
        count
      }
    }
    , variables: {"offset":0,"limit":10,"orderBy":"id_ASC"}

Some Context

I have a table component that implements pagination by executing a GraphQL query with updated limit offset and orderBy variables.

When this table component first mounts, the initial query which populates the table is as follows:

feed(limit: $limit, offset: $offset, orderBy: $orderBy, filter: $filter) {
        myFeed {
          id
          description
... other fields
        }
        count
      }
    }
    , variables: {"offset":0,"limit":10,"orderBy":"id_DESC"}

Note: the orderBy is 'is_DESC'

What I'm trying to test:

1) When I click a table header Apollo Client sends an updated GraphQL query to fetch the paginated data.
2) When I click next page, Apollo Client sends an updated GraphQL query to fetch the paginated data.

I am using react-testing-library to simulate clicks, and when a header is clicked the following code is executed:

data.fetchMore({
      fetchPolicy: 'cache-and-network',
      variables: {
        offset,
        limit,
        orderBy: `${key}_${newDirection}`, // the only thing that changes
        filter,
      },
      updateQuery: (previous, { fetchMoreResult }) => {
        setLoading(false);
        return fetchMoreResult || previous;
      },
    });

As you can see we are updating the query and changing only a single variable, the orderBy. Apollo client handles this okay as the error message indicates it is looking for a mock response with orderBy : 'id_ASC' _however it is unable to find one_

I have not got time to create a reproduction repo at the minute, but I will hopefully be able to soon.

Version

Most helpful comment

So I figured out the issue after 3 days of banging my head, going to do a small write up here.

I've read several other threads where people mentioned that the variables declared in the mocks must match exactly the variables being passed to ApolloClient.
But I did not realise an _undefined_ variable would be passed through.

Take the following Query

<Query
query={myQuery}
variables={{
    limit: 10,
    offset: 0,
    filter, // this value is undefined
}}
>
</Query>

Would you expect the following mock to match this query?

request: {
      query: myQuery,
      variables: {
        limit: 10,
        offset: 0
      },
    },

I did, but I was mistaken. So just for future reference - undefined values are still included and must be mocked up as well

All 2 comments

My issue sounds quite similar to https://github.com/apollographql/react-apollo/issues/993

Which was seemingly closed after no responses... worrying

So I figured out the issue after 3 days of banging my head, going to do a small write up here.

I've read several other threads where people mentioned that the variables declared in the mocks must match exactly the variables being passed to ApolloClient.
But I did not realise an _undefined_ variable would be passed through.

Take the following Query

<Query
query={myQuery}
variables={{
    limit: 10,
    offset: 0,
    filter, // this value is undefined
}}
>
</Query>

Would you expect the following mock to match this query?

request: {
      query: myQuery,
      variables: {
        limit: 10,
        offset: 0
      },
    },

I did, but I was mistaken. So just for future reference - undefined values are still included and must be mocked up as well

Was this page helpful?
0 / 5 - 0 ratings