Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

MockedProvider query with Fragments throwing __typename warning #1747

Closed
justinlevi opened this issue Mar 2, 2018 · 24 comments
Closed

MockedProvider query with Fragments throwing __typename warning #1747

justinlevi opened this issue Mar 2, 2018 · 24 comments

Comments

@justinlevi
Copy link

Intended outcome:

When I run my tests I expect them to pass without an error .

Actual outcome:

I get a massive warning about using fragments without __typename

 You're using fragments in your queries, but either don't have the addTypename:
        true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
         Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
         can accurately match fragments.

Here are my tests:
apolloProxy.test.js

/* eslint-disable react/prop-types */

import React, { Component } from 'react';
import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';

import { withCurrentUser, withArticlesByUser, CURRENT_USER_QUERY, ARTICLES_BY_USER_QUERY } from './apolloProxy';
import {
  CURRENT_USER_QUERY_DATA as mockedUserData,
  ARTICLES_BY_USER_DATA as mockedArticlesByUserData,
} from './__mocks__/data';


// source: https://stackoverflow.com/questions/45700550/how-to-use-a-mocked-data-with-react-apollo-for-tests
/*
  Note: By using a Dummy component you just test your graphql()
  Queries and Mutations and the way you have configured them
  (options, props, skip, variables, etc.) So you don't mount your
  ctual React components. It's better to test those in their 'unconnected' state.
*/

describe('Testing GraphQL Queries', () => {
  let done;
  let query;

  class Dummy extends Component {
    componentDidMount() {
      const { loading, data } = this.props.data;

      expect(loading).toBe(true);
      expect(data).toBe(undefined);
    }

    componentWillReceiveProps(nextProps) {
      switch (query) {
        case 'CURRENT_USER_QUERY': {
          const { loading, user } = nextProps.data;
          expect(loading).toBe(false);
          expect(user).toEqual(mockedUserData.data.user);
          break;
        }
        case 'ARTICLES_BY_USER_QUERY': {
          const { loading, user } = nextProps.data;
          expect(loading).toBe(false);
          expect(user).toEqual(mockedArticlesByUserData.data.user);
          break;
        }
        default:
          break;
      }
      done();
    }

    render() {
      return null;
    }
  }

  it('Check Current User Query', (doneCallback) => {
    done = doneCallback;
    query = 'CURRENT_USER_QUERY';
    const variables = { cache: false };
    const DummyWithCurrentUser = withCurrentUser(Dummy);
    const mocks = [
      {
        request: { query: CURRENT_USER_QUERY, variables },
        result: { data: mockedUserData.data },
      },
    ];
    mount(<MockedProvider removeTypename mocks={mocks}><DummyWithCurrentUser /></MockedProvider>);
  });

  it('Check Articles by User Query', (doneCallback) => {
    done = doneCallback;
    query = 'ARTICLES_BY_USER_QUERY';
    const variables = { cache: false };

    const DummyWithArticlesByUser = withArticlesByUser(Dummy);
    const mocks = [
      {
        request: { query: ARTICLES_BY_USER_QUERY, variables },
        result: { data: mockedArticlesByUserData.data },
      },
    ];
    mount(<MockedProvider removeTypename mocks={mocks}><DummyWithArticlesByUser /></MockedProvider>);
  });
});

Here are my queries:
apolloProxy.js


const fragments = {
  nodeArticle: gql`
    fragment ArticleFields on NodeArticle{
      author:entityOwner{
        name
      },
      title,
      body {
        value
      },
      nid,
      uuid
      images:fieldMediaImage{
        mid:targetId,
        ... on FieldNodeFieldMediaImage {
          entity{
            ... on MediaImage {
              image:fieldImage {
                derivative(style:medium) {
                  url
                }
              }
            }
          }
        }
      }
    }
  `,
};


export const CURRENT_USER_QUERY = gql`
  query{
    user: currentUserContext{
      uid,
      uuid
    }
  }
`;
export const currentUser = () => apolloClient.query({
  query: CURRENT_USER_QUERY,
});
export const withCurrentUser = graphql(CURRENT_USER_QUERY);


export const ARTICLES_BY_USER_QUERY = gql`
  query articlesByUserQuery{
    user:currentUserContext{
      ...on User{
        uid
        nodes:reverseUidNode(offset:0, limit:1000){
          articles: entities{
            ... ArticleFields
          }
        }
      }
    }
  }
  ${fragments.nodeArticle}
`;
export const articlesByUser = () => apolloClient.query({
  query: ARTICLES_BY_USER_QUERY,
  fetchPolicy: 'network-only',
});
export const withArticlesByUser = graphql(ARTICLES_BY_USER_QUERY);

Here is my mock data.js:

export const CURRENT_USER_QUERY_DATA = {
  data: {
    user: {
      uid: 1,
      uuid: '4e9af657-689b-4c06-8721-e267914f2255',
    },
  },
};

export const ARTICLES_BY_USER_DATA = {
  data: {
    user: {
      uid: 1,
      nodes: {
        articles: [
          {
            author: {
              name: 'admin',
            },
            title: 'hello article update',
            body: null,
            nid: 13,
            uuid: '79502776-61f8-4c48-b464-d94eebe0e01b',
            images: [],
          },
          {
            author: {
              name: 'admin',
            },
            title: 'asdfads',
            body: {
              value: '<p>asdfasdf</p>\r\n',
            },
            nid: 14,
            uuid: 'be510cd5-645e-4f72-8baa-06cf89f53f84',
            images: [],
          },
        ],
      },
    },
  },
};

Version

"apollo-cache-inmemory": "^1.1.5",
    "apollo-client": "^2.0.3",
    "apollo-client-preset": "^1.0.5",
    "apollo-link": "^1.1.0",
    "apollo-link-http": "^1.3.3",

I have tried adding __typename throughout my fragment and my query but the warning persists. My tests pass, but I'd like to get rid of the warnings if possible.

Reference where I found the above approach:
https://stackoverflow.com/questions/45700550/how-to-use-a-mocked-data-with-react-apollo-for-tests#

Wondering if I need to add an optimisticResponse???
Thanks for any direction

@LeathanTeal
Copy link

@justinlevi I am running into this exact same issue, and haven't been able to find a solution anywhere. Did you happen to ever find anything?

@donedgardo
Copy link

Following this blog post from Apollo also getting these errors/warnings. I'm also using fragments in my queries. I do have the prop in MockProvider as true.

@mptap
Copy link

mptap commented Jul 27, 2018

I'm running into the same issue too, tried to resolve using removeTypename in my MockedProvider, addTypename={false} in MockedProvider, tried putting __typename in my fragment, no joy though. Did anyone find how to move past this?

@LeathanTeal
Copy link

LeathanTeal commented Jul 27, 2018

@mptap I ended up solving this by hand rolling my own MockedProvider with an IntrospectionFragmentMatcher (Apollo Docs on using fragments), and with __typename in the mocked data:

import React from 'react'
import { render } from 'react-testing-library'
import wait from 'waait'

import { ApolloProvider } from 'react-apollo'
import { MockLink } from 'react-apollo/test-utils'
import { ApolloClient } from 'apollo-client'
import {
  InMemoryCache,
  IntrospectionFragmentMatcher,
} from 'apollo-cache-inmemory'

import PostsQuery, { query } from './PostsQuery'

import introspectionQueryResultData from 'fragmentTypes.json'

const getCacheForApollo = () => {
  const fragmentMatcher = new IntrospectionFragmentMatcher({
    introspectionQueryResultData,
  })

  return new InMemoryCache({ fragmentMatcher })
}

const getTestClient = mocks => {
  const cache = getCacheForApollo()

  const link = new MockLink(mocks, true)
  return new ApolloClient({
    link,
    cache,
  })
}

class ResultStore {
  constructor() {
    this.results = {}
  }

  store = ({ loading, ...props }) => {
    if (!props.loading) {
      this.results = props
    }

    return null
  }
}

it('sorts return types by date', async () => {
  const resultStore = new ResultStore()
  const mocks = [
    {
      request: {
        query,
      },
      result: {
        data: {
          posts: [...],
        },
      },
    },
  ]

  render(
    <ApolloProvider client={getTestClient(mocks)}>
      <PostsQuery>{resultStore.store}</PostsQuery>
    </ApolloProvider>
  )

  await wait(0)

  expect(resultStore.results.posts).toEqual([...])
})

Hope this helps!

@typical000
Copy link

Any updates here? The only solution for me, and for now is to add __typename to every fragment inside entire application:

fragment CommonUserId on CommonUser {
  id
  __typename
}

But... why I need to add typename only for fragments but not to queries and mutations? :)
Also, addTypename is set to true

@cmbirk
Copy link

cmbirk commented Sep 14, 2018

Having the same issue in testing. Not sure what the best solution is, as addTypename doesn't seem to have any effect

@myleshyson
Copy link

Hey any update on this? I'm having the same issue testing. Here's my test file.

/* eslint-env jest */
import React from 'react';
import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';
import ServiceSection from '../../components/ServiceSection';
import { GET_SERVICES } from '../../queries';

const servicesMock = {
  request: {
    query: GET_SERVICES
  },
  result: {
    data: {
      services: {
        all: [
          {
            title: 'Servie 1',
            slug: 'service-1',
            excerpt: 'Some Excerpt',
            content: '<p>Some Content</p>',
            featuredImage: {
              sourceUrl: 'http://test.com/service-1.png',
              mediaDetails: {
                sizes: [
                  {
                    name: 'medium_large',
                    sourceUrl: 'http://test.com/service-1.png',
                    width: 700
                  },
                  {
                    name: 'large',
                    sourceUrl: 'http://test.com/service-1.png',
                    width: 700
                  }
                ]
              }
            }
          },
          {
            title: 'Servie 2',
            slug: 'service-2',
            excerpt: 'Some Excerpt 2',
            content: '<p>Some Content 2</p>',
            featuredImage: {
              sourceUrl: 'http://test.com/service-2.png',
              mediaDetails: {
                sizes: [
                  {
                    name: 'medium_large',
                    sourceUrl: 'http://test.com/service-2.png',
                    width: 700
                  },
                  {
                    name: 'large',
                    sourceUrl: 'http://test.com/service-2.png',
                    width: 800
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
};

describe('Services Section', () => {
  it('renders when given query', async () => {
    const wrapper = mount(
      <MockedProvider mocks={[servicesMock]} addTypename={false}>
        <ServiceSection />
      </MockedProvider>
    );
    await new Promise(resolve => setTimeout(resolve));
    wrapper.update();
  });
});

And here's the errors I get

yarn run v1.10.1
warning ../../../package.json: No license field
$ NODE_ENV=test jest --verbose __tests__/components/services.test.js
 PASS  __tests__/components/services.test.js
  Services Section
    ✓ renders when given query (56ms)

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
    You're using fragments in your queries, but either don't have the addTypename:
      true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
       Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
       can accurately match fragments.

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
    Could not find __typename on Fragment  Service { title: 'Servie 1',
      slug: 'service-1',
      excerpt: 'Some Excerpt',
      content: '<p>Some Content</p>',
      featuredImage:
       { sourceUrl: 'http://test.com/service-1.png',
         mediaDetails: { sizes: [Array] } } }

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
    DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.

  console.error node_modules/apollo-cache-inmemory/lib/bundle.umd.js:786
    WARNING: heuristic fragment matching going on!

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
    Missing field __typename in {
      "title": "Servie 1",
      "slug": "service-1",
      "excerpt": "Some Excerpt",
      "content": "<p>Some C

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
    Missing field id in {
      "title": "Servie 1",
      "slug": "service-1",
      "excerpt": "Some Excerpt",
      "content": "<p>Some C

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
    You're using fragments in your queries, but either don't have the addTypename:
      true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
       Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
       can accurately match fragments.

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
    Could not find __typename on Fragment  Service { title: 'Servie 2',
      slug: 'service-2',
      excerpt: 'Some Excerpt 2',
      content: '<p>Some Content 2</p>',
      featuredImage:
       { sourceUrl: 'http://test.com/service-2.png',
         mediaDetails: { sizes: [Array] } } }

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
    DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.

  console.error node_modules/apollo-cache-inmemory/lib/bundle.umd.js:786
    WARNING: heuristic fragment matching going on!

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
    Missing field __typename in {
      "title": "Servie 2",
      "slug": "service-2",
      "excerpt": "Some Excerpt 2",
      "content": "<p>Some

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
    Missing field id in {
      "title": "Servie 2",
      "slug": "service-2",
      "excerpt": "Some Excerpt 2",
      "content": "<p>Some

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
    You're using fragments in your queries, but either don't have the addTypename:
      true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
       Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
       can accurately match fragments.

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
    Could not find __typename on Fragment  Service { title: 'Servie 1',
      slug: 'service-1',
      excerpt: 'Some Excerpt',
      featuredImage:
       { type: 'id',
         generated: true,
         id: '$ROOT_QUERY.services.nodes.0.featuredImage',
         typename: undefined } }

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
    DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
    You're using fragments in your queries, but either don't have the addTypename:
      true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
       Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
       can accurately match fragments.

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
    Could not find __typename on Fragment  Service { title: 'Servie 2',
      slug: 'service-2',
      excerpt: 'Some Excerpt 2',
      featuredImage:
       { type: 'id',
         generated: true,
         id: '$ROOT_QUERY.services.nodes.1.featuredImage',
         typename: undefined } }

  console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
    DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.

@thanpolas
Copy link

This is quite an annoying problem, what is the update on this?

@rosskevin
Copy link
Contributor

This looks to be an accurate warning about misusing fragments without a fragmentMatcher.

Here is an example bit of code I use to setup my provider:

<MockedProvider cache={createApolloCache(require('../../../../fragmentTypes.json')}>

with

import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import { IntrospectionResultData } from 'apollo-cache-inmemory/lib/types'

export default function createApolloCache(introspectionQueryResultData: object) {
  return new InMemoryCache({
    fragmentMatcher: new IntrospectionFragmentMatcher({
      introspectionQueryResultData: introspectionQueryResultData as IntrospectionResultData,
    }),
  })
}

@michaelhankin
Copy link

Thanks for the response. Is there any way we could get this requirement reflected/further explained in documentation somewhere?

@rosskevin
Copy link
Contributor

Please feel free to PR a change to the component docs or the site docs and add me as a reviewer, I'll be happy to take a look at what you come up with.

@damiangreen
Copy link

damiangreen commented Sep 16, 2019

So I've read the comments but I don't see the solution. I tried @rosskevin's solution
but it then rendered empty components.

I also tried adding

defaultOptions={{
          watchQuery: {
            fetchPolicy: 'network-only',
          },
        }}

but had same issue

@madisonbullard
Copy link

madisonbullard commented Nov 13, 2019

We use IntrospectionFragmentMatcher in production, but had just gotten away with using <MockedProvider addTypename={false}> in tests to avoid needing to add __typeName keys to every stub in our tests. At a certain point that stopped working with Fragments, as described in the comments above.

I found a solution to avoid updating all the stubs with __typename:

import {
  IntrospectionFragmentMatcher,
  InMemoryCache,
} from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../constants/fragmentTypes.json';

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData,
});

// ...then in the test:

<MockedProvider
  mocks={mocks}
  addTypename={false}
  cache={
    new InMemoryCache({
      addTypename: false,
      fragmentMatcher,
    })
  }
>

Setting addTypename to false both as a prop on MockedProvider and in the InMemoryCache() call was the key to getting things to pass.

Hope that helps 😃

@ryanirilli
Copy link

@madisonbullard where does fragmentTypes.json come from?

@czabaj
Copy link

czabaj commented Dec 6, 2019

May be a little bit dangerous and I haven't test it extensivelly, but instead of putting __typename everywhere in the mocks, I have just used super simple fragment matcher which don't complaints 😄

const happyFragmentMatcher = { match: () => true }

<MockedProvider
  mocks={mocks}
  addTypename={false}
  cache={
    new InMemoryCache({
      addTypename: false,
      fragmentMatcher: happyFragmentMatcher,
    })
  }
>

@madisonbullard
Copy link

@madisonbullard where does fragmentTypes.json come from?

@ryanirilli You can read about GraphQL introspection here: https://graphql.org/learn/introspection/

We copy/paste the results of a __schema query into our fragmentTypes.json file. Our query looks like this:

{
  __schema {
    types {
      kind
      name
      possibleTypes {
        name
      }
    }
  }
}

@vctormb
Copy link

vctormb commented Dec 12, 2019

We use IntrospectionFragmentMatcher in production, but had just gotten away with using <MockedProvider addTypename={false}> in tests to avoid needing to add __typeName keys to every stub in our tests. At a certain point that stopped working with Fragments, as described in the comments above.

I found a solution to avoid updating all the subs with __typename:

import {
  IntrospectionFragmentMatcher,
  InMemoryCache,
} from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../constants/fragmentTypes.json';

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData,
});

// ...then in the test:

<MockedProvider
  mocks={mocks}
  addTypename={false}
  cache={
    new InMemoryCache({
      addTypename: false,
      fragmentMatcher,
    })
  }
>

Setting addTypename to false both as a prop on MockedProvider and in the InMemoryCache() call was the key to getting things to pass.

Hope that helps 😃

I tried this example. I generated the fragmentTypes and added addTypename to false, but in my tests the request is failing trying to find the __typename in the object. Is there a solution for it?

@MYKEU
Copy link

MYKEU commented Dec 13, 2019

Same as above - if I remove __typename from my query, my test seems to fail.

@zhouzi
Copy link

zhouzi commented Feb 4, 2020

This looks to be an accurate warning about misusing fragments without a fragmentMatcher.

Here is an example bit of code I use to setup my provider:

<MockedProvider cache={createApolloCache(require('../../../../fragmentTypes.json')}>

with

import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import { IntrospectionResultData } from 'apollo-cache-inmemory/lib/types'

export default function createApolloCache(introspectionQueryResultData: object) {
  return new InMemoryCache({
    fragmentMatcher: new IntrospectionFragmentMatcher({
      introspectionQueryResultData: introspectionQueryResultData as IntrospectionResultData,
    }),
  })
}

Perhaps there's something missing in this answer but it's not working as is.

I tried @madisonbullard's suggestion to set addTypename to false in the InMemoryCache as well but as pointed by others, it's now throwing the following error:

Error: Network error: Error writing result to store for query: ...
Cannot match fragment because __typename property is missing: ...

@smeijer
Copy link

smeijer commented Feb 9, 2020

I'm having the same as @zhouzi, worth to say; the MockProvider now does return (generate?) some data, but it never reaches the component due to the failed write.

@luyanrock
Copy link

I also had the same as @zhouzi and @smejier, is there a solution for this?

@drudv
Copy link

drudv commented Mar 16, 2020

Also tried to pass addTypename: false to both MockedProvider and InMemoryCache, but but the issue still persists.

@deltek-rossjackson
Copy link

deltek-rossjackson commented Mar 24, 2020

I just disabled my caching since cache takes the __typename and id

<MockedProvider
                mocks={ ... }
                addTypename={false}
                defaultOptions={{
                    watchQuery: { fetchPolicy: 'no-cache' },
                    query: { fetchPolicy: 'no-cache' },
                }}
            > ...

@zhouzi
Copy link

zhouzi commented Mar 25, 2020

I just disabled my caching since cache takes the __typename and id

<MockedProvider
                mocks={ ... }
                addTypename={false}
                defaultOptions={{
                    watchQuery: { fetchPolicy: 'no-cache' },
                    query: { fetchPolicy: 'no-cache' },
                }}
            > ...

I confirm that adding the defaultOptions as suggested by @deltek-rossjackson got rid of the warnings, thanks 👍 I guess we just have to be careful that our tests are not relying on the cache, which is probably not the case.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests