canvas-lms/ui/shared/graphql-query-mock
Aaron Shafovaloff 86d0e7ced2 Update JavaScript in ui/ to latest prettier config
Test plan:
  - All existing tests pass

flag=none

[skip-eslint]

Change-Id: I36c1a2f47004185f8f02f4155b838c877ee495e3
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/302256
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Charley Kline <ckline@instructure.com>
Reviewed-by: Ed Schiebel <eschiebel@instructure.com>
Product-Review: Charley Kline <ckline@instructure.com>
QA-Review: Aaron Shafovaloff <ashafovaloff@instructure.com>
2022-09-29 22:04:26 +00:00
..
__tests__ Update JavaScript in ui/ to latest prettier config 2022-09-29 22:04:26 +00:00
README.md fetch observers in new message-students dialog 2022-03-16 16:27:45 +00:00
index.js Update JavaScript in ui/ to latest prettier config 2022-09-29 22:04:26 +00:00
package.json

README.md

Basic Usage

This will allow you to mock data for a graphql query (or mutation) that semantically matches the data types for canvas's graphql API. For example, if you queried for some data that should return a not null integer, this will return data for that field that contains a random integer, or if a field is an enum, it will randomly pick one of the enum values. This is a wrapper around the apollo mocking library (https://www.apollographql.com/docs/graphql-tools/mocking/)

async function example() {
  const query = gql`
    query ExampleQuery {
      course(id: "1") {
        name
        state
      }
    }
  `
  const result = await mockGraphqlQuery(query)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "course": {
      "name": "Hello World",
      "state": "created"
    }
  }
}

Overriding return data

You can modify the returned data of your query by using the overrides argument in mockGraphqlQuery.

async function example() {
  const query = gql`
    query ExampleQuery {
      course(id: "1") {
        course_name: name
        state
      }
    }
  `
  const overrides = [{
    Course: {
      name: 'Course 1',
      state: 'available'
    }
  }]
  const result = await mockGraphqlQuery(query, overrides)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "course": {
      "course_name": "Course 1",
      "state": "available"
    }
  }
}

It is important to note that overrides work on the graphql type. Notice that the above overrides was for Course, instead of course like in the query. This is because Course is the actual graphql typename for that object. Similarly, when overriding an aliased field like course_name, we still apply the override to to the graphql name for that field, in this case name.

You can find the graphql typenames by looking at the Documentation Explorer tab on the /graphiql page, or by looking at the schema.graphql file.

Because we are overriding the graphql types, any overrides we provide are going to be used anywhere that type shows up in the given query. This is very helpful for being able to mock data deep in a query without having to go through each individual node to get there. For example:

async function example() {
  const query = gql`
    query MyQuery {
      assignment(id: "1") {
        discussion {
          modules {
            name
          }
        }
        modules {
          name
        }
      }
    }
  `
  const overrides = [{
    Module: {name: 'Test Module'}
  }]
  const result = await mockGraphqlQuery(query, overrides)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "assignment": {
      "discussion": {
        "modules": [
          {"name": "Test Module"},
          {"name": "Test Module"}
        ]
      },
      "modules": [
        {"name": "Test Module"},
        {"name": "Test Module"}
      ]
    }
  }
}

If you only want to mock data for a single type instead of everywhere it shows up in the graphql query, you can do so by mocking the parent that contains the data. For example:

async function example() {
  const query = gql`
    query MyQuery {
      assignment(id: "1") {
        discussion {
          modules {
            name
          }
        }
        modules {
          name
        }
      }
    }
  `
  const overrides = [{
    Module: {name: 'Test Module 1'}
    Discussion: {
      modules: [{name: "Test Module 2"}]
    }
  }]
  const result = await mockGraphqlQuery(query, overrides)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "assignment": {
      "discussion": {
        "modules": [
          {"name": "Test Module 2"}
        ]
      },
      "modules": [
        {"name": "Test Module 1"},
        {"name": "Test Module 1"}
      ]
    }
  }
}

Overriding lists of data

By default, any list type returns a list with two mocked elements in it. We can customize this by passing in our own list of overrides for a specific field, where we can control the number of elements in the list and the specific overrides for each individual element:

async function example() {
  const query = gql`
    query ExampleQuery {
      course(id: "1") {
        assignmentsConnection {
          nodes {
            _id
            name
          }
        }
      }
    }
  `

  // We are saying that these overrides should have a list of three items, one
  // with no specific overrides, one with the _id overridden, and one with the
  // _id and name overwritten.
  const overrides = {
    Assignment: {
      nodes: [
        {},
        {_id: '1'},
        {_id: '2', name: 'Test Assignment'}
      ]
    }
  }

  const result = await mockGraphqlQuery(query)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "course": {
      "assignmentsConnection": {
        "nodes": [
          {"_id": "1532", "name": "Hello World"},
          {"_id": "1", "name": "Hello World"},
          {"_id": "2", "name": "Test Assignment"}
        ]
      }
    }
  }
}

Mocking results of interfaces

When querying an interface that could return different data types (for example the node and legacyNode interfaces), you will need to be explicity about what type of data you want back from the mocked query. This is done by adding a __typename override to the interface:

async function example() {
  const query = gql`
    query MyQuery {
      node(id: "abc123") {
        ... on User {
          name
        }
      }
    }
  `
  const overrides = [{
    Node: {__typename: 'User'}
  }]
  const result = await mockGraphqlQuery(query, overrides)
  console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "node": {
      "name": "Hello World"
    }
  }
}

Passing in variables

You can also pass in variables to your query via the third argument to mockGraphqlQuery. For example:

const query = gql`
  query TestQuery($assignmentID: ID!) {
    assignment(id: $assignmentID) {
	  name
    }
  }
`
const variables = {assignmentID: '1'}
const result = await mockGraphqlQuery(query, [], variables)
console.log(JSON.stringify(result, null, 2))
{
  "data": {
    "assignment": {
      "name": "Hello World"
    }
  }
}

Mocking apollo queries ()

Here is an example of using mockGraphqlQuery in conjunction with Apollo's MockedProvider. This can be tweaked to the needs of your specific test:

import {createCache} from '@canvas/apollo'
import mockGraphqlQuery from 'graphql-query-mock'

const ASSIGNMENT_QUERY = gql`
  query AssignmentQuery($assignmentID: ID!) {
    assignment(id: $assignmentID) {
      name
    }
  }
`

async function makeMocks() {
  const variables = {assignmentID: '1'}
  const overrides = {Assignment: {name: 'foobarbaz'}}
  const result = await mockGraphqlQuery(ASSIGNMENT_QUERY, overrides, variables)

  return [
    {
      request: {
        query: ASSIGNMENT_QUERY,
        variables
      },
      result
    }
  ]
}

it('does a thing', async () => {
  const mocks = await makeMocks()
  const {getByText} = render(
    <MockedProvider mocks={mocks} cache={createCache()}>
      <AssignmentQueryComponent />
    </MockedProvider>
  )

  // Rest of unit test
})

Using functions as mocks resolvers

Instead of setting values for overrides, you can pass in a function instead:

const overrides = {
  User: () => ({
    _id: () => Math.floor(Math.random() * 10)
  })
}
const result = await mockGraphqlQuery(query, overrides)