src/query/__tests__/RelayQL-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';
const QueryBuilder = require('QueryBuilder');
const Relay = require('Relay');
const RelayTestUtils = require('RelayTestUtils');
describe('RelayQL', () => {
beforeEach(() => {
jest.resetModuleRegistry();
jasmine.addMatchers(RelayTestUtils.matchers);
});
it('throws if not transformed', () => {
const badQL = Relay.QL;
expect(() => {
// Transform cannot find this call site.
badQL`
query {
viewer {
actor {
id
}
}
}
`;
}).toFailInvariant(
'RelayQL: Unexpected invocation at runtime. Either the Babel transform ' +
'was not set up, or it failed to identify this call site. Make sure it ' +
'is being used verbatim as `Relay.QL`.'
);
});
it('does not throw if transformed', () => {
expect(() => {
Relay.QL`
query {
viewer {
actor {
id
}
}
}
`;
}).not.toThrowError();
});
it('permits valid variable substitutions', () => {
const SIZE = 42;
expect(() => {
Relay.QL`
query {
viewer {
actor {
profilePicture(size: ${SIZE}) {
uri
}
}
}
}
`;
}).not.toThrowError();
});
it('wraps variable substituted values in concrete call values', () => {
const SIZE = 42;
expect(Relay.QL.__var(SIZE)).toEqual({
kind: 'CallValue',
callValue: SIZE,
});
});
it('throws for illegal variable substitutions', () => {
const variables = {
size: QueryBuilder.createCallVariable('size'),
};
expect(() => {
Relay.QL`
query {
viewer {
actor {
profilePicture(size: ${variables.size}) {
uri
}
}
}
}
`;
}).toFailInvariant(
'RelayQL: Invalid argument `size` supplied via template substitution. ' +
'Instead, use an inline variable (e.g. `comments(count: $count)`).'
);
});
it('permits fragment substitutions', () => {
const fragment = QueryBuilder.createFragment({
name: 'Foo',
type: 'Bar',
});
expect(() => {
Relay.QL`
query {
viewer {
${fragment}
}
}
`;
}).not.toThrow();
});
it('permits fragment reference substitutions', () => {
const fragmentReference = QueryBuilder.createFragmentReference(
QueryBuilder.createFragment({
name: 'Foo',
type: 'Bar',
})
);
expect(() => {
Relay.QL`
query {
viewer {
${fragmentReference}
}
}
`;
}).not.toThrow();
});
it('permits an array of fragment substitutions', () => {
const fragment = QueryBuilder.createFragment({
name: 'Foo',
type: 'Bar',
});
expect(() => {
Relay.QL`
query {
viewer {
${[fragment]}
}
}
`;
}).not.toThrow();
});
it('throws for invalid fragment substitutions', () => {
expect(() => {
Relay.QL`
query {
viewer {
${'foo'}
}
}
`;
}).toFailInvariant(
'RelayQL: Invalid fragment composition, use ' +
'`${Child.getFragment(\'name\')}`.'
);
expect(() => {
Relay.QL`
query {
viewer {
${['foo']}
}
}
`;
}).toFailInvariant(
'RelayQL: Invalid fragment composition, use ' +
'`${Child.getFragment(\'name\')}`.'
);
const fragment = QueryBuilder.createFragment({
name: 'Foo',
type: 'Bar',
});
expect(() => {
Relay.QL`
query {
viewer {
${[[fragment]]}
}
}
`;
}).toFailInvariant(
'RelayQL: Invalid fragment composition, use ' +
'`${Child.getFragment(\'name\')}`.'
);
});
it('generates unique concrete fragment IDs', () => {
const getFragment = () => Relay.QL`
fragment on Node {
id
}
`;
const nodeA = getFragment();
const nodeB = getFragment();
expect(nodeA).not.toBe(nodeB);
expect(nodeA.id).not.toBe(nodeB.id);
});
it('generates identical concrete IDs for static fragments', () => {
const getFragment = () => Relay.QL`
fragment on Node @relay(isStaticFragment: true) {
id
}
`;
const nodeA = getFragment();
const nodeB = getFragment();
expect(nodeA).not.toBe(nodeB);
expect(nodeA.id).toBe(nodeB.id);
});
});