Skip to content

Improve coverage of the entire codebase #2332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/__fixtures__/schema-kitchen-sink.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ extend input InputType @onInputObject
This is a description of the `@skip` directive
"""
directive @skip(
"""This is a description of the `if` argument"""
if: Boolean! @onArgumentDefinition
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

Expand Down
18 changes: 18 additions & 0 deletions src/__tests__/graphql-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @flow strict

import { expect } from 'chai';
import { describe, it } from 'mocha';

import { GraphQLSchema } from '../type/schema';

import { graphqlSync } from '../graphql';

describe('graphql', () => {
it('report errors raised during schema validation', () => {
const schema = new GraphQLSchema({});
const result = graphqlSync({ schema, source: '{ __typename }' });
expect(result).to.deep.equal({
errors: [{ message: 'Query root type must be provided.' }],
});
});
});
15 changes: 12 additions & 3 deletions src/__tests__/starWarsQuery-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import { graphql } from '../graphql';
import { graphql, graphqlSync } from '../graphql';

import { StarWarsSchema as schema } from './starWarsSchema';

Expand Down Expand Up @@ -45,6 +45,9 @@ describe('Star Wars Query Tests', () => {
},
},
});

const syncResult = graphqlSync(schema, source);
expect(syncResult).to.deep.equal(result);
});

it('Allows us to query for the ID and friends of R2-D2', async () => {
Expand Down Expand Up @@ -165,12 +168,15 @@ describe('Star Wars Query Tests', () => {
});

describe('Using IDs and query parameters to refetch objects', () => {
it('Allows us to query for Luke Skywalker directly, using his ID', async () => {
it('Allows us to query characters directly, using their IDs', async () => {
const source = `
query FetchLukeQuery {
query FetchLukeAndC3POQuery {
human(id: "1000") {
name
}
droid(id: "2000") {
name
}
}
`;

Expand All @@ -180,6 +186,9 @@ describe('Star Wars Query Tests', () => {
human: {
name: 'Luke Skywalker',
},
droid: {
name: 'C-3PO',
},
},
});
});
Expand Down
40 changes: 40 additions & 0 deletions src/execution/__tests__/abstract-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,46 @@ describe('Execute: Handles execution of abstract types', () => {
});
});

it('missing both resolveType and isTypeOf yields useful error', () => {
const fooInterface = new GraphQLInterfaceType({
name: 'FooInterface',
fields: { bar: { type: GraphQLString } },
});

const fooObject = new GraphQLObjectType({
name: 'FooObject',
fields: { bar: { type: GraphQLString } },
interfaces: [fooInterface],
});

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
foo: {
type: fooInterface,
resolve: () => 'dummy',
},
},
}),
types: [fooObject],
});

const result = graphqlSync({ schema, source: '{ foo { bar } }' });

expect(result).to.deep.equal({
data: { foo: null },
errors: [
{
message:
'Abstract type "FooInterface" must resolve to an Object type at runtime for field "Query.foo" with value "dummy", received "undefined". Either the "FooInterface" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.',
locations: [{ line: 1, column: 3 }],
path: ['foo'],
},
],
});
});

it('resolveType allows resolving with type name', () => {
const PetType = new GraphQLInterfaceType({
name: 'Pet',
Expand Down
60 changes: 58 additions & 2 deletions src/execution/__tests__/executor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ describe('Execute: Handles basic execution tasks', () => {
);
});

it('throws on invalid variables', () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Type',
fields: {
fieldA: {
type: GraphQLString,
args: { argA: { type: GraphQLInt } },
},
},
}),
});
const document = parse(`
query ($a: Int) {
fieldA(argA: $a)
}
`);
const variableValues = '{ "a": 1 }';

// $DisableFlowOnNegativeTest
expect(() => execute({ schema, document, variableValues })).to.throw(
'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.',
);
});

it('accepts positional arguments', () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
Expand Down Expand Up @@ -77,7 +102,7 @@ describe('Execute: Handles basic execution tasks', () => {
e: () => 'Egg',
f: 'Fish',
// Called only by DataType::pic static resolver
pic: size => 'Pic of size: ' + (size || 50),
pic: size => 'Pic of size: ' + size,
deep: () => deepData,
promise: promiseData,
};
Expand Down Expand Up @@ -908,6 +933,30 @@ describe('Execute: Handles basic execution tasks', () => {
});
});

it('ignores missing sub selections on fields', () => {
const someType = new GraphQLObjectType({
name: 'SomeType',
fields: {
b: { type: GraphQLString },
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
a: { type: someType },
},
}),
});
const document = parse('{ a }');
const rootValue = { a: { b: 'c' } };

const result = execute({ schema, document, rootValue });
expect(result).to.deep.equal({
data: { a: {} },
});
});

it('does not include illegal fields in output', () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
Expand Down Expand Up @@ -973,7 +1022,10 @@ describe('Execute: Handles basic execution tasks', () => {

const SpecialType = new GraphQLObjectType({
name: 'SpecialType',
isTypeOf: obj => obj instanceof Special,
isTypeOf(obj, context) {
const result = obj instanceof Special;
return context && context.async ? Promise.resolve(result) : result;
},
fields: { value: { type: GraphQLString } },
});

Expand Down Expand Up @@ -1005,6 +1057,10 @@ describe('Execute: Handles basic execution tasks', () => {
},
],
});

const contextValue = { async: true };
const asyncResult = execute({ schema, document, rootValue, contextValue });
expect(asyncResult).to.deep.equal(asyncResult);
});

it('executes ignoring invalid non-executable definitions', () => {
Expand Down
9 changes: 9 additions & 0 deletions src/execution/__tests__/mutations-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ describe('Execute: Handles mutation execution ordering', () => {
});
});

it('does not include illegal mutation fields in output', () => {
const document = parse('mutation { thisIsIllegalDoNotIncludeMe }');

const result = execute({ schema, document });
expect(result).to.deep.equal({
data: {},
});
});

it('evaluates mutations correctly in the presence of a failed mutation', async () => {
const document = parse(`
mutation M {
Expand Down
7 changes: 4 additions & 3 deletions src/execution/__tests__/nonnull-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import invariant from '../../jsutils/invariant';

import { parse } from '../../language/parser';

import { GraphQLSchema } from '../../type/schema';
Expand Down Expand Up @@ -566,9 +568,8 @@ describe('Execute: handles non-nullable types', () => {
},
},
resolve: (_, args) => {
if (typeof args.cannotBeNull === 'string') {
return 'Passed: ' + args.cannotBeNull;
}
invariant(typeof args.cannotBeNull === 'string');
return 'Passed: ' + args.cannotBeNull;
},
},
},
Expand Down
41 changes: 41 additions & 0 deletions src/execution/__tests__/union-interface-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,47 @@ describe('Execute: Union and intersection types', () => {
});
});

it('executes interface types with named fragments', () => {
const document = parse(`
{
__typename
name
friends {
__typename
name
...DogBarks
...CatMeows
}
}

fragment DogBarks on Dog {
barks
}

fragment CatMeows on Cat {
meows
}
`);

expect(execute({ schema, document, rootValue: john })).to.deep.equal({
data: {
__typename: 'Person',
name: 'John',
friends: [
{
__typename: 'Person',
name: 'Liz',
},
{
__typename: 'Dog',
name: 'Odie',
barks: true,
},
],
},
});
});

it('allows fragment conditions to be abstract types', () => {
const document = parse(`
{
Expand Down
18 changes: 4 additions & 14 deletions src/execution/__tests__/variables-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,13 @@ import { getVariableValues } from '../values';

const TestComplexScalar = new GraphQLScalarType({
name: 'ComplexScalar',
serialize(value) {
if (value === 'DeserializedValue') {
return 'SerializedValue';
}
return null;
},
parseValue(value) {
if (value === 'SerializedValue') {
return 'DeserializedValue';
}
return null;
invariant(value === 'SerializedValue');
return 'DeserializedValue';
},
parseLiteral(ast) {
if (ast.value === 'SerializedValue') {
return 'DeserializedValue';
}
return null;
invariant(ast.value === 'SerializedValue');
return 'DeserializedValue';
},
});

Expand Down
16 changes: 16 additions & 0 deletions src/jsutils/__tests__/invariant-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @flow strict

import { expect } from 'chai';
import { describe, it } from 'mocha';

import invariant from '../invariant';

describe('invariant', () => {
it('throws on false conditions', () => {
expect(() => invariant(false, 'Oops!')).to.throw('Oops!');
});

it('use default error message', () => {
expect(() => invariant(false)).to.throw('Unexpected invariant triggered.');
});
});
Loading