Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Find breaking directive changes (#1152)
* find removed directives

* added missing semi

* set > array for lookups

* find removed directive arguments

* find added non-null directive arguments

* find removed locations for a directive

* detect removed locations from directives in a schema

* mv findDangerous::new functions to findBreaking

* s/Argument/Arg

* minor refactor, introducing Schema::getDirectiveMap()

* make detector changes live

* lint: Arrow function should not return assignment

* move getDirectiveMap to findBreakingChanges.js

* moved getArgumentMap to findBreakingChanges.js

* yayee for keyMap!

* noop

* noop
  • Loading branch information
kenleezle authored and leebyron committed Dec 15, 2017
1 parent c4e301d commit f236898
Show file tree
Hide file tree
Showing 2 changed files with 397 additions and 0 deletions.
224 changes: 224 additions & 0 deletions src/utilities/__tests__/findBreakingChanges-test.js
Expand Up @@ -36,8 +36,22 @@ import {
findArgChanges,
findInterfacesRemovedFromObjectTypes,
findInterfacesAddedToObjectTypes,
findRemovedDirectives,
findRemovedDirectiveArgs,
findAddedNonNullDirectiveArgs,
findRemovedLocationsForDirective,
findRemovedDirectiveLocations,
} from '../findBreakingChanges';

import {
GraphQLSkipDirective,
GraphQLIncludeDirective,
GraphQLDeprecatedDirective,
GraphQLDirective,
} from '../../type/directives';

import { DirectiveLocation } from '../../language/directiveLocation';

describe('findBreakingChanges', () => {
const queryType = new GraphQLObjectType({
name: 'Query',
Expand Down Expand Up @@ -1164,6 +1178,44 @@ describe('findBreakingChanges', () => {
},
});

const directiveThatIsRemoved = GraphQLSkipDirective;
const directiveThatRemovesArgOld = new GraphQLDirective({
name: 'DirectiveThatRemovesArg',
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
arg1: {
name: 'arg1',
},
},
});
const directiveThatRemovesArgNew = new GraphQLDirective({
name: 'DirectiveThatRemovesArg',
locations: [DirectiveLocation.FIELD_DEFINITION],
});
const nonNullDirectiveAddedOld = new GraphQLDirective({
name: 'NonNullDirectiveAdded',
locations: [DirectiveLocation.FIELD_DEFINITION],
});
const nonNullDirectiveAddedNew = new GraphQLDirective({
name: 'NonNullDirectiveAdded',
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
arg1: {
name: 'arg1',
type: GraphQLNonNull(GraphQLBoolean),
},
},
});
const directiveRemovedLocationOld = new GraphQLDirective({
name: 'Directive Name',
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.QUERY],
});

const directiveRemovedLocationNew = new GraphQLDirective({
name: 'Directive Name',
locations: [DirectiveLocation.FIELD_DEFINITION],
});

const oldSchema = new GraphQLSchema({
query: queryType,
types: [
Expand All @@ -1175,6 +1227,12 @@ describe('findBreakingChanges', () => {
argThatChanges,
typeThatLosesInterfaceOld,
],
directives: [
directiveThatIsRemoved,
directiveThatRemovesArgOld,
nonNullDirectiveAddedOld,
directiveRemovedLocationOld,
],
});

const newSchema = new GraphQLSchema({
Expand All @@ -1188,6 +1246,11 @@ describe('findBreakingChanges', () => {
typeThaLosesInterfaceNew,
interface1,
],
directives: [
directiveThatRemovesArgNew,
nonNullDirectiveAddedNew,
directiveRemovedLocationNew,
],
});

const expectedBreakingChanges = [
Expand Down Expand Up @@ -1242,11 +1305,172 @@ describe('findBreakingChanges', () => {
'TypeThatGainsInterface1 no longer implements ' +
'interface Interface1.',
},
{
type: BreakingChangeType.DIRECTIVE_REMOVED,
description: 'skip was removed',
},
{
type: BreakingChangeType.DIRECTIVE_ARG_REMOVED,
description: 'arg1 was removed from DirectiveThatRemovesArg',
},
{
type: BreakingChangeType.NON_NULL_DIRECTIVE_ARG_ADDED,
description:
'A non-null arg arg1 on directive ' +
'NonNullDirectiveAdded was added',
},
{
type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
description: 'QUERY was removed from Directive Name',
},
];
expect(findBreakingChanges(oldSchema, newSchema)).to.eql(
expectedBreakingChanges,
);
});

it('should detect if a directive was explicitly removed', () => {
const oldSchema = new GraphQLSchema({
directives: [GraphQLSkipDirective, GraphQLIncludeDirective],
});

const newSchema = new GraphQLSchema({
directives: [GraphQLSkipDirective],
});

expect(findRemovedDirectives(oldSchema, newSchema)).to.eql([
{
type: BreakingChangeType.DIRECTIVE_REMOVED,
description: `${GraphQLIncludeDirective.name} was removed`,
},
]);
});

it('should detect if a directive was implicitly removed', () => {
const oldSchema = new GraphQLSchema({});

const newSchema = new GraphQLSchema({
directives: [GraphQLSkipDirective, GraphQLIncludeDirective],
});

expect(findRemovedDirectives(oldSchema, newSchema)).to.eql([
{
type: BreakingChangeType.DIRECTIVE_REMOVED,
description: `${GraphQLDeprecatedDirective.name} was removed`,
},
]);
});

it('should detect if a directive argument was removed', () => {
const oldSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'DirectiveWithArg',
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
arg1: {
name: 'arg1',
},
},
}),
],
});

const newSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'DirectiveWithArg',
locations: [DirectiveLocation.FIELD_DEFINITION],
}),
],
});

expect(findRemovedDirectiveArgs(oldSchema, newSchema)).to.eql([
{
type: BreakingChangeType.DIRECTIVE_ARG_REMOVED,
description: 'arg1 was removed from DirectiveWithArg',
},
]);
});

it('should detect if a non-nullable directive argument was added', () => {
const oldSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'DirectiveName',
locations: [DirectiveLocation.FIELD_DEFINITION],
}),
],
});

const newSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'DirectiveName',
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
arg1: {
name: 'arg1',
type: GraphQLNonNull(GraphQLBoolean),
},
},
}),
],
});

expect(findAddedNonNullDirectiveArgs(oldSchema, newSchema)).to.eql([
{
type: BreakingChangeType.NON_NULL_DIRECTIVE_ARG_ADDED,
description: 'A non-null arg arg1 on directive DirectiveName was added',
},
]);
});

it('should detect locations removed from a directive', () => {
const d1 = new GraphQLDirective({
name: 'Directive Name',
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.QUERY],
});

const d2 = new GraphQLDirective({
name: 'Directive Name',
locations: [DirectiveLocation.FIELD_DEFINITION],
});

expect(findRemovedLocationsForDirective(d1, d2)).to.eql([
DirectiveLocation.QUERY,
]);
});

it('should detect locations removed directives within a schema', () => {
const oldSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'Directive Name',
locations: [
DirectiveLocation.FIELD_DEFINITION,
DirectiveLocation.QUERY,
],
}),
],
});

const newSchema = new GraphQLSchema({
directives: [
new GraphQLDirective({
name: 'Directive Name',
locations: [DirectiveLocation.FIELD_DEFINITION],
}),
],
});

expect(findRemovedDirectiveLocations(oldSchema, newSchema)).to.eql([
{
type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
description: 'QUERY was removed from Directive Name',
},
]);
});
});

describe('findDangerousChanges', () => {
Expand Down

0 comments on commit f236898

Please sign in to comment.