npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

enzyme-context-apollo

v2.1.0

Published

Initialize react-apollo context with enzyme-context

Downloads

8

Readme

enzyme-context-apollo

Introduction

This plugin sets up an apollo client with a mock graphql backend to test components that fetch data via graphql.

Installation

  1. Setup required peer dependencies: apollo-cache-inmemory, apollo-client, enzyme, react, react-apollo, and react-test-renderer.

  2. Install via yarn or npm

    $> yarn add -D enzyme-context enzyme-context-apollo
  3. Add to plugins:

    import fs from 'fs';
    import path from 'path';
    import { createMount, createShallow } from 'enzyme-context';
    import { apolloContext } from 'enzyme-context-apollo';
    
    // path to a .graphql/.gql schema definition for your backend
    const typeDefs = fs.readFileSync(path.resolve(__dirname), './schema.graphql');
    
    const plugins = {
      client: apolloContext({
        schema: {
          typeDefs,
        },
        defaultMocks: {
          Query: () => ({
            viewer: () => ({
              id: '1612',
              name: 'Joe Dart',
              instrument: 'Custom Music Man Bass',
            }),
          }),
        },
      }),
    };
    
    export const mount = createMount(plugins);
    export const shallow = createShallow(plugins);

Usage

After adding the plugin to your mount/shallow, it can be used in your tests like so:

import { mount } from './test-utils/enzyme'; // import the mount created with enzyme-context
import MyComponent from './MyComponent';

describe('<MyComponent />', () => {
  let wrapper;

  beforeEach(async () => {
    wrapper = mount(<MyComponent />);
    // Reseting the apollo store does two things:
    //  1. Abort any in-flight requests that may have been leftover from the last spec
    //  2. Return a Promise that allows us to deterministically wait until all of our
    //     component's queries have loaded before proceeding with our tests.
    await wrapper.client.resetStore();
    // Update the Enzyme wrapper after the queries have loaded
    wrapper.update();
  });

  it('renders data from an apollo query', () => {
    expect(wrapper.text()).toContain('Viewer: Joe Dart');
  });
});

Configuration API

apolloContext(options) => EnzymePlugin

Arguments

  1. options (Object):

    • options.schema (IExecutableSchemaDefinition): the same object passed to graphql-tools's makeExecutableSchema.

    • options.fragmentMatcher (FragmentMatcherInterface [optional]): Useful if your app is using fragments on unions and interfaces. Example:

      import { createMount } from 'enzyme-context';
      import { apolloContext } from 'enzyme-context-apollo';
      import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
      // Path to JSON returned from a gql introspection query
      // (https://gist.github.com/craigbeck/b90915d49fda19d5b2b17ead14dcd6da)
      import introspectionJSON from './schemaIntrospectionJSON.json';
      
      const fragmentMatcher = new IntrospectionFragmentMatcher({
        introspectionQueryResultData: introspectionJSON,
      });
      
      export const mount = createMount({
        client: apolloContext({
          fragmentMatcher,
        }),
      });
    • options.defaultMocks (IMocks [optional]): default resolvers for the mock graphql backend. These objects are the same as the ones outlined in the Customizing mocks section of graphql-tools's docs.

Returns

EnzymePlugin: The plugin which can be passed to createMount/createShallow.

Example:

import fs from 'fs';
import path from 'path';
import { createMount, createShallow } from 'enzyme-context';
import { apolloContext } from 'enzyme-context-apollo';

// path to a .graphql/.gql schema definition for your backend
const typeDefs = fs.readFileSync(path.resolve(__dirname), './schema.graphql');

const plugins = {
  client: apolloContext({
    schema: {
      typeDefs,
    },
    defaultMocks: {
      Query: () => ({
        viewer: () => ({
          id: '1612',
          name: 'Joe Dart',
          instrument: 'Custom Music Man Bass',
        }),
      }),
    },
  }),
};

export const mount = createMount(plugins);
export const shallow = createShallow(plugins);

Mount/Shallow API

This plugin also allows some configuration to be passed at mount-time:

  1. apolloMocks (IMocks [optional]): resolvers for the mock graphql backend. These objects are the same as the ones outlined in the Customizing mocks section of graphql-tools's docs. The mocks defined here will be deeply merged with the ones defined at plugin configuration time.
    • Example:
      const wrapper = mount(<MyComponent />, {
        apolloMocks: {
          Query: () => ({
            viewer: () => ({
              id: '44',
              name: 'Woody Goss',
              instrument: 'Keys',
            }),
          }),
        },
      });

FAQ / Tips

Help! My mocks don't seem to be working.

We've found there are a few typical reasons that mocks fail. Let's assume we're working with a very simple GraphQL schema like this:

schema {
  query: Query
  mutation: Mutation
}

type Query {
  id: String!
}

type Mutation {
  logoutUser(user: String!): Boolean!
}

What are the main causes of mocks not working?

  1. Mock names don't align with schema names

    You need to make sure that the type name and field name you're trying to mock exactly match your schema. Here's an example of how this problem might arise:

    const wrapper = mount(<MyComponent />, {
     apolloMocks: {
       // Type name in the GraphQL schema is `Mutation`, so this won't work
       Mutations: () => ({
         logoutUser: someMockImplementationFunction
       }),
     },
    });
  2. Wrong data types

    If you call a mutation or pass arguments to a query, you need to make sure that the data passed in matches the schema data types, otherwise your mock function won't get called. For example, if you pass in a null argument where your schema is expecting a defined value, the call will fail and your mock won't get run.

  3. Not waiting for results

    The apollo client / mock GraphQL backend operate asynchronously, so when a new query or mutation is made, you can't expect it to be sychronously completed and must instead wait for it to finish.

    Ideally, your component has a prop that returns a promise that is completed when your mutation completes. In that case, you can simply await that promise:

    await component.prop('onLogoutClick')();
    // now assert something here

    However, sometimes you might not be able to get a handle to a promise. In that case you can typically just wait a tick and the async operations will be resolved:

    const sleep = (ms=0) => new Promise(resolve => setTimeout(resolve, ms));
    
    component.find('button').simulate('click');
    await sleep();
    // now assert something