Home Reference Source Repository

src/query/__tests__/RelayQueryField-test.js

/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @emails oncall+relay
 */

'use strict';

require('configureForRelayOSS');

const Relay = require('Relay');
const RelayConnectionInterface = require('RelayConnectionInterface');
const RelayQuery = require('RelayQuery');
const RelayTestUtils = require('RelayTestUtils');
const RelayVariable = require('RelayVariable');

const generateRQLFieldAlias = require('generateRQLFieldAlias');

describe('RelayQueryField', () => {
  const {getNode, getVerbatimNode} = RelayTestUtils;

  let aliasedIdField;
  let cursorField;
  let edgesField;
  let friendsScalarField;
  let friendsConnectionField;
  let friendsVariableField;
  let generatedIdField;
  let generatedIdFieldRQL;
  let nodeField;
  let nodeIdField;
  let pageInfoField;
  let userAddressField;

  beforeEach(() => {
    jest.resetModuleRegistry();

    jasmine.addMatchers(RelayTestUtils.matchers);

    const scalarRQL = Relay.QL`
      fragment on Node {
        id
      }
    `;
    nodeIdField = getNode(scalarRQL).getChildren()[0];
    expect(nodeIdField.getSchemaName()).toBe('id');

    const query = getNode(Relay.QL`
      query {
        node(id:"4") {
          friends(first:"1") {
            edges {
              node {
                special_id: id
              }
            }
          }
        }
      }
    `);
    friendsConnectionField = query.getChildren()[0];
    edgesField = friendsConnectionField.getChildren()[0];
    nodeField = edgesField.getChildren()[0];
    aliasedIdField =  nodeField.getChildren()[0];
    expect(aliasedIdField.getSchemaName()).toBe('id');

    const groupRQL = Relay.QL`
      fragment on User {
        address {
          city
        }
      }
    `;
    userAddressField = getNode(groupRQL).getChildren()[0];
    expect(userAddressField.getSchemaName()).toBe('address');

    const friendsScalarFieldRQL = Relay.QL`
      fragment on User {
        friends_scalar: friends
          (first:"10",after:"offset",orderby:"name") {
          edges {
            node {
              id
            }
          }
        }
      }
    `;
    friendsScalarField = getNode(friendsScalarFieldRQL).getChildren()[0];
    expect(friendsScalarField.getSchemaName()).toBe('friends');
    pageInfoField = getNode(friendsScalarFieldRQL)
      .getChildren()[0]
      .getChildren()[1];
    expect(pageInfoField.getSchemaName())
      .toBe(RelayConnectionInterface.PAGE_INFO);
    // feed.edges.cursor
    cursorField = getNode(friendsScalarFieldRQL)
      .getChildren()[0].getChildren()[0].getChildren()[1];
    expect(cursorField.getSchemaName()).toBe('cursor');

    const friendsVariableFieldRQL = Relay.QL`
      fragment on User {
        friends_variable: friends(first:$first,after:$after) {
          edges {
            node {
              id
            }
          }
        }
      }
    `;
    const variables = {
      after: 'offset',
      first: 10,
    };
    friendsVariableField =
      getNode(friendsVariableFieldRQL, variables).getChildren()[0];
    expect(friendsVariableField.getSchemaName()).toBe('friends');

    generatedIdFieldRQL = Relay.QL`
      fragment on User {
        name
      }
    `;
    generatedIdField = getNode(generatedIdFieldRQL).getChildren()[1];
    expect(generatedIdField.getSchemaName()).toBe('id');
  });

  it('returns the type', () => {
    const actor = getNode(Relay.QL`
      fragment on Viewer {
        actor {
          name
        }
      }
    `).getChildren()[0];
    expect(actor.getType()).toBe('Actor');
  });

  it('gets children by storage key', () => {
    const edges = friendsScalarField.getFieldByStorageKey('edges');
    expect(edges).toBe(friendsScalarField.getChildren()[0]);
  });

  it('gets children by field', () => {
    const edges = friendsScalarField.getFieldByStorageKey('edges');
    expect(edges).toBe(friendsScalarField.getChildren()[0]);
    const varFeedEdges = friendsVariableField.getField(edges);
    expect(varFeedEdges).toBe(friendsVariableField.getChildren()[0]);
  });

  it('equals the same fields', () => {
    expect(nodeIdField.equals(nodeIdField)).toBe(true);
    expect(userAddressField.equals(userAddressField)).toBe(true);
    expect(friendsScalarField.equals(friendsScalarField)).toBe(true);
    expect(friendsVariableField.equals(friendsVariableField)).toBe(true);
    expect(generatedIdField.equals(generatedIdField)).toBe(true);
    expect(pageInfoField.equals(pageInfoField)).toBe(true);
    expect(cursorField.equals(cursorField)).toBe(true);
  });

  it('equals equivalent fields', () => {
    const pictureScalarRQL = Relay.QL`
      fragment on User {
        profilePicture(size:"32")
      }
    `;
    const pictureScalar = getNode(pictureScalarRQL).getChildren()[0];
    const pictureVariableRQL = Relay.QL`
      fragment on User {
        profilePicture(size:$size)
      }
    `;
    const variables = {size: '32'};
    const pictureVariable =
      getNode(pictureVariableRQL,  variables).getChildren()[0];
    expect(pictureScalar.equals(pictureVariable)).toBe(true);

    const pictureTypedVariableA = getNode(
      pictureVariableRQL,
      {size: new RelayVariable('size')}
    ).getChildren()[0];
    const pictureTypedVariableB = getNode(
      pictureVariableRQL,
      {size: new RelayVariable('size')}
    ).getChildren()[0];
    expect(pictureTypedVariableA.equals(pictureTypedVariableB)).toBe(true);

    const diffId = getNode(generatedIdFieldRQL).getChildren()[1];
    expect(generatedIdField.equals(diffId)).toBe(true);
  });

  it('does not equal fields with different values', () => {
    const pictureScalarRQL = Relay.QL`
      fragment on User {
        profilePicture(size:"32")
      }
    `;
    const pictureScalar = getNode(pictureScalarRQL).getChildren()[0];
    const pictureVariableRQL = Relay.QL`
      fragment on User {
        profilePicture(size:$size)
      }
    `;
    const pictureVariable =
      getNode(pictureVariableRQL, {size: '33'}).getChildren()[0];
    expect(pictureScalar.equals(pictureVariable)).toBe(false);

    const pictureTypedVariableA = getNode(
      pictureVariableRQL,
      {size: new RelayVariable('size')}
    ).getChildren()[0];
    const pictureMimeticVariableB = getNode(
      pictureVariableRQL,
      {size: {name: 'size'}}
    ).getChildren()[0];
    expect(pictureTypedVariableA.equals(pictureMimeticVariableB)).toBe(false);
  });

  it('scalar fields have no children', () => {
    expect(nodeIdField.canHaveSubselections()).toBe(false);
    expect(nodeIdField.getChildren().length).toBe(0);
  });

  it('returns the same object when cloning a scalar field', () => {
    expect(nodeIdField.clone([])).toBe(nodeIdField);
  });

  it('clones with updated children', () => {
    const query = getNode(Relay.QL`
      fragment on Story {
        feedback {
          id
          canViewerComment
        }
      }
    `).getChildren()[0];
    const clone = query.clone([query.getChildren()[0]]);
    expect(clone.getChildren().length).toBe(1);
    expect(clone.getChildren()[0].getSchemaName()).toBe('id');
    expect(clone.getFieldByStorageKey('canViewerComment')).toBe(undefined);
  });

  it('throws if cloning a subselection-ineligible field with children', () => {
    const expectedError = (
      'RelayQueryNode: Cannot add children to field `id` because it does not ' +
      'support sub-selections (sub-fields).'
    );
    expect(() => {
      nodeIdField.clone([null]);
    }).toFailInvariant(expectedError);
    expect(() => {
      nodeIdField.cloneFieldWithCalls([null], []);
    }).toFailInvariant(expectedError);
  });

  it('returns children', () => {
    const children = userAddressField.getChildren();
    expect(children.length).toBe(1);
    expect(children[0].getSchemaName()).toBe('city');
  });

  it('return the same object when cloning with the same children', () => {
    const children = userAddressField.getChildren();
    const child = children[0];
    expect(userAddressField.clone(children)).toBe(userAddressField);
    expect(userAddressField.clone([child])).toBe(userAddressField);
    expect(userAddressField.clone([child, null])).toBe(userAddressField);
    expect(userAddressField.clone([null, child, null])).toBe(userAddressField);
  });

  it('returns a new object when cloning with different children', () => {
    expect(userAddressField.clone([nodeIdField])).not.toBe(userAddressField);
  });

  it('returns null when cloning without children', () => {
    expect(userAddressField.clone([])).toBe(null);
    expect(userAddressField.clone([null])).toBe(null);
    expect(userAddressField.cloneFieldWithCalls([], [])).toBe(null);
    expect(userAddressField.cloneFieldWithCalls([null], [])).toBe(null);
  });

  it('returns the schema/application names', () => {
    expect(friendsScalarField.getSchemaName()).toBe('friends');
    expect(friendsScalarField.getApplicationName()).toBe('friends_scalar');

    expect(friendsVariableField.getSchemaName()).toBe('friends');
    expect(friendsVariableField.getApplicationName()).toBe('friends_variable');
  });

  it('returns call types', () => {
    const field = getNode(
      Relay.QL`fragment on User{profilePicture(size:"32")}`
    ).getChildren()[0];
    field.getConcreteQueryNode().calls[0].metadata = {type: 'scalar'};

    expect(field.getCallType('size')).toBe('scalar');
    expect(field.getCallType('nonExistentCall')).toBe(undefined);
  });

  it('throws if a variable is missing', () => {
    const pictureFragment = Relay.QL`
      fragment on User {
        profilePicture(size:[$width,$height])
      }
    `;
    const variables = {};
    const pictureField = getNode(pictureFragment, variables).getChildren()[0];
    expect(() => pictureField.getCallsWithValues()).toFailInvariant(
      'callsFromGraphQL(): Expected a declared value for variable, `$width`.'
    );
  });

  it('permits null or undefined variable values', () => {
    const pictureFragment = Relay.QL`
      fragment on User {
        profilePicture(size:[$width,$height])
      }
    `;
    const variables = {
      width: null,
      height: undefined,
    };
    const pictureField = getNode(pictureFragment, variables).getChildren()[0];
    expect(pictureField.getCallsWithValues()).toEqual([
      {
        name: 'size',
        value: [
          null,
          undefined,
        ],
      },
    ]);
  });

  describe('canHaveSubselections()', () => {
    it('returns true for fields that support sub-selections', () => {
      expect(edgesField.canHaveSubselections()).toBe(true);
      expect(friendsConnectionField.canHaveSubselections()).toBe(true);
      expect(friendsScalarField.canHaveSubselections()).toBe(true);
      expect(friendsVariableField.canHaveSubselections()).toBe(true);
      expect(nodeField.canHaveSubselections()).toBe(true);
      expect(pageInfoField.canHaveSubselections()).toBe(true);
      expect(userAddressField.canHaveSubselections()).toBe(true);
    });

    it('returns false for fields that do not support sub-selections', () => {
      expect(aliasedIdField.canHaveSubselections()).toBe(false);
      expect(cursorField.canHaveSubselections()).toBe(false);
      expect(generatedIdField.canHaveSubselections()).toBe(false);
      expect(nodeIdField.canHaveSubselections()).toBe(false);
    });
  });

  describe('getRangeBehaviorCalls()', () => {
    it('strips range calls on connections', () => {
      const connectionField = getNode(
        Relay.QL`fragment on User { friends(first:"10",isViewerFriend:true) }`
      ).getChildren()[0];
      expect(connectionField.getRangeBehaviorCalls())
        .toEqual([{name: 'isViewerFriend', value: true}]);
    });

    it('throws for non-connection fields', () => {
      const nonConnectionField = getNode(
        Relay.QL`query { node(id:"4") }`
      ).getChildren()[0];
      expect(nonConnectionField.getRangeBehaviorCalls).toThrow();
    });

    it('strips passing `if` calls', () => {
      const ifTrue = getNode(
        Relay.QL`fragment on User { friends(if:true) }`
      ).getChildren()[0];
      expect(ifTrue.getRangeBehaviorCalls()).toEqual([]);

      const ifFalse = getNode(
        Relay.QL`fragment on User { friends(if:false) }`
      ).getChildren()[0];
      expect(ifFalse.getRangeBehaviorCalls()).toEqual([{
        name: 'if',
        value: false,
      }]);
    });

    it('strips failing `unless` calls', () => {
      const unlessTrue = getNode(
        Relay.QL`fragment on User { friends(unless:true) }`
      ).getChildren()[0];
      expect(unlessTrue.getRangeBehaviorCalls()).toEqual([{
        name: 'unless',
        value: true,
      }]);

      const unlessFalse = getNode(Relay.QL`
        fragment on User {
          friends(unless:false)
        }
      `).getChildren()[0];
      expect(unlessFalse.getRangeBehaviorCalls()).toEqual([]);
    });

    it('substitutes variable values', () => {
      const friendsScalarRQL = Relay.QL`
        fragment on User { friends(isViewerFriend:false) }
      `;
      const friendsScalar = getNode(friendsScalarRQL).getChildren()[0];
      expect(friendsScalar.getRangeBehaviorCalls()).toEqual([{
        name: 'isViewerFriend',
        value: false,
      }]);

      const friendsVariableRQL = Relay.QL`
        fragment on User { friends(isViewerFriend:$isViewerFriend) }
      `;
      const variables = {isViewerFriend: false};
      const friendsVariable =
        getNode(friendsVariableRQL, variables).getChildren()[0];
      expect(friendsVariable.getRangeBehaviorCalls()).toEqual([{
        name: 'isViewerFriend',
        value: false,
      }]);
    });
  });

  describe('getSerializationKey()', () => {
    it('serializes all calls', () => {
      expect(friendsScalarField.getSerializationKey()).toBe(
        generateRQLFieldAlias(
          'friends.friends_scalar.after(offset).first(10).orderby(name)'
        )
      );
    });

    it('substitutes variable values', () => {
      const key = generateRQLFieldAlias('profilePicture.size(32,64)');
      const pictureScalarRQL = Relay.QL`
        fragment on User {
          profilePicture(size:["32","64"])
        }
      `;
      const pictureScalar = getNode(pictureScalarRQL).getChildren()[0];
      expect(pictureScalar.getSerializationKey()).toBe(key);

      const pictureVariableRQL = Relay.QL`
        fragment on User {
          profilePicture(size:[$width,$height])
        }
      `;
      const variables = {
        height: 64,
        width: 32,
      };
      const pictureVariable =
        getNode(pictureVariableRQL, variables).getChildren()[0];
      expect(pictureVariable.getSerializationKey()).toBe(key);
    });

    it('includes the alias on fields with calls', () => {
      const fragment = getVerbatimNode(Relay.QL`
        fragment on User {
          const: profilePicture(size: "100") { uri }
          var: profilePicture(size: $size) { uri }
        }
      `, {
        size: 100,
      });
      const children = fragment.getChildren();
      expect(children[0].getSerializationKey()).toBe(
        generateRQLFieldAlias('profilePicture.const.size(100)')
      );
      expect(children[1].getSerializationKey()).toBe(
        generateRQLFieldAlias('profilePicture.var.size(100)')
      );
    });

    it('excludes the alias on fields without calls', () => {
      const fragment = getVerbatimNode(Relay.QL`
        fragment on User {
          alias: username
        }
      `);
      const children = fragment.getChildren();
      expect(children[0].getSerializationKey()).toBe('username');
    });
  });

  describe('getShallowHash()', () => {
    it('serializes all calls', () => {
      expect(friendsScalarField.getShallowHash()).toBe(
        'friends{after:"offset",first:"10",orderby:"name"}'
      );
    });

    it('serializes argument literal values', () => {
      const node = getNode(Relay.QL`
        fragment on User {
          profilePicture(size: ["32", "64"])
        }
      `);
      expect(node.getChildren()[0].getShallowHash()).toBe(
        'profilePicture{size:[0:"32",1:"64"]}'
      );
    });

    it('serializes argument variable values', () => {
      const node = getNode(Relay.QL`
        fragment on User {
          profilePicture(size: [$width, $height])
        }
      `, {
        width: 32,
        height: 64,
      });
      expect(node.getChildren()[0].getShallowHash()).toBe(
        'profilePicture{size:[0:32,1:64]}'
      );
    });
  });

  describe('getStorageKey()', () => {
    it('strips range calls on connections', () => {
      const connectionField = getNode(Relay.QL`
        fragment on User {
          friends(first:"10",isViewerFriend:true) {
            edges { node { id } }
          }
        }
      `).getChildren()[0];
      expect(connectionField.getStorageKey())
        .toBe('friends{isViewerFriend:true}');
    });

    it('preserves range-like calls on non-connections', () => {
      // NOTE: `segments.edges.node` is scalar.
      const nonConnectionField = getNode(Relay.QL`
        fragment on Node {
          segments(first:"3") {
            edges { node }
          }
        }
      `).getChildren()[0];
      expect(nonConnectionField.getStorageKey()).toBe('segments{first:"3"}');
    });

    it('strips passing `if` calls', () => {
      const ifTrue = getNode(Relay.QL`
        fragment on Node {
          firstName(if:true)
        }
      `).getChildren()[0];
      expect(ifTrue.getStorageKey()).toBe('firstName');

      const ifFalse = getNode(Relay.QL`
        fragment on Node {
          firstName(if:false)
        }
      `).getChildren()[0];
      expect(ifFalse.getStorageKey()).toBe('firstName{if:false}');
    });

    it('strips failing `unless` calls', () => {
      const unlessTrue = getNode(Relay.QL`
        fragment on Node{
          firstName(unless:true)
        }
      `).getChildren()[0];
      expect(unlessTrue.getStorageKey()).toBe('firstName{unless:true}');

      const unlessFalse = getNode(Relay.QL`
        fragment on Node{
          firstName(unless:false)
        }
      `).getChildren()[0];
      expect(unlessFalse.getStorageKey()).toBe('firstName');
    });

    it('substitutes variable values', () => {
      const key = 'profilePicture{size:[0:"32",1:"64"]}';
      const pictureScalarRQL = Relay.QL`
        fragment on User {
          profilePicture(size:["32","64"])
        }
      `;
      const pictureScalar = getNode(pictureScalarRQL).getChildren()[0];
      expect(pictureScalar.getStorageKey()).toBe(key);

      const pictureVariableRQL = Relay.QL`
        fragment on User {
          profilePicture(size:[$width,$height])
        }
      `;
      const variables = {
        height: '64',
        width: '32',
      };
      const pictureVariable =
        getNode(pictureVariableRQL, variables).getChildren()[0];
      expect(pictureVariable.getStorageKey()).toBe(key);
    });

    it('produces stable keys regardless of argument order', () => {
      const pictureFieldA = getNode(Relay.QL`fragment on User {
        profilePicture(size: "32", preset: SMALL)
      }`).getChildren()[0];
      const pictureFieldB = getNode(Relay.QL`fragment on User {
        profilePicture(preset: SMALL, size: "32")
      }`).getChildren()[0];
      const expectedKey = 'profilePicture{preset:"SMALL",size:"32"}';
      expect(pictureFieldA.getStorageKey()).toBe(expectedKey);
      expect(pictureFieldB.getStorageKey()).toBe(expectedKey);
    });
  });

  it('returns arguments with values', () => {
    // scalar values are converted to strings
    expect(friendsScalarField.getCallsWithValues()).toEqual([
      {name: 'first', value: '10'},
      {name: 'after', value: 'offset'},
      {name: 'orderby', value: 'name'},
    ]);
    // variables return their values
    expect(friendsVariableField.getCallsWithValues()).toEqual([
      {name: 'first', value: 10},
      {name: 'after', value: 'offset'},
    ]);

    const pictureScalarRQL = Relay.QL`
      fragment on User {
        profilePicture(size:["32","64"])
      }
    `;
    const pictureScalar = getNode(pictureScalarRQL).getChildren()[0];
    expect(pictureScalar.getCallsWithValues()).toEqual([
      {name: 'size', value: ['32', '64']},
    ]);

    const pictureVariableRQL = Relay.QL`
      fragment on User {
        profilePicture(size:[$width,$height])
      }
    `;
    const variables = {
      height: '64',
      width: 32,
    };
    const pictureVariable =
      getNode(pictureVariableRQL, variables).getChildren()[0];
    expect(pictureVariable.getCallsWithValues()).toEqual([
      {name: 'size', value: [32, '64']},
    ]);
  });

  it('returns arguments with array values', () => {
    const variables = {size: [32, 64]};
    const profilePicture = getNode(Relay.QL`
      fragment on User {
        profilePicture(size: $size)
      }
    `, variables).getChildren()[0];
    expect(profilePicture.getCallsWithValues()).toEqual(
      [{name: 'size', value: [32, 64]}]
    );
  });

  it('clones with different call values', () => {
    let clonedFeed = friendsVariableField.cloneFieldWithCalls(
      friendsVariableField.getChildren(),
      [{name: 'first', value: 25}]
    );
    expect(clonedFeed.getSchemaName()).toBe('friends');
    expect(clonedFeed.getCallsWithValues()).toEqual([
      {name: 'first', value: 25},
    ]);
    expect(clonedFeed.getSerializationKey()).toEqual(
      generateRQLFieldAlias('friends.friends_variable.first(25)')
    );
    expect(clonedFeed.getStorageKey()).toEqual('friends');

    clonedFeed = friendsVariableField.cloneFieldWithCalls(
      friendsVariableField.getChildren(),
      [
        {name: 'first', value: 10},
        {name: 'after', value: 'offset'},
      ]
    );
    expect(clonedFeed).toBe(friendsVariableField);
  });

  it('returns isAbstract', () => {
    expect(getNode(Relay.QL`
      fragment on Viewer {
        actor {
          name
        }
      }
    `).getFieldByStorageKey('actor').isAbstract()).toBe(true);
    expect(getNode(Relay.QL`
      fragment on User {
        address {
          city
        }
      }
    `).getFieldByStorageKey('address').isAbstract()).toBe(false);
  });

  it('returns isGenerated', () => {
    expect(aliasedIdField.isGenerated()).toBe(false);
    expect(cursorField.isGenerated()).toBe(true);
    expect(userAddressField.isGenerated()).toBe(false);
    expect(generatedIdField.isGenerated()).toBe(true);
    expect(nodeIdField.isGenerated()).toBe(false);
    expect(pageInfoField.isGenerated()).toBe(true);
  });

  it('returns isRefQueryDependency', () => {
    // Not ref query dependencies:
    expect(aliasedIdField.isRefQueryDependency()).toBe(false);
    expect(cursorField.isRefQueryDependency()).toBe(false);
    expect(userAddressField.isRefQueryDependency()).toBe(false);
    expect(generatedIdField.isRefQueryDependency()).toBe(false);
    expect(nodeIdField.isRefQueryDependency()).toBe(false);
    expect(pageInfoField.isRefQueryDependency()).toBe(false);

    // Pretend some of them are ref query dependencies:
    expect(aliasedIdField.cloneAsRefQueryDependency().isRefQueryDependency())
      .toBe(true);
    expect(cursorField.cloneAsRefQueryDependency().isRefQueryDependency())
      .toBe(true);
    expect(generatedIdField.cloneAsRefQueryDependency().isRefQueryDependency())
      .toBe(true);
    expect(nodeIdField.cloneAsRefQueryDependency().isRefQueryDependency())
      .toBe(true);
    expect(pageInfoField.cloneAsRefQueryDependency().isRefQueryDependency())
      .toBe(true);
  });

  it('returns isRequisite', () => {
    expect(aliasedIdField.isRequisite()).toBe(true);
    expect(cursorField.isRequisite()).toBe(true);
    expect(userAddressField.isRequisite()).toBe(false);
    expect(generatedIdField.isRequisite()).toBe(true);
    expect(nodeIdField.isRequisite()).toBe(true);
    expect(pageInfoField.isRequisite()).toBe(true);
  });

  it('returns isFindable', () => {
    expect(nodeIdField.isFindable()).toBe(false);
    expect(friendsScalarField.isFindable()).toBe(true);
    expect(userAddressField.isFindable()).toBe(false);
  });

  it('returns the inferred primary key', () => {
    const field = getNode(Relay.QL`fragment on Story{feedback}`).getChildren()[0];
    expect(field.getInferredPrimaryKey()).toBe('id');

    expect(friendsScalarField.getInferredPrimaryKey()).toBe(undefined);
  });

  it('returns the inferred root call name', () => {
    const field = getNode(Relay.QL`fragment on Story{feedback}`).getChildren()[0];
    expect(field.getInferredRootCallName()).toBe('node');

    expect(friendsScalarField.getInferredRootCallName()).toBe(undefined);
  });

  it('creates nodes', () => {
    const fragmentRQL = Relay.QL`
      fragment on FeedUnit {
        actorCount
      }
    `;
    const node = nodeIdField.createNode(fragmentRQL);
    expect(node instanceof RelayQuery.Fragment).toBe(true);
    expect(node.getType()).toBe('FeedUnit');
    expect(node.getRoute()).toBe(nodeIdField.getRoute());
    expect(node.getVariables()).toBe(nodeIdField.getVariables());
    expect(node.getFieldByStorageKey('actorCount').getType()).toBe('Int');
  });

  it('returns directives', () => {
    const field = getNode(Relay.QL`
      fragment on Story {
        feedback @include(if: $cond)
      }
    `, {cond: true}).getChildren()[0];
    expect(field.getDirectives()).toEqual([
      {
        args: [
          {name: 'if', value: true},
        ],
        name: 'include',
      },
    ]);
  });
});