EasyGraphQL / easygraphql-tester

Test GraphQL queries, mutations and schemas on an easy way! 🚀
https://easygraphql.com/docs/easygraphql-tester/overview
MIT License
314 stars 34 forks source link

queryParser with fragments #63

Closed artola closed 5 years ago

artola commented 5 years ago

I am using Github API v4 schema (see attached file), for a simple query: "show me the issues of the actual viewer".

Running this query produce an error:

There is no query called issues on the Schema

because "issues" is not wrapped by "viewer", then not matching the schema. I debugged the code, and it is coming from: https://github.com/EasyGraphQL/easygraphql-tester/blob/a9931d460ce560fcdf7395ca923c3643aef7026b/lib/queryParser.js#L165

easygraphql-tester = v3.0.4

@estrada9166 do you have some hint regarding handling fragments? may be some example?

const appQuery = graphql`
  query appQuery {
    viewer {
      ...issues_viewer
    }
  }

  fragment issues_viewer on User
    @argumentDefinitions(
      count: {type: "Int", defaultValue: 10}
      cursor: {type: "String"}
      orderBy: {
        type: "IssueOrder"
        defaultValue: {field: CREATED_AT, direction: DESC}
      }
    ) {
    issues(first: $count, after: $cursor, orderBy: $orderBy)
      @connection(key: "viewer_issues") {
      edges {
        node {
          ...issuesNode @relay(mask: false)
        }
      }
    }
  }

  fragment issuesNode on Issue @relay(mask: false) {
    id
    title
    repository {
      name
    }
    viewerDidAuthor
    state
  }
`;

schema.graphql.txt

artola commented 5 years ago
    const q1 = `
      query trialQuery {
        viewer {
          issues(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) {
            edges {
              node {
                id
                title
                repository {
                  name
                }
                viewerDidAuthor
                state
              }
            }
          }
        }
      }
    `;

    tester.test(true, q1); // => OK = no fragments, no problems ;)

    const q2 = `
      query trialQuery {
        viewer {
          ...trial_viewer
        }
      }

      fragment trial_viewer on User {
        issues(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) {
          edges {
            node {
              id
              title
              repository {
                name
              }
              viewerDidAuthor
              state
            }
          }
        }
      }
    `;

    tester.test(true, q2); // => throws `There is no query called issues on the Schema`
estrada9166 commented 5 years ago

@artola Thank you so much for creating this issue!! I'll push the fix soon!

estrada9166 commented 5 years ago

@artola There's a new version that solves this issue v3.0.5 can you try please, so we can close this issue!!

Also, thank you for reporting the issue like this, with all this information, it was really useful!

artola commented 5 years ago

@estrada9166 Just now installed and testing. Tests that were not passing before, now are OK. Thanks a lot !!!

I did a setup using your comment ... the line with return mockedQuery.json() might be just return mockedQuery ?

estrada9166 commented 5 years ago

@artola That's awesome!!

I did a setup using your comment ... the line with return mockedQuery.json() might be just return mockedQuery ?

Yes, it should return a mock of the requested query, is it working for you?

artola commented 5 years ago

@estrada9166 Going back to my app implementation, I have some problems now with the fixture implementation,

Error: viewer is not called on the query, and it's on the fixture.

Probably from:

function handleObjectFixture(mock, fixture) {
  for (const val of Object.keys(fixture)) {
    if (typeof mock[val] === 'undefined') {
      throw new Error(
        `${val} is not called on the query, and it's on the fixture.`,
      );
    }
export let fixtures: Any = {
  appQuery: {
    viewer: {
      issues: {
        edges: [
          {
            node: {
              id: 'MDU6SXNzdWUzOTg3OTg1MjE=',
              title: 'test 25',
              repository: {
                name: 'test',
                id: 'MDEwOlJlcG9zaXRvcnkxNjU2MjkwMDc=',
              },
              viewerDidAuthor: true,
              state: 'OPEN',
              __typename: 'Issue',
            },
            cursor: 'Y3Vyc29yOnYyOpK5MjAxOS0wMS0xNFQxMDowNTo0NSswMTowMM4XxS65',
          },
...
export const mockQuery = (operation: Any, variables: Any) => {
  console.log('===>', operation.name, '<==='); // => appQuery

  return tester.mock({
    query: operation.text,
    variables,
    fixture: fixtures[operation.name],
  });
};

some hint?

estrada9166 commented 5 years ago

@artola Sorry for that! The way to solve that with fixtures is creating the fixture like this:

export let fixtures: Any = {
  appQuery: {
    issues: {
      edges: [
        {
          node: {
            id: 'MDU6SXNzdWUzOTg3OTg1MjE=',
            title: 'test 25',
            repository: {
              name: 'test',
              id: 'MDEwOlJlcG9zaXRvcnkxNjU2MjkwMDc=',
            },
            viewerDidAuthor: true,
            state: 'OPEN',
            __typename: 'Issue',
          },
          cursor: 'Y3Vyc29yOnYyOpK5MjAxOS0wMS0xNFQxMDowNTo0NSswMTowMM4XxS65',
        },
...

On the fixture, you don't have to set the name of the query, just the fields!

Let me know if it works for you!

artola commented 5 years ago

@estrada9166 I have this situation with the fixture:

Error: viewer is not called on the query, and it's on the fixture.

export let fixtures: Any = {
  appQuery: {
     viewer: {
        issues: {
          edges: [
            {
              node: {
                id: 'MDU6SXNzdWUzOTg3OTg1MjE=',
...

Looking at:

https://github.com/EasyGraphQL/easygraphql-tester/blob/246fae5e4a4cf46cd7f4afed8866cc6538651bc8/utils/fixture.js#L21

I can see:

// mock
{
  issues: {edges: [[Object]], pageInfo: {endCursor: null, hasNextPage: false}},
  id: '50',
}
// fixture
{
  viewer: {
    issues: {edges: [Array], pageInfo: [Object]},
    id: 'MDQ6VXNlcjExNTAwNzYz',
  },
}

This is the reason than later throws, because mock does not contains the key viewer.

https://github.com/EasyGraphQL/easygraphql-tester/blob/246fae5e4a4cf46cd7f4afed8866cc6538651bc8/utils/fixture.js#L28

In both cases, there is nothing in the store _recordSource is empty.

estrada9166 commented 5 years ago

I have replicated the relay-example using easygraphql-tester, I'll push the example soon, but now I have the solution for you (I think so).

In order to make it work with relay you have to do it like this:

export const mockQuery = (operation: Any, variables: Any) => {
  console.log('===>', operation.name, '<==='); // => appQuery

  const mockedQuery = tester.mock({
    query: operation.text,
    variables,
    fixture: fixtures[operation.name],
  });

  const response = {
    "data": mockedQuery
  }
  return response
};

We where missing 2 things:

Also, with the mocks, it'll add the query name on the top of the mock, and with the fixture, use it without the query name, like this

Try it please and let me know, if it works for you, I'll update this comment

artola commented 5 years ago

@estrada9166 Thanks for your prompt reply and lot of help.

I did several changes, and "magically" works :)

Seems related with mock validations (checking if the mock contains a field provided in the fixture). I did a quick and dirty, does not mean that cover all the cases neither is recommended, just that my query with fragments works.

Still I need to review it and clean it, as minimum:

query appQuery {
  viewer {
    # added field: login
    login
    issues( ...
function handleObjectFixture(mock, fixture) {
  if (typeof mock === 'undefined') {
    return fixture;
  }
      for (const arrVal of fixture[val]) {
        if (isObject(arrVal)) {
          // check if mock[val] exists
          // Error: Cannot read property '0' of undefined
          const result = mock[val] ? handleNestedObjects(mock[val][0], arrVal) : arrVal;
          fixtureArr.push(result);
  for (const val of Object.keys(fixture)) {
    // commented because edges does not exists on mock
    // `Error: edges is not called on the query, and it's on the fixture.
    // if (typeof mock[val] === 'undefined') {
    //   throw new Error(
    //     `${val} is not called on the query, and it's on the fixture.`,
    //   );
    // }
function handleObjectFixture(mock, fixture) {
  // support extra fields in fixture
  // Error: Cannot read property 'endCursor' of undefined
  if (typeof mock === 'undefined') {
    return fixture;
  }

@estrada9166 looking your forward, here at 3:10 AM :)

estrada9166 commented 5 years ago

@artola Sure thing!!

also, here is the link of the example using relay-easygraphql