From f73fa0d0959bf7e967a1bab159996090aed09120 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Tue, 25 Jun 2024 15:43:50 -0600 Subject: [PATCH] Run Prettier on `internals-js` --- .prettierrc.js | 2 +- .../src/__tests__/definitions.test.ts | 223 +++-- .../directiveAndTypeSpecifications.test.ts | 80 +- .../extractSubgraphsFromSupergraph.test.ts | 51 +- internals-js/src/__tests__/federation.test.ts | 11 +- .../__tests__/graphQLJSSchemaToAST.test.ts | 18 +- internals-js/src/__tests__/operations.test.ts | 769 +++++++++++------- .../removeInaccessibleElements.test.ts | 434 +++++----- .../src/__tests__/schemaUpgrader.test.ts | 116 ++- .../src/__tests__/subgraphValidation.test.ts | 664 ++++++++++----- .../src/__tests__/toAPISchema.test.ts | 11 +- internals-js/src/__tests__/utils.test.ts | 38 +- internals-js/src/__tests__/values.test.ts | 130 +-- .../src/specs/__tests__/coreSpec.test.ts | 241 +++--- 14 files changed, 1729 insertions(+), 1059 deletions(-) diff --git a/.prettierrc.js b/.prettierrc.js index ce4bf3f7b..9fd7a62c7 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -15,7 +15,7 @@ module.exports = { requirePragma: true, overrides: [ { - files: '{docs/{,source/**},.,{gateway-js,federation-integration-testsuite-js,query-planner-js,subgraph-js}/**,test}/{*.js,*.ts}', + files: '{docs/{,source/**},.,{gateway-js,federation-integration-testsuite-js,query-planner-js,subgraph-js,internals-js}/**,test}/{*.js,*.ts}', options: { requirePragma: false, trailingComma: 'all', diff --git a/internals-js/src/__tests__/definitions.test.ts b/internals-js/src/__tests__/definitions.test.ts index 404ecd6f7..5a603c5f3 100644 --- a/internals-js/src/__tests__/definitions.test.ts +++ b/internals-js/src/__tests__/definitions.test.ts @@ -9,12 +9,14 @@ import { UnionType, InputObjectType, } from '../definitions'; -import { - printSchema as printGraphQLjsSchema, -} from 'graphql'; +import { printSchema as printGraphQLjsSchema } from 'graphql'; import { defaultPrintOptions, printSchema } from '../print'; import { buildSchema } from '../buildSchema'; -import { buildSubgraph, federationMetadata, newEmptyFederation2Schema } from '../federation'; +import { + buildSubgraph, + federationMetadata, + newEmptyFederation2Schema, +} from '../federation'; function parseSchema(schema: string): Schema { try { @@ -48,50 +50,71 @@ declare global { namespace jest { interface Matchers { toHaveField(name: string, type?: Type): R; - toHaveDirective(directive: DirectiveDefinition, args?: TArgs): R; + toHaveDirective( + directive: DirectiveDefinition, + args?: TArgs, + ): R; } } } expect.extend({ - toHaveField(parentType: ObjectType | InterfaceType, name: string, type?: Type) { + toHaveField( + parentType: ObjectType | InterfaceType, + name: string, + type?: Type, + ) { const field = parentType.field(name); if (!field) { return { - message: () => `Cannot find field '${name}' in Object Type ${parentType} with fields [${[...parentType.fields()]}]`, - pass: false + message: () => + `Cannot find field '${name}' in Object Type ${parentType} with fields [${[ + ...parentType.fields(), + ]}]`, + pass: false, }; } if (field.name != name) { return { - message: () => `Type ${parentType} has a field linked to name ${name} but that field name is actually ${field.name}`, - pass: false + message: () => + `Type ${parentType} has a field linked to name ${name} but that field name is actually ${field.name}`, + pass: false, }; } if (type && field.type != type) { return { - message: () => `Expected field ${parentType}.${name} to have type ${type} but got type ${field.type}`, - pass: false + message: () => + `Expected field ${parentType}.${name} to have type ${type} but got type ${field.type}`, + pass: false, }; } return { - message: () => `Expected ${parentType} not to have field ${name} but it does (${field})`, - pass: true - } + message: () => + `Expected ${parentType} not to have field ${name} but it does (${field})`, + pass: true, + }; }, - toHaveDirective(element: SchemaElement, definition: DirectiveDefinition, args?: Record) { + toHaveDirective( + element: SchemaElement, + definition: DirectiveDefinition, + args?: Record, + ) { const directives = element.appliedDirectivesOf(definition); if (directives.length == 0) { return { - message: () => `Cannot find directive @${definition} applied to element ${element} (whose applied directives are [${element.appliedDirectives.join(', ')}]`, - pass: false + message: () => + `Cannot find directive @${definition} applied to element ${element} (whose applied directives are [${element.appliedDirectives.join( + ', ', + )}]`, + pass: false, }; } if (!args) { return { - message: () => `Expected directive @${definition} to not be applied to ${element} but it is`, - pass: true + message: () => + `Expected directive @${definition} to not be applied to ${element} but it is`, + pass: true, }; } @@ -99,35 +122,45 @@ expect.extend({ if (directive.matchArguments(args)) { return { // Not 100% certain that message is correct but I don't think it's going to be used ... - message: () => `Expected directive ${directive.name} applied to ${element} to have arguments ${JSON.stringify(args)} but got ${JSON.stringify(directive.arguments)}`, - pass: true + message: () => + `Expected directive ${ + directive.name + } applied to ${element} to have arguments ${JSON.stringify( + args, + )} but got ${JSON.stringify(directive.arguments)}`, + pass: true, }; } } return { - message: () => `Element ${element} has application of directive @${definition} but not with the requested arguments. Got applications: [${directives.join(', ')}]`, - pass: false - } + message: () => + `Element ${element} has application of directive @${definition} but not with the requested arguments. Got applications: [${directives.join( + ', ', + )}]`, + pass: false, + }; }, }); test('building a simple schema programatically', () => { const schema = newEmptyFederation2Schema(); - const queryType = schema.schemaDefinition.setRoot('query', schema.addType(new ObjectType('Query'))).type; + const queryType = schema.schemaDefinition.setRoot( + 'query', + schema.addType(new ObjectType('Query')), + ).type; const typeA = schema.addType(new ObjectType('A')); const key = federationMetadata(schema)!.keyDirective(); queryType.addField('a', typeA); typeA.addField('q', queryType); - typeA.applyDirective(key, { fields: 'a'}); + typeA.applyDirective(key, { fields: 'a' }); expect(queryType).toBe(schema.schemaDefinition.root('query')!.type); expect(queryType).toHaveField('a', typeA); expect(typeA).toHaveField('q', queryType); - expect(typeA).toHaveDirective(key, { fields: 'a'}); + expect(typeA).toHaveDirective(key, { fields: 'a' }); }); - test('parse schema and modify', () => { const sdl = ` schema { @@ -164,7 +197,10 @@ test('parse schema and modify', () => { }); test('removal of all directives of a schema', () => { - const subgraph = buildSubgraph('foo', '', ` + const subgraph = buildSubgraph( + 'foo', + '', + ` schema @foo { query: Query } @@ -188,11 +224,12 @@ test('removal of all directives of a schema', () => { directive @foo on SCHEMA | FIELD_DEFINITION | OBJECT directive @foobar on UNION directive @bar on ARGUMENT_DEFINITION | FIELD_DEFINITION - `).validate(); + `, + ).validate(); const schema = subgraph.schema; for (const element of schema.allSchemaElement()) { - element.appliedDirectives.forEach(d => d.remove()); + element.appliedDirectives.forEach((d) => d.remove()); } expect(subgraph.toString()).toMatchString(` @@ -232,17 +269,20 @@ test('removal of an enum type should remove enum values', () => { } `); - const enumType = schema.type("Enum"); - expectEnumType(enumType) + const enumType = schema.type('Enum'); + expectEnumType(enumType); const enumValues = Array.from(enumType.values); - enumType.remove() + enumType.remove(); for (const value of enumValues) { - expect(value.isAttached()).toBe(false) + expect(value.isAttached()).toBe(false); } }); test('removal of all inaccessible elements of a schema', () => { - const subgraph = buildSubgraph('foo', '', ` + const subgraph = buildSubgraph( + 'foo', + '', + ` schema @foo { query: Query } @@ -265,7 +305,8 @@ test('removal of all inaccessible elements of a schema', () => { directive @inaccessible on FIELD_DEFINITION | OBJECT | ARGUMENT_DEFINITION | UNION directive @foo on SCHEMA | FIELD_DEFINITION directive @bar on ARGUMENT_DEFINITION | FIELD_DEFINITION - `); + `, + ); const schema = subgraph.schema; const inaccessibleDirective = schema.directive('inaccessible')!; @@ -475,7 +516,8 @@ test('handling of descriptions', () => { // Checking we get back the schema through printing it is mostly good enough, but let's just // make sure long descriptions don't get annoying formatting newlines for instance when acessed on the // schema directly. - const longComment = "Something that explains what the product is. This can just be the title of the product, but this can be more than that if we want to. But it should be useful you know, otherwise our customer won't buy it."; + const longComment = + "Something that explains what the product is. This can just be the title of the product, but this can be more than that if we want to. But it should be useful you know, otherwise our customer won't buy it."; const product = schema.type('Product'); expectInterfaceType(product); expect(product.field('description')!.description).toBe(longComment); @@ -536,9 +578,18 @@ test('handling of extensions', () => { const aunion = schema.type('AUnion'); expectUnionType(aunion); - expect([...aunion.types()].map(t => t.name)).toEqual(['AType', 'AType2', 'AType3']); - - expect(subgraph.toString({ ...defaultPrintOptions, mergeTypesAndExtensions: true })).toMatchString(` + expect([...aunion.types()].map((t) => t.name)).toEqual([ + 'AType', + 'AType2', + 'AType3', + ]); + + expect( + subgraph.toString({ + ...defaultPrintOptions, + mergeTypesAndExtensions: true, + }), + ).toMatchString(` directive @foo on SCALAR type Query { @@ -666,7 +717,7 @@ describe('type extension where definition is empty', () => { expect(schema.type('Foo')?.hasNonExtensionElements()).toBeTruthy(); expect(schema.type('Foo')?.hasExtensionElements()).toBeTruthy(); }); -}) +}); test('reject type defined multiple times', () => { const sdl = ` @@ -683,7 +734,9 @@ test('reject type defined multiple times', () => { } `; - expect(() => buildSchema(sdl).validate()).toThrow('There can be only one type named "Foo"'); + expect(() => buildSchema(sdl).validate()).toThrow( + 'There can be only one type named "Foo"', + ); }); test('default arguments for directives', () => { @@ -718,12 +771,12 @@ test('default arguments for directives', () => { const d3 = v3.appliedDirectivesOf(exampleDirective)[0]; expect(d1.arguments()).toEqual({}); - expect(d2.arguments()).toEqual({ inputObject: {}}); - expect(d3.arguments()).toEqual({ inputObject: { number: 3 }}); + expect(d2.arguments()).toEqual({ inputObject: {} }); + expect(d3.arguments()).toEqual({ inputObject: { number: 3 } }); - expect(d1.arguments(true)).toEqual({ inputObject: { number: 3 }}); - expect(d2.arguments(true)).toEqual({ inputObject: { number: 3 }}); - expect(d3.arguments(true)).toEqual({ inputObject: { number: 3 }}); + expect(d1.arguments(true)).toEqual({ inputObject: { number: 3 } }); + expect(d2.arguments(true)).toEqual({ inputObject: { number: 3 } }); + expect(d3.arguments(true)).toEqual({ inputObject: { number: 3 } }); }); describe('clone', () => { @@ -741,17 +794,20 @@ describe('clone', () => { } `).clone(); - expect(schema.elementByCoordinate("@foo")).toBeDefined(); - expect(schema.elementByCoordinate("@wizz")).toBeDefined(); - expect(schema.elementByCoordinate("@fuzz")).toBeDefined(); - expect(schema.elementByCoordinate("@buzz")).toBeDefined(); - expect(schema.elementByCoordinate("@baz")).toBeDefined(); - expect(schema.elementByCoordinate("@bar")).toBeDefined(); + expect(schema.elementByCoordinate('@foo')).toBeDefined(); + expect(schema.elementByCoordinate('@wizz')).toBeDefined(); + expect(schema.elementByCoordinate('@fuzz')).toBeDefined(); + expect(schema.elementByCoordinate('@buzz')).toBeDefined(); + expect(schema.elementByCoordinate('@baz')).toBeDefined(); + expect(schema.elementByCoordinate('@bar')).toBeDefined(); }); // https://github.com/apollographql/federation/issues/1794 it('should allow using an imported federation diretive in another directive', () => { - const schema = buildSubgraph('foo', "", ` + const schema = buildSubgraph( + 'foo', + '', + ` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@tag"]) @@ -760,9 +816,10 @@ describe('clone', () => { type Query { hi: String! @foo } - `).schema.clone(); - expect(schema.elementByCoordinate("@foo")).toBeDefined(); - expect(schema.elementByCoordinate("@tag")).toBeDefined(); + `, + ).schema.clone(); + expect(schema.elementByCoordinate('@foo')).toBeDefined(); + expect(schema.elementByCoordinate('@tag')).toBeDefined(); }); it('should allow type use in directives', () => { @@ -775,7 +832,7 @@ describe('clone', () => { } `).clone(); - expect(schema.elementByCoordinate("@foo")).toBeDefined(); + expect(schema.elementByCoordinate('@foo')).toBeDefined(); }); it('should allow recursive directive definitions', () => { @@ -787,8 +844,8 @@ describe('clone', () => { getData(arg: String @foo): String! } `).clone(); - expect(schema.elementByCoordinate("@foo")).toBeDefined(); - expect(schema.elementByCoordinate("@bar")).toBeDefined(); + expect(schema.elementByCoordinate('@foo')).toBeDefined(); + expect(schema.elementByCoordinate('@bar')).toBeDefined(); }); }); @@ -825,7 +882,9 @@ describe('Conversion to graphQL-js schema', () => { `; const schema = parseSchema(sdl); - expect(printGraphQLjsSchema(schema.toGraphQLJSSchema({ includeDefer: true }))).toMatchString(` + expect( + printGraphQLjsSchema(schema.toGraphQLJSSchema({ includeDefer: true })), + ).toMatchString(` directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT type Query { @@ -833,7 +892,11 @@ describe('Conversion to graphQL-js schema', () => { } `); - expect(printGraphQLjsSchema(schema.toGraphQLJSSchema({ includeDefer: true, includeStream: true }))).toMatchString(` + expect( + printGraphQLjsSchema( + schema.toGraphQLJSSchema({ includeDefer: true, includeStream: true }), + ), + ).toMatchString(` directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT directive @stream(label: String, initialCount: Int = 0, if: Boolean! = true) on FIELD @@ -868,7 +931,9 @@ describe('Conversion to graphQL-js schema', () => { } `); - expect(printGraphQLjsSchema(apiSchema.toGraphQLJSSchema({ includeDefer: true }))).toMatchString(` + expect( + printGraphQLjsSchema(apiSchema.toGraphQLJSSchema({ includeDefer: true })), + ).toMatchString(` directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT type Query { @@ -876,7 +941,14 @@ describe('Conversion to graphQL-js schema', () => { } `); - expect(printGraphQLjsSchema(apiSchema.toGraphQLJSSchema({ includeDefer: true, includeStream: true }))).toMatchString(` + expect( + printGraphQLjsSchema( + apiSchema.toGraphQLJSSchema({ + includeDefer: true, + includeStream: true, + }), + ), + ).toMatchString(` directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT directive @stream(label: String, initialCount: Int = 0, if: Boolean! = true) on FIELD @@ -888,7 +960,6 @@ describe('Conversion to graphQL-js schema', () => { }); }); - test('retrieving elements by coordinate', () => { const sdl = ` directive @foo(bar: Int) on FIELD @@ -919,14 +990,20 @@ test('retrieving elements by coordinate', () => { `; const schema = parseSchema(sdl); - expect(schema.elementByCoordinate('Query')).toBe(schema.schemaDefinition.rootType('query')); - expect(schema.elementByCoordinate('Query.t')).toBe(schema.schemaDefinition.rootType('query')?.field('t')); + expect(schema.elementByCoordinate('Query')).toBe( + schema.schemaDefinition.rootType('query'), + ); + expect(schema.elementByCoordinate('Query.t')).toBe( + schema.schemaDefinition.rootType('query')?.field('t'), + ); const typeT = schema.type('T') as ObjectType; expect(schema.elementByCoordinate('T')).toBe(typeT); expect(schema.elementByCoordinate('T.f1')).toBe(typeT.field('f1')); expect(schema.elementByCoordinate('T.f2')).toBe(typeT.field('f2')); - expect(schema.elementByCoordinate('T.f1(x:)')).toBe(typeT.field('f1')?.argument('x')); + expect(schema.elementByCoordinate('T.f1(x:)')).toBe( + typeT.field('f1')?.argument('x'), + ); const typeI = schema.type('I') as InterfaceType; expect(schema.elementByCoordinate('I')).toBe(typeI); @@ -944,7 +1021,9 @@ test('retrieving elements by coordinate', () => { const directiveFoo = schema.directive('foo')!; expect(schema.elementByCoordinate('@foo')).toBe(directiveFoo); - expect(schema.elementByCoordinate('@foo(bar:)')).toBe(directiveFoo.argument('bar')); + expect(schema.elementByCoordinate('@foo(bar:)')).toBe( + directiveFoo.argument('bar'), + ); expect(schema.elementByCoordinate('SomeType')).toBeUndefined(); expect(schema.elementByCoordinate('T.f3')).toBeUndefined(); @@ -961,7 +1040,7 @@ test('retrieving elements by coordinate', () => { expect(() => schema.elementByCoordinate('O.x(foo:)')).toThrow(); // Note that because 'Date' is a scalar, it cannot have fields expect(() => schema.elementByCoordinate('Date.bar')).toThrow(); -}) +}); test('parse error', () => { const schema = ` diff --git a/internals-js/src/__tests__/directiveAndTypeSpecifications.test.ts b/internals-js/src/__tests__/directiveAndTypeSpecifications.test.ts index df0fcc366..dfaa9aaa6 100644 --- a/internals-js/src/__tests__/directiveAndTypeSpecifications.test.ts +++ b/internals-js/src/__tests__/directiveAndTypeSpecifications.test.ts @@ -1,41 +1,61 @@ -import { DirectiveLocation } from "graphql"; -import "../definitions"; -import { createDirectiveSpecification } from "../directiveAndTypeSpecification"; -import { ARGUMENT_COMPOSITION_STRATEGIES } from "../argumentCompositionStrategies"; -import { TAG_VERSIONS } from "../specs/tagSpec"; +import { DirectiveLocation } from 'graphql'; +import '../definitions'; +import { createDirectiveSpecification } from '../directiveAndTypeSpecification'; +import { ARGUMENT_COMPOSITION_STRATEGIES } from '../argumentCompositionStrategies'; +import { TAG_VERSIONS } from '../specs/tagSpec'; const supergraphSpecification = () => TAG_VERSIONS.latest(); test('must have supergraph link if composed', () => { - expect(() => createDirectiveSpecification({ - name: 'foo', - locations: [DirectiveLocation.OBJECT], - composes: true, - })).toThrow('Should provide a @link specification to use in supergraph for directive @foo if it composes'); + expect(() => + createDirectiveSpecification({ + name: 'foo', + locations: [DirectiveLocation.OBJECT], + composes: true, + }), + ).toThrow( + 'Should provide a @link specification to use in supergraph for directive @foo if it composes', + ); }); test('must have a merge strategy on all arguments if any', () => { - expect(() => createDirectiveSpecification({ - name: 'foo', - locations: [DirectiveLocation.OBJECT], - composes: true, - supergraphSpecification, - args: [ - { name: "v1", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX }, - { name: "v2", type: (schema) => schema.intType() } - ], - })).toThrow('Invalid directive specification for @foo: not all arguments define a composition strategy'); + expect(() => + createDirectiveSpecification({ + name: 'foo', + locations: [DirectiveLocation.OBJECT], + composes: true, + supergraphSpecification, + args: [ + { + name: 'v1', + type: (schema) => schema.intType(), + compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX, + }, + { name: 'v2', type: (schema) => schema.intType() }, + ], + }), + ).toThrow( + 'Invalid directive specification for @foo: not all arguments define a composition strategy', + ); }); test('must be not be repeatable if it has a merge strategy', () => { - expect(() => createDirectiveSpecification({ - name: 'foo', - locations: [DirectiveLocation.OBJECT], - composes: true, - repeatable: true, - supergraphSpecification, - args: [ - { name: "v", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX }, - ], - })).toThrow('Invalid directive specification for @foo: @foo is repeatable and should not define composition strategy for its arguments'); + expect(() => + createDirectiveSpecification({ + name: 'foo', + locations: [DirectiveLocation.OBJECT], + composes: true, + repeatable: true, + supergraphSpecification, + args: [ + { + name: 'v', + type: (schema) => schema.intType(), + compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX, + }, + ], + }), + ).toThrow( + 'Invalid directive specification for @foo: @foo is repeatable and should not define composition strategy for its arguments', + ); }); diff --git a/internals-js/src/__tests__/extractSubgraphsFromSupergraph.test.ts b/internals-js/src/__tests__/extractSubgraphsFromSupergraph.test.ts index 6685a7974..955f1c823 100644 --- a/internals-js/src/__tests__/extractSubgraphsFromSupergraph.test.ts +++ b/internals-js/src/__tests__/extractSubgraphsFromSupergraph.test.ts @@ -1,5 +1,4 @@ -import { Supergraph, InputObjectType, ObjectType, printSchema } from ".."; - +import { Supergraph, InputObjectType, ObjectType, printSchema } from '..'; test('handles types having no fields referenced by other objects in a subgraph correctly', () => { /* @@ -107,7 +106,7 @@ test('handles types having no fields referenced by other objects in a subgraph c expect(c.type('A')).toBeUndefined(); expect(c.type('B')).toBeUndefined(); -}) +}); test('handles types having no fields referenced by other interfaces in a subgraph correctly', () => { /* @@ -213,7 +212,7 @@ test('handles types having no fields referenced by other interfaces in a subgrap expect(c.type('A')).toBeUndefined(); expect(c.type('B')).toBeUndefined(); -}) +}); test('handles types having no fields referenced by other unions in a subgraph correctly', () => { /* @@ -314,7 +313,7 @@ test('handles types having no fields referenced by other unions in a subgraph co expect(b.type('B')).toBeUndefined(); expect(b.type('C')).toBeUndefined(); expect(a.type('D')).toBeDefined(); -}) +}); test('handles types having only some of their fields removed in a subgraph correctly', () => { /* @@ -425,7 +424,7 @@ test('handles types having only some of their fields removed in a subgraph corre expect(c.type('A')).toBeDefined(); expect(c.type('B')).toBeDefined(); -}) +}); test('handles unions types having no members in a subgraph correctly', () => { /* @@ -527,7 +526,7 @@ test('handles unions types having no members in a subgraph correctly', () => { expect(b.type('B')).toBeUndefined(); expect(b.type('C')).toBeUndefined(); expect(a.type('D')).toBeDefined(); -}) +}); test('preserves default values of input object fields', () => { const supergraph = ` @@ -583,12 +582,14 @@ test('preserves default values of input object fields', () => { const subgraphs = Supergraph.build(supergraph).subgraphs(); - const subgraph = subgraphs.get('service') - const inputType = subgraph?.schema.type('Input') as InputObjectType | undefined - const inputFieldA = inputType?.field('a') + const subgraph = subgraphs.get('service'); + const inputType = subgraph?.schema.type('Input') as + | InputObjectType + | undefined; + const inputFieldA = inputType?.field('a'); - expect(inputFieldA?.defaultValue).toBe(1234) -}) + expect(inputFieldA?.defaultValue).toBe(1234); +}); test('throw meaningful error for invalid federation directive fieldSet', () => { const supergraph = ` @@ -645,13 +646,13 @@ test('throw meaningful error for invalid federation directive fieldSet', () => { `; expect(() => Supergraph.build(supergraph).subgraphs()).toThrow( - 'Error extracting subgraph "serviceB" from the supergraph: this might be due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n' - + 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph.\n' - + '\n' - + 'Details:\n' - + '[serviceB] On field "A.a", for @requires(fields: "b { x }"): Cannot query field "b" on type "A" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).' + 'Error extracting subgraph "serviceB" from the supergraph: this might be due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n' + + 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph.\n' + + '\n' + + 'Details:\n' + + '[serviceB] On field "A.a", for @requires(fields: "b { x }"): Cannot query field "b" on type "A" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).', ); -}) +}); test('throw meaningful error for type erased from supergraph due to extending an entity without a key', () => { // Supergraph generated by fed1 composition from: @@ -731,13 +732,13 @@ test('throw meaningful error for type erased from supergraph due to extending an `; expect(() => Supergraph.build(supergraph).subgraphs()).toThrow( - 'Error extracting subgraphs from the supergraph: this might be due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n' - + 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph.\n' - + '\n' - + 'Details:\n' - + 'Error: Cannot find type "T" in subgraph "serviceB"' + 'Error extracting subgraphs from the supergraph: this might be due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n' + + 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph.\n' + + '\n' + + 'Details:\n' + + 'Error: Cannot find type "T" in subgraph "serviceB"', ); -}) +}); test('types that are empty because of overridden fields are erased', () => { const supergraph = ` @@ -919,7 +920,7 @@ type T prop: String! }`); -expect(printedSchema).toMatch(` + expect(printedSchema).toMatch(` type U @key(fields: "id") { diff --git a/internals-js/src/__tests__/federation.test.ts b/internals-js/src/__tests__/federation.test.ts index dd81d9293..eae48a335 100644 --- a/internals-js/src/__tests__/federation.test.ts +++ b/internals-js/src/__tests__/federation.test.ts @@ -1,7 +1,7 @@ -import gql from "graphql-tag"; -import { buildSubgraph, federationMetadata } from ".."; +import gql from 'graphql-tag'; +import { buildSubgraph, federationMetadata } from '..'; -it("detects federation 1 subgraphs correctly", () => { +it('detects federation 1 subgraphs correctly', () => { const schema = gql` type Query { s: String @@ -14,10 +14,9 @@ it("detects federation 1 subgraphs correctly", () => { expect(metadata?.isFed2Schema()).toBeFalsy(); }); -it("detects federation 2 subgraphs correctly", () => { +it('detects federation 2 subgraphs correctly', () => { const schema = gql` - extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0") + extend schema @link(url: "https://specs.apollo.dev/federation/v2.0") type Query { s: String diff --git a/internals-js/src/__tests__/graphQLJSSchemaToAST.test.ts b/internals-js/src/__tests__/graphQLJSSchemaToAST.test.ts index 9c5fa0735..acacc7e29 100644 --- a/internals-js/src/__tests__/graphQLJSSchemaToAST.test.ts +++ b/internals-js/src/__tests__/graphQLJSSchemaToAST.test.ts @@ -3,15 +3,20 @@ import { buildSchema, GraphQLSchema, introspectionFromSchema, - print -} from "graphql"; -import { graphQLJSSchemaToAST } from "../graphQLJSSchemaToAST"; - -function validateRoundtrip(schemaStr: string, expectedWithoutASTNodes: string | undefined = schemaStr) { + print, +} from 'graphql'; +import { graphQLJSSchemaToAST } from '../graphQLJSSchemaToAST'; + +function validateRoundtrip( + schemaStr: string, + expectedWithoutASTNodes: string | undefined = schemaStr, +) { const schema = buildSchema(schemaStr); expect(print(graphQLJSSchemaToAST(schema))).toMatchString(schemaStr); if (expectedWithoutASTNodes) { - expect(print(graphQLJSSchemaToAST(withoutASTNodes(schema)))).toMatchString(expectedWithoutASTNodes); + expect(print(graphQLJSSchemaToAST(withoutASTNodes(schema)))).toMatchString( + expectedWithoutASTNodes, + ); } } @@ -152,4 +157,3 @@ it('round-trip with extensions', () => { validateRoundtrip(schema, noAST); }); - diff --git a/internals-js/src/__tests__/operations.test.ts b/internals-js/src/__tests__/operations.test.ts index 6e26b7fc2..28c048c9e 100644 --- a/internals-js/src/__tests__/operations.test.ts +++ b/internals-js/src/__tests__/operations.test.ts @@ -6,8 +6,26 @@ import { } from '../definitions'; import { buildSchema } from '../buildSchema'; import { FederationBlueprint } from '../federation'; -import { FragmentRestrictionAtType, MutableSelectionSet, NamedFragmentDefinition, Operation, operationFromDocument, parseOperation } from '../operations'; -import { DocumentNode, FieldNode, GraphQLError, Kind, OperationDefinitionNode, OperationTypeNode, parse, SelectionNode, SelectionSetNode, validate } from 'graphql'; +import { + FragmentRestrictionAtType, + MutableSelectionSet, + NamedFragmentDefinition, + Operation, + operationFromDocument, + parseOperation, +} from '../operations'; +import { + DocumentNode, + FieldNode, + GraphQLError, + Kind, + OperationDefinitionNode, + OperationTypeNode, + parse, + SelectionNode, + SelectionSetNode, + validate, +} from 'graphql'; import { assert } from '../utils'; import gql from 'graphql-tag'; @@ -52,7 +70,9 @@ describe('generate query fragments', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { entities { ... on B { @@ -66,7 +86,8 @@ describe('generate query fragments', () => { } } } - `); + `, + ); const withGeneratedFragments = operation.generateQueryFragments(); console.log(withGeneratedFragments.toString()); @@ -102,9 +123,9 @@ describe('fragments optimization', () => { query, expanded, }: { - schema: Schema, - query: string, - expanded: string, + schema: Schema; + query: string; + expanded: string; }) { const operation = parseOperation(schema, query); const withoutFragments = operation.expandAllFragments(); @@ -146,7 +167,9 @@ describe('fragments optimization', () => { union U = T1 | T2 `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` fragment OnT1 on T1 { a b @@ -177,7 +200,8 @@ describe('fragments optimization', () => { } } } - `); + `, + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -270,7 +294,9 @@ describe('fragments optimization', () => { union U = T1 | T2 `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` fragment OnT1 on T1 { a b @@ -309,7 +335,8 @@ describe('fragments optimization', () => { } } } - `); + `, + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -478,7 +505,6 @@ describe('fragments optimization', () => { } `); - // The subtlety here is that `FA` contains `__typename` and so after we're reused it, the // selection will look like: // { @@ -493,7 +519,7 @@ describe('fragments optimization', () => { // directly, it is fine to reuse). testFragmentsRoundtrip({ schema, - query: ` + query: ` fragment FA on A { __typename x @@ -1027,7 +1053,9 @@ describe('fragments optimization', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t { ...TFrag @@ -1047,7 +1075,8 @@ describe('fragments optimization', () => { __typename id } - `); + `, + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1279,7 +1308,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { id @@ -1296,8 +1327,11 @@ describe('fragments optimization', () => { f(arg: 1) } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1339,7 +1373,9 @@ describe('fragments optimization', () => { // // And so this test does make sure we do not generate the query above (do not use `F1` in `t1`). const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment F1 on I { @@ -1388,7 +1424,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { id @@ -1410,8 +1448,11 @@ describe('fragments optimization', () => { } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1433,7 +1474,9 @@ describe('fragments optimization', () => { // first, and then we need to make sure we do not apply `F2` even though it's restriction // inside `t1` matches its selection set. const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment F1 on T1 { @@ -1493,7 +1536,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { id @@ -1524,8 +1569,11 @@ describe('fragments optimization', () => { } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1556,7 +1604,9 @@ describe('fragments optimization', () => { // within the first `T1` branch. But they can't both be used because their `... on WithF` part conflict, // and even though that part is dead in `T1`, this would still be illegal graphQL. const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment F1 on I { @@ -1613,7 +1663,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` fragment onV1V2 on SomeV { ... on V1 { x @@ -1636,8 +1688,11 @@ describe('fragments optimization', () => { } } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1662,7 +1717,9 @@ describe('fragments optimization', () => { `); const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment onV1V2 on SomeV { @@ -1716,7 +1773,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` fragment onV1V3 on SomeV { ... on V1 { x @@ -1749,8 +1808,11 @@ describe('fragments optimization', () => { } } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1781,7 +1843,9 @@ describe('fragments optimization', () => { `); const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment onV1V3 on SomeV { @@ -1856,7 +1920,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` fragment onV1V2 on SomeV { ... on V1 { x @@ -1887,8 +1953,11 @@ describe('fragments optimization', () => { } } } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -1921,7 +1990,9 @@ describe('fragments optimization', () => { `); const optimized = withoutFragments.optimize(operation.fragments!, 1); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment onV1V2 on SomeV { @@ -1981,7 +2052,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t1 { ...GetAll @@ -2004,8 +2077,11 @@ describe('fragments optimization', () => { fragment GetT2 on T2 { b } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -2031,7 +2107,9 @@ describe('fragments optimization', () => { // "getting rid" of the `...GetT2` spread, keeping in the query, which is // invalid (we cannot have `...GetT2` inside `t1`). const optimized = withoutFragments.optimize(operation.fragments!, 2); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment GetT2 on T2 { @@ -2074,7 +2152,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { u1 { ...F1 @@ -2109,8 +2189,11 @@ describe('fragments optimization', () => { } ...F2 } - `); - expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); + `, + ); + expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual( + [], + ); const withoutFragments = operation.expandAllFragments(); expect(withoutFragments.toString()).toMatchString(` @@ -2149,9 +2232,13 @@ describe('fragments optimization', () => { // We use `mapToExpandedSelectionSets` with a no-op mapper because this will still expand the selections // and re-optimize them, which 1) happens to match what happens in the query planner and 2) is necessary // for reproducing a bug that this test was initially added to cover. - const newFragments = operation.fragments!.mapToExpandedSelectionSets((s) => s); + const newFragments = operation.fragments!.mapToExpandedSelectionSets( + (s) => s, + ); const optimized = withoutFragments.optimize(newFragments, 2); - expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]); + expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual( + [], + ); expect(optimized.toString()).toMatchString(` fragment F3 on U { @@ -2209,7 +2296,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { ...Outer @@ -2233,7 +2322,8 @@ describe('fragments optimization', () => { y } } - `); + `, + ); expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); const withoutFragments = operation.expandAllFragments(); @@ -2309,7 +2399,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { ...Outer @@ -2342,7 +2434,8 @@ describe('fragments optimization', () => { fragment WillBeUnused on Y { v } - `); + `, + ); expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); const withoutFragments = operation.expandAllFragments(); @@ -2387,7 +2480,9 @@ describe('fragments optimization', () => { `); const gqlSchema = schema.toGraphQLJSSchema(); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t1 { ...TFields @@ -2416,7 +2511,8 @@ describe('fragments optimization', () => { x y } - `); + `, + ); expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]); const withoutFragments = operation.expandAllFragments(); @@ -2505,14 +2601,23 @@ describe('validations', () => { `); expect(() => { - parseOperation(schema, ` + parseOperation( + schema, + ` ${rootKind} { ... ${directive} { x } } - `) - }).toThrowError(new GraphQLError(`The @defer and @stream directives cannot be used on ${rootKind} root type "${defaultRootName(rootKind as SchemaRootKind)}"`)); + `, + ); + }).toThrowError( + new GraphQLError( + `The @defer and @stream directives cannot be used on ${rootKind} root type "${defaultRootName( + rootKind as SchemaRootKind, + )}"`, + ), + ); }); test('allows nullable variable for non-nullable input field with default', () => { @@ -2527,11 +2632,14 @@ describe('validations', () => { `); // Just testing that this parse correctly and does not throw an exception. - parseOperation(schema, ` + parseOperation( + schema, + ` query test($x: Int) { f(i: { x: $x }) } - `); + `, + ); }); }); @@ -2567,77 +2675,69 @@ describe('empty branches removal', () => { kind: Kind.OPERATION_DEFINITION, operation: OperationTypeNode.QUERY, selectionSet: op, - } + }; const document: DocumentNode = { kind: Kind.DOCUMENT, definitions: [opDef], - } + }; operation = operationFromDocument(schema, document, { validate: false }); } - return operation.selectionSet.withoutEmptyBranches()?.toString() + return operation.selectionSet.withoutEmptyBranches()?.toString(); }; - - it.each([ - '{ t { a } }', - '{ t { a b } }', - '{ t { a c { x y } } }', - ])('is identity if there is no empty branch', (op) => { - expect(withoutEmptyBranches(op)).toBe(op); - }); + it.each(['{ t { a } }', '{ t { a b } }', '{ t { a c { x y } } }'])( + 'is identity if there is no empty branch', + (op) => { + expect(withoutEmptyBranches(op)).toBe(op); + }, + ); it('removes simple empty branches', () => { - expect(withoutEmptyBranches( - astSSet( - astField('t', astSSet( - astField('a'), - astField('c', astSSet()), - )) - ) - )).toBe('{ t { a } }'); - - expect(withoutEmptyBranches( - astSSet( - astField('t', astSSet( - astField('c', astSSet()), - astField('a'), - )) - ) - )).toBe('{ t { a } }'); - - expect(withoutEmptyBranches( - astSSet( - astField('t', astSSet()) - ) - )).toBeUndefined(); + expect( + withoutEmptyBranches( + astSSet( + astField('t', astSSet(astField('a'), astField('c', astSSet()))), + ), + ), + ).toBe('{ t { a } }'); + + expect( + withoutEmptyBranches( + astSSet( + astField('t', astSSet(astField('c', astSSet()), astField('a'))), + ), + ), + ).toBe('{ t { a } }'); + + expect( + withoutEmptyBranches(astSSet(astField('t', astSSet()))), + ).toBeUndefined(); }); it('removes cascading empty branches', () => { - expect(withoutEmptyBranches( - astSSet( - astField('t', astSSet( - astField('c', astSSet()), - )) - ) - )).toBeUndefined(); - - expect(withoutEmptyBranches( - astSSet( - astField('u'), - astField('t', astSSet( - astField('c', astSSet()), - )) - ) - )).toBe('{ u }'); - - expect(withoutEmptyBranches( - astSSet( - astField('t', astSSet( - astField('c', astSSet()), - )), - astField('u'), - ) - )).toBe('{ u }'); + expect( + withoutEmptyBranches( + astSSet(astField('t', astSSet(astField('c', astSSet())))), + ), + ).toBeUndefined(); + + expect( + withoutEmptyBranches( + astSSet( + astField('u'), + astField('t', astSSet(astField('c', astSSet()))), + ), + ), + ).toBe('{ u }'); + + expect( + withoutEmptyBranches( + astSSet( + astField('t', astSSet(astField('c', astSSet()))), + astField('u'), + ), + ), + ).toBe('{ u }'); }); }); @@ -2676,7 +2776,9 @@ describe('basic operations', () => { directive @customSkip(if: Boolean!, label: String!) on FIELD | INLINE_FRAGMENT `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t { v1 @@ -2697,12 +2799,15 @@ describe('basic operations', () => { } } } - `); + `, + ); test('forEachElement', () => { // We collect a pair of (parent type, field-or-fragment). const actual: [string, string][] = []; - operation.selectionSet.forEachElement((elt) => actual.push([elt.parentType.name, elt.toString()])); + operation.selectionSet.forEachElement((elt) => + actual.push([elt.parentType.name, elt.toString()]), + ); expect(actual).toStrictEqual([ ['Query', 't'], ['T', 'v1'], @@ -2717,20 +2822,23 @@ describe('basic operations', () => { ['B', 'b2'], ['T', 'v2'], ]); - }) + }); describe('same field merging', () => { test('do merge when same field and no directive', () => { - const operation = operationFromDocument(schema, gql` - query Test { - t { - v1 - } - t { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test { + t { + v1 + } + t { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test { @@ -2743,16 +2851,19 @@ describe('basic operations', () => { }); test('do merge when both have the _same_ directive', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t @skip(if: $skipIf) { - v1 - } - t @skip(if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t @skip(if: $skipIf) { + v1 + } + t @skip(if: $skipIf) { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2765,16 +2876,19 @@ describe('basic operations', () => { }); test('do merge when both have the _same_ directive, even if argument order differs', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t @customSkip(if: $skipIf, label: "foo") { - v1 - } - t @customSkip(label: "foo", if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t @customSkip(if: $skipIf, label: "foo") { + v1 + } + t @customSkip(label: "foo", if: $skipIf) { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2787,16 +2901,19 @@ describe('basic operations', () => { }); test('do not merge when one has a directive and the other do not', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t { - v1 - } - t @skip(if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t { + v1 + } + t @skip(if: $skipIf) { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2811,16 +2928,19 @@ describe('basic operations', () => { }); test('do not merge when both have _differing_ directives', () => { - const operation = operationFromDocument(schema, gql` - query Test($skip1: Boolean!, $skip2: Boolean!) { - t @skip(if: $skip1) { - v1 - } - t @skip(if: $skip2) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skip1: Boolean!, $skip2: Boolean!) { + t @skip(if: $skip1) { + v1 + } + t @skip(if: $skip2) { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skip1: Boolean!, $skip2: Boolean!) { @@ -2835,16 +2955,19 @@ describe('basic operations', () => { }); test('do not merge @defer directive, even if applied the same way', () => { - const operation = operationFromDocument(schema, gql` - query Test { - t @defer { - v1 - } - t @defer { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test { + t @defer { + v1 + } + t @defer { + v2 + } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test { @@ -2861,18 +2984,21 @@ describe('basic operations', () => { describe('same fragment merging', () => { test('do merge when same fragment and no directive', () => { - const operation = operationFromDocument(schema, gql` - query Test { - t { - ... on T { - v1 - } - ... on T { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test { + t { + ... on T { + v1 + } + ... on T { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test { @@ -2887,18 +3013,21 @@ describe('basic operations', () => { }); test('do merge when both have the _same_ directive', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t { - ... on T @skip(if: $skipIf) { - v1 - } - ... on T @skip(if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t { + ... on T @skip(if: $skipIf) { + v1 + } + ... on T @skip(if: $skipIf) { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2913,18 +3042,21 @@ describe('basic operations', () => { }); test('do merge when both have the _same_ directive, even if argument order differs', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t { - ... on T @customSkip(if: $skipIf, label: "foo") { - v1 - } - ... on T @customSkip(label: "foo", if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t { + ... on T @customSkip(if: $skipIf, label: "foo") { + v1 + } + ... on T @customSkip(label: "foo", if: $skipIf) { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2939,18 +3071,21 @@ describe('basic operations', () => { }); test('do not merge when one has a directive and the other do not', () => { - const operation = operationFromDocument(schema, gql` - query Test($skipIf: Boolean!) { - t { - ... on T { - v1 - } - ... on T @skip(if: $skipIf) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skipIf: Boolean!) { + t { + ... on T { + v1 + } + ... on T @skip(if: $skipIf) { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skipIf: Boolean!) { @@ -2967,18 +3102,21 @@ describe('basic operations', () => { }); test('do not merge when both have _differing_ directives', () => { - const operation = operationFromDocument(schema, gql` - query Test($skip1: Boolean!, $skip2: Boolean!) { - t { - ... on T @skip(if: $skip1) { - v1 - } - ... on T @skip(if: $skip2) { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test($skip1: Boolean!, $skip2: Boolean!) { + t { + ... on T @skip(if: $skip1) { + v1 + } + ... on T @skip(if: $skip2) { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test($skip1: Boolean!, $skip2: Boolean!) { @@ -2995,18 +3133,21 @@ describe('basic operations', () => { }); test('do not merge @defer directive, even if applied the same way', () => { - const operation = operationFromDocument(schema, gql` - query Test { - t { - ... on T @defer { - v1 - } - ... on T @defer { - v2 + const operation = operationFromDocument( + schema, + gql` + query Test { + t { + ... on T @defer { + v1 + } + ... on T @defer { + v2 + } } } - } - `); + `, + ); expect(operation.toString()).toMatchString(` query Test { @@ -3040,20 +3181,17 @@ describe('MutableSelectionSet', () => { `); type Value = { - count: number + count: number; }; let calls = 0; const sets: string[] = []; const queryType = schema.schemaDefinition.rootType('query')!; - const ss = MutableSelectionSet.emptyWithMemoized( - queryType, - (s) => { - sets.push(s.toString()); - return { count: ++calls }; - } - ); + const ss = MutableSelectionSet.emptyWithMemoized(queryType, (s) => { + sets.push(s.toString()); + return { count: ++calls }; + }); expect(ss.memoized().count).toBe(1); // Calling a 2nd time with no change to make sure we're not re-generating the value. @@ -3081,7 +3219,12 @@ describe('MutableSelectionSet', () => { expect(ss.memoized().count).toBe(3); // But that of the clone should have changed. expect(cloned.memoized().count).toBe(4); - expect(sets).toStrictEqual(['{}', '{ t { v1 } }', '{ t { v1 v3 } }', '{ t { v1 v3 v2 } }']); + expect(sets).toStrictEqual([ + '{}', + '{ t { v1 } }', + '{ t { v1 v3 } }', + '{ t { v1 v3 v2 } }', + ]); // And here we make sure that if we update the fist selection, we don't have v3 in the set received ss.updates().add(parseOperation(schema, `{ t { v4 } }`).selectionSet); @@ -3089,7 +3232,13 @@ describe('MutableSelectionSet', () => { // the total count should be 5 (even if the previous count for `ss` was only 3). expect(ss.memoized().count).toBe(5); expect(cloned.memoized().count).toBe(4); - expect(sets).toStrictEqual(['{}', '{ t { v1 } }', '{ t { v1 v3 } }', '{ t { v1 v3 v2 } }', '{ t { v1 v3 v4 } }']); + expect(sets).toStrictEqual([ + '{}', + '{ t { v1 } }', + '{ t { v1 v3 } }', + '{ t { v1 v3 v2 } }', + '{ t { v1 v3 v4 } }', + ]); }); }); @@ -3129,36 +3278,51 @@ describe('unsatisfiable branches removal', () => { `); const normalized = (op: string) => { - return parseOperation(schema, op).normalize().toString(false, false) + return parseOperation(schema, op).normalize().toString(false, false); }; - - it.each([ - '{ i { a } }', - '{ i { ... on T1 { a b c } } }', - ])('is identity if there is no unsatisfiable branches', (op) => { - expect(normalized(op)).toBe(op); - }); + it.each(['{ i { a } }', '{ i { ... on T1 { a b c } } }'])( + 'is identity if there is no unsatisfiable branches', + (op) => { + expect(normalized(op)).toBe(op); + }, + ); it.each([ { input: '{ i { ... on I { a } } }', output: '{ i { a } }' }, - { input: '{ i { ... on T1 { ... on I { a b } } } }', output: '{ i { ... on T1 { a b } } }' }, - { input: '{ i { ... on I { a ... on T2 { d } } } }', output: '{ i { a ... on T2 { d } } }' }, - { input: '{ i { ... on T2 { ... on I { a ... on J { b } } } } }', output: '{ i { ... on T2 { a } } }' }, - ])('removes unsatisfiable branches', ({input, output}) => { + { + input: '{ i { ... on T1 { ... on I { a b } } } }', + output: '{ i { ... on T1 { a b } } }', + }, + { + input: '{ i { ... on I { a ... on T2 { d } } } }', + output: '{ i { a ... on T2 { d } } }', + }, + { + input: '{ i { ... on T2 { ... on I { a ... on J { b } } } } }', + output: '{ i { ... on T2 { a } } }', + }, + ])('removes unsatisfiable branches', ({ input, output }) => { expect(normalized(input)).toBe(output); }); }); describe('named fragment selection set restrictions at type', () => { - const expandAtType = (frag: NamedFragmentDefinition, schema: Schema, typeName: string): FragmentRestrictionAtType => { + const expandAtType = ( + frag: NamedFragmentDefinition, + schema: Schema, + typeName: string, + ): FragmentRestrictionAtType => { const type = schema.type(typeName); - assert(type && isCompositeType(type), `Invalid type ${typeName}`) + assert(type && isCompositeType(type), `Invalid type ${typeName}`); // `expandedSelectionSetAtType` assumes it's argument passes `canApplyAtType`, so let's make sure we're // not typo-ing something in our tests. - assert(frag.canApplyDirectlyAtType(type), `${frag.name} cannot be applied at type ${typeName}`); + assert( + frag.canApplyDirectlyAtType(type), + `${frag.name} cannot be applied at type ${typeName}`, + ); return frag.expandedSelectionSetAtType(type); - } + }; test('for fragment on object types', () => { const schema = parseSchema(` @@ -3172,7 +3336,9 @@ describe('named fragment selection set restrictions at type', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t1 { ...FonT1 @@ -3183,7 +3349,8 @@ describe('named fragment selection set restrictions at type', () => { x y } - `); + `, + ); const frag = operation.fragments?.get('FonT1')!; @@ -3223,7 +3390,9 @@ describe('named fragment selection set restrictions at type', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t1 { ...FonI1 @@ -3245,12 +3414,15 @@ describe('named fragment selection set restrictions at type', () => { x } } - `); + `, + ); const frag = operation.fragments?.get('FonI1')!; let { selectionSet, validator } = expandAtType(frag, schema, 'I1'); - expect(selectionSet.toString()).toBe('{ x ... on T1 { x } ... on T2 { x } ... on I2 { x } ... on I3 { x } }'); + expect(selectionSet.toString()).toBe( + '{ x ... on T1 { x } ... on T2 { x } ... on I2 { x } ... on I3 { x } }', + ); // Note: Due to `FieldsInSetCanMerge` rule, we can't use trimmed validators for // fragments on non-object types. expect(validator?.toString()).toMatchString(` @@ -3308,7 +3480,9 @@ describe('named fragment selection set restrictions at type', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` { t1 { ...FonU1 @@ -3333,7 +3507,8 @@ describe('named fragment selection set restrictions at type', () => { } } } - `); + `, + ); const frag = operation.fragments?.get('FonU1')!; @@ -3341,7 +3516,9 @@ describe('named fragment selection set restrictions at type', () => { // possible runtimes. let { selectionSet, validator } = expandAtType(frag, schema, 'U1'); - expect(selectionSet.toString()).toBe('{ ... on T1 { x y } ... on T2 { z w } }'); + expect(selectionSet.toString()).toBe( + '{ ... on T1 { x y } ... on T2 { z w } }', + ); // Similar remarks than on interfaces (the validator is strictly speaking not necessary, but // this happens due to the "lifting" of selection mentioned above, is a bit hard to avoid, // and is essentially harmess (it may result in a bit more cpu cycles in some cases but @@ -3451,7 +3628,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t { ...FragOnT @@ -3472,7 +3651,8 @@ describe('named fragment rebasing on subgraphs', () => { v5 } } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); @@ -3523,7 +3703,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t { ...FragOnT @@ -3542,7 +3724,8 @@ describe('named fragment rebasing on subgraphs', () => { x y } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); @@ -3591,7 +3774,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { i { ...FragOnI @@ -3608,7 +3793,8 @@ describe('named fragment rebasing on subgraphs', () => { y } } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); @@ -3651,7 +3837,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { i { ...FragOnI @@ -3663,12 +3851,14 @@ describe('named fragment rebasing on subgraphs', () => { id x } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); - const subgraph = buildSchema(` + const subgraph = buildSchema( + ` extend schema @link( url: "https://specs.apollo.dev/federation/v2.5", @@ -3710,7 +3900,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { t { ...F1 @@ -3737,7 +3929,8 @@ describe('named fragment rebasing on subgraphs', () => { c d } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); @@ -3781,7 +3974,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { ...TheQuery } @@ -3799,7 +3994,8 @@ describe('named fragment rebasing on subgraphs', () => { z } } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); @@ -3842,7 +4038,9 @@ describe('named fragment rebasing on subgraphs', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { ...TQuery } @@ -3856,7 +4054,8 @@ describe('named fragment rebasing on subgraphs', () => { } } } - `); + `, + ); const fragments = operation.fragments; assert(fragments, 'Should have some fragments'); diff --git a/internals-js/src/__tests__/removeInaccessibleElements.test.ts b/internals-js/src/__tests__/removeInaccessibleElements.test.ts index 513bf71b0..9afa860bd 100644 --- a/internals-js/src/__tests__/removeInaccessibleElements.test.ts +++ b/internals-js/src/__tests__/removeInaccessibleElements.test.ts @@ -4,13 +4,13 @@ import { InterfaceType, ObjectType, UnionType, -} from "../definitions"; -import { buildSchema } from "../buildSchema"; -import { removeInaccessibleElements } from "../specs/inaccessibleSpec"; -import { GraphQLError } from "graphql"; -import { errorCauses } from "../error"; +} from '../definitions'; +import { buildSchema } from '../buildSchema'; +import { removeInaccessibleElements } from '../specs/inaccessibleSpec'; +import { GraphQLError } from 'graphql'; +import { errorCauses } from '../error'; -describe("removeInaccessibleElements", () => { +describe('removeInaccessibleElements', () => { const INACCESSIBLE_V02_HEADER = ` directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA @@ -52,7 +52,7 @@ describe("removeInaccessibleElements", () => { expect(causes).toHaveLength(expectedCauseCount); const messages = causes.map((cause) => cause.message); for (const message of messages) { - expect(typeof message === "string").toBeTruthy(); + expect(typeof message === 'string').toBeTruthy(); } messages.sort(); return messages; @@ -106,7 +106,7 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Query.someField')).toBeDefined(); }); it(`fails for no @inaccessible definition`, () => { @@ -206,8 +206,8 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query.someField")).toBeDefined(); - expect(schema.elementByCoordinate("Query.privateField")).toBeUndefined(); + expect(schema.elementByCoordinate('Query.someField')).toBeDefined(); + expect(schema.elementByCoordinate('Query.privateField')).toBeUndefined(); }); it(`handles renames of @inaccessible via import "as"`, () => { @@ -242,8 +242,8 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query.someField")).toBeDefined(); - expect(schema.elementByCoordinate("Query.privateField")).toBeUndefined(); + expect(schema.elementByCoordinate('Query.someField')).toBeDefined(); + expect(schema.elementByCoordinate('Query.privateField')).toBeUndefined(); }); it(`fails for @inaccessible built-ins`, () => { @@ -473,25 +473,25 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query")).toBeDefined(); - expect(schema.elementByCoordinate("Mutation")).toBeUndefined(); - expect(schema.elementByCoordinate("Subscription")).toBeUndefined(); - expect(schema.elementByCoordinate("Object")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Query')).toBeDefined(); + expect(schema.elementByCoordinate('Mutation')).toBeUndefined(); + expect(schema.elementByCoordinate('Subscription')).toBeUndefined(); + expect(schema.elementByCoordinate('Object')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer3.privatefield") + schema.elementByCoordinate('Referencer3.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4")).toBeUndefined(); - const unionType = schema.elementByCoordinate("Referencer5"); + expect(schema.elementByCoordinate('Referencer4')).toBeUndefined(); + const unionType = schema.elementByCoordinate('Referencer5'); expect(unionType instanceof UnionType).toBeTruthy(); - expect((unionType as UnionType).hasTypeMember("Query")).toBeTruthy(); - expect((unionType as UnionType).hasTypeMember("Object")).toBeFalsy(); - expect(schema.elementByCoordinate("Referencer6")).toBeUndefined(); + expect((unionType as UnionType).hasTypeMember('Query')).toBeTruthy(); + expect((unionType as UnionType).hasTypeMember('Object')).toBeFalsy(); + expect(schema.elementByCoordinate('Referencer6')).toBeUndefined(); }); it(`fails to remove @inaccessible object types for breaking removals`, () => { @@ -594,33 +594,33 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("VisibleInterface")).toBeDefined(); - expect(schema.elementByCoordinate("Interface")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('VisibleInterface')).toBeDefined(); + expect(schema.elementByCoordinate('Interface')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer3.privatefield") + schema.elementByCoordinate('Referencer3.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4")).toBeUndefined(); - const objectType = schema.elementByCoordinate("Referencer5"); + expect(schema.elementByCoordinate('Referencer4')).toBeUndefined(); + const objectType = schema.elementByCoordinate('Referencer5'); expect(objectType instanceof ObjectType).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("VisibleInterface") + (objectType as ObjectType).implementsInterface('VisibleInterface'), ).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Interface") + (objectType as ObjectType).implementsInterface('Interface'), ).toBeFalsy(); - const interfaceType = schema.elementByCoordinate("Referencer6"); + const interfaceType = schema.elementByCoordinate('Referencer6'); expect(interfaceType instanceof InterfaceType).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("VisibleInterface") + (interfaceType as InterfaceType).implementsInterface('VisibleInterface'), ).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Interface") + (interfaceType as InterfaceType).implementsInterface('Interface'), ).toBeFalsy(); }); @@ -703,18 +703,18 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("VisibleUnion")).toBeDefined(); - expect(schema.elementByCoordinate("Union")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('VisibleUnion')).toBeDefined(); + expect(schema.elementByCoordinate('Union')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer3.privatefield") + schema.elementByCoordinate('Referencer3.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer4')).toBeUndefined(); }); it(`fails to remove @inaccessible union types for breaking removals`, () => { @@ -829,34 +829,34 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("VisibleInputObject")).toBeDefined(); - expect(schema.elementByCoordinate("InputObject")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('VisibleInputObject')).toBeDefined(); + expect(schema.elementByCoordinate('InputObject')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.someField(privateArg:)") + schema.elementByCoordinate('Referencer1.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer2.privateField") + schema.elementByCoordinate('Referencer2.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer4.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer4.someField(privateArg:)") + schema.elementByCoordinate('Referencer4.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer5.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer5.privateField") + schema.elementByCoordinate('Referencer5.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer6")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer6')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer7.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer7.privatefield") + schema.elementByCoordinate('Referencer7.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer8")).toBeUndefined(); - expect(schema.elementByCoordinate("@referencer9")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer8')).toBeUndefined(); + expect(schema.elementByCoordinate('@referencer9')).toBeDefined(); expect( - schema.elementByCoordinate("@referencer9(privateArg:)") + schema.elementByCoordinate('@referencer9(privateArg:)'), ).toBeUndefined(); }); @@ -1007,44 +1007,44 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("VisibleEnum")).toBeDefined(); - expect(schema.elementByCoordinate("Enum")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('VisibleEnum')).toBeDefined(); + expect(schema.elementByCoordinate('Enum')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer3.privatefield") + schema.elementByCoordinate('Referencer3.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer4')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer5.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer5.someField(privateArg:)") + schema.elementByCoordinate('Referencer5.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer6.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer6.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer6.privateField") + schema.elementByCoordinate('Referencer6.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer7")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer8.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer7')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer8.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer8.someField(privateArg:)") + schema.elementByCoordinate('Referencer8.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer9.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer9.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer9.privateField") + schema.elementByCoordinate('Referencer9.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer10")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer11.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer10')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer11.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer11.privatefield") + schema.elementByCoordinate('Referencer11.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer12")).toBeUndefined(); - expect(schema.elementByCoordinate("@referencer13")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer12')).toBeUndefined(); + expect(schema.elementByCoordinate('@referencer13')).toBeDefined(); expect( - schema.elementByCoordinate("@referencer13(privateArg:)") + schema.elementByCoordinate('@referencer13(privateArg:)'), ).toBeUndefined(); }); @@ -1206,44 +1206,44 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("VisibleScalar")).toBeDefined(); - expect(schema.elementByCoordinate("Scalar")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('VisibleScalar')).toBeDefined(); + expect(schema.elementByCoordinate('Scalar')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer3.privatefield") + schema.elementByCoordinate('Referencer3.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer4')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer5.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer5.someField(privateArg:)") + schema.elementByCoordinate('Referencer5.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer6.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer6.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer6.privateField") + schema.elementByCoordinate('Referencer6.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer7")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer8.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer7')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer8.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer8.someField(privateArg:)") + schema.elementByCoordinate('Referencer8.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer9.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer9.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer9.privateField") + schema.elementByCoordinate('Referencer9.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer10")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer11.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer10')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer11.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer11.privatefield") + schema.elementByCoordinate('Referencer11.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer12")).toBeUndefined(); - expect(schema.elementByCoordinate("@referencer13")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer12')).toBeUndefined(); + expect(schema.elementByCoordinate('@referencer13')).toBeDefined(); expect( - schema.elementByCoordinate("@referencer13(privateArg:)") + schema.elementByCoordinate('@referencer13(privateArg:)'), ).toBeUndefined(); }); @@ -1364,30 +1364,30 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query.someField")).toBeDefined(); - expect(schema.elementByCoordinate("Query.privateField")).toBeUndefined(); - expect(schema.elementByCoordinate("Mutation.someField")).toBeDefined(); - expect(schema.elementByCoordinate("Mutation.privateField")).toBeUndefined(); - expect(schema.elementByCoordinate("Subscription.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Query.someField')).toBeDefined(); + expect(schema.elementByCoordinate('Query.privateField')).toBeUndefined(); + expect(schema.elementByCoordinate('Mutation.someField')).toBeDefined(); + expect(schema.elementByCoordinate('Mutation.privateField')).toBeUndefined(); + expect(schema.elementByCoordinate('Subscription.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Subscription.privateField") + schema.elementByCoordinate('Subscription.privateField'), ).toBeUndefined(); - const objectType = schema.elementByCoordinate("Object"); + const objectType = schema.elementByCoordinate('Object'); expect(objectType instanceof ObjectType).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Referencer1") + (objectType as ObjectType).implementsInterface('Referencer1'), ).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Referencer2") + (objectType as ObjectType).implementsInterface('Referencer2'), ).toBeFalsy(); - expect(schema.elementByCoordinate("Object.someField")).toBeDefined(); - expect(schema.elementByCoordinate("Object.privateField")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Object.someField')).toBeDefined(); + expect(schema.elementByCoordinate('Object.privateField')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); }); it(`fails to remove @inaccessible object fields for breaking removals`, () => { @@ -1491,24 +1491,24 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - const interfaceType = schema.elementByCoordinate("Interface"); + const interfaceType = schema.elementByCoordinate('Interface'); expect(interfaceType instanceof InterfaceType).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Referencer1") + (interfaceType as InterfaceType).implementsInterface('Referencer1'), ).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Referencer2") + (interfaceType as InterfaceType).implementsInterface('Referencer2'), ).toBeFalsy(); - expect(schema.elementByCoordinate("Interface.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Interface.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Interface.privateField") + schema.elementByCoordinate('Interface.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.privatefield") + schema.elementByCoordinate('Referencer1.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); }); it(`fails to remove @inaccessible interface fields for breaking removals`, () => { @@ -1615,45 +1615,45 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Query.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Query.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Query.someField(privateArg:)") + schema.elementByCoordinate('Query.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Mutation.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Mutation.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Mutation.someField(privateArg:)") + schema.elementByCoordinate('Mutation.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Subscription.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Subscription.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Subscription.someField(privateArg:)") + schema.elementByCoordinate('Subscription.someField(privateArg:)'), ).toBeUndefined(); - const objectType = schema.elementByCoordinate("Object"); + const objectType = schema.elementByCoordinate('Object'); expect(objectType instanceof ObjectType).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Referencer1") + (objectType as ObjectType).implementsInterface('Referencer1'), ).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Referencer2") + (objectType as ObjectType).implementsInterface('Referencer2'), ).toBeTruthy(); expect( - (objectType as ObjectType).implementsInterface("Referencer3") + (objectType as ObjectType).implementsInterface('Referencer3'), ).toBeFalsy(); expect( - schema.elementByCoordinate("Object.someField(someArg:)") + schema.elementByCoordinate('Object.someField(someArg:)'), ).toBeDefined(); expect( - schema.elementByCoordinate("Object.someField(privateArg:)") + schema.elementByCoordinate('Object.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.someField(privateArg:)") + schema.elementByCoordinate('Referencer1.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeDefined(); - expect(schema.elementByCoordinate("Referencer2.someField")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); - expect(schema.elementByCoordinate("ObjectDefault.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2.someField')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); + expect(schema.elementByCoordinate('ObjectDefault.someField')).toBeDefined(); expect( - schema.elementByCoordinate("ObjectDefault.someField(privateArg:)") + schema.elementByCoordinate('ObjectDefault.someField(privateArg:)'), ).toBeUndefined(); }); @@ -1771,54 +1771,54 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - const interfaceType = schema.elementByCoordinate("Interface"); + const interfaceType = schema.elementByCoordinate('Interface'); expect(interfaceType instanceof InterfaceType).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Referencer1") + (interfaceType as InterfaceType).implementsInterface('Referencer1'), ).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Referencer2") + (interfaceType as InterfaceType).implementsInterface('Referencer2'), ).toBeTruthy(); expect( - (interfaceType as InterfaceType).implementsInterface("Referencer3") + (interfaceType as InterfaceType).implementsInterface('Referencer3'), ).toBeFalsy(); expect( - schema.elementByCoordinate("Interface.someField(someArg:)") + schema.elementByCoordinate('Interface.someField(someArg:)'), ).toBeDefined(); expect( - schema.elementByCoordinate("Interface.someField(privateArg:)") + schema.elementByCoordinate('Interface.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.someField(privateArg:)") + schema.elementByCoordinate('Referencer1.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2")).toBeDefined(); - expect(schema.elementByCoordinate("Referencer2.someField")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); - expect(schema.elementByCoordinate("Interface.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2')).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2.someField')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); + expect(schema.elementByCoordinate('Interface.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Interface.someField(privateArg:)") + schema.elementByCoordinate('Interface.someField(privateArg:)'), ).toBeUndefined(); const objectArg = schema.elementByCoordinate( - "Referencer4.someField(privateArg:)" + 'Referencer4.someField(privateArg:)', ); expect(objectArg instanceof ArgumentDefinition).toBeTruthy(); expect( ( objectArg as ArgumentDefinition> - ).isRequired() + ).isRequired(), ).toBeFalsy(); - expect(schema.elementByCoordinate("Referencer5")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer5')).toBeUndefined(); const interfaceArg = schema.elementByCoordinate( - "Referencer6.someField(privateArg:)" + 'Referencer6.someField(privateArg:)', ); expect(interfaceArg instanceof ArgumentDefinition).toBeTruthy(); expect( ( interfaceArg as ArgumentDefinition> - ).isRequired() + ).isRequired(), ).toBeFalsy(); - expect(schema.elementByCoordinate("Referencer7")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer7')).toBeUndefined(); }); it(`fails to remove @inaccessible interface field arguments for breaking removals`, () => { @@ -1973,46 +1973,46 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("InputObject.someField")).toBeDefined(); + expect(schema.elementByCoordinate('InputObject.someField')).toBeDefined(); expect( - schema.elementByCoordinate("InputObject.privateField") + schema.elementByCoordinate('InputObject.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.someField(privateArg:)") + schema.elementByCoordinate('Referencer1.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer2.privateField") + schema.elementByCoordinate('Referencer2.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer4.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer4.someField(privateArg:)") + schema.elementByCoordinate('Referencer4.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer5.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer5.privateField") + schema.elementByCoordinate('Referencer5.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer6")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer6')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer7.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer7.privatefield") + schema.elementByCoordinate('Referencer7.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer8")).toBeUndefined(); - expect(schema.elementByCoordinate("@referencer9")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer8')).toBeUndefined(); + expect(schema.elementByCoordinate('@referencer9')).toBeDefined(); expect( - schema.elementByCoordinate("@referencer9(privateArg:)") + schema.elementByCoordinate('@referencer9(privateArg:)'), ).toBeUndefined(); expect( - schema.elementByCoordinate("Referencer10.someField(privateArg:)") + schema.elementByCoordinate('Referencer10.someField(privateArg:)'), ).toBeDefined(); - expect(schema.elementByCoordinate("Referencer11")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer11')).toBeUndefined(); expect( - schema.elementByCoordinate("InputObjectDefault.someField") + schema.elementByCoordinate('InputObjectDefault.someField'), ).toBeDefined(); expect( - schema.elementByCoordinate("InputObjectDefault.privatefield") + schema.elementByCoordinate('InputObjectDefault.privatefield'), ).toBeUndefined(); }); @@ -2179,39 +2179,39 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("Enum.SOME_VALUE")).toBeDefined(); - expect(schema.elementByCoordinate("Enum.PRIVATE_VALUE")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer1.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Enum.SOME_VALUE')).toBeDefined(); + expect(schema.elementByCoordinate('Enum.PRIVATE_VALUE')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer1.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer1.someField(privateArg:)") + schema.elementByCoordinate('Referencer1.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer2.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer2.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer2.privateField") + schema.elementByCoordinate('Referencer2.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer3")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer4.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer3')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer4.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer4.someField(privateArg:)") + schema.elementByCoordinate('Referencer4.someField(privateArg:)'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer5.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer5.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer5.privateField") + schema.elementByCoordinate('Referencer5.privateField'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer6")).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer7.someField")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer6')).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer7.someField')).toBeDefined(); expect( - schema.elementByCoordinate("Referencer7.privatefield") + schema.elementByCoordinate('Referencer7.privatefield'), ).toBeUndefined(); - expect(schema.elementByCoordinate("Referencer8")).toBeUndefined(); - expect(schema.elementByCoordinate("@referencer9")).toBeDefined(); + expect(schema.elementByCoordinate('Referencer8')).toBeUndefined(); + expect(schema.elementByCoordinate('@referencer9')).toBeDefined(); expect( - schema.elementByCoordinate("@referencer9(privateArg:)") + schema.elementByCoordinate('@referencer9(privateArg:)'), ).toBeUndefined(); expect( - schema.elementByCoordinate("Referencer10.someField(privateArg:)") + schema.elementByCoordinate('Referencer10.someField(privateArg:)'), ).toBeDefined(); - expect(schema.elementByCoordinate("Referencer11")).toBeUndefined(); + expect(schema.elementByCoordinate('Referencer11')).toBeUndefined(); }); it(`fails to remove @inaccessible enum values for breaking removals`, () => { @@ -2296,15 +2296,15 @@ describe("removeInaccessibleElements", () => { removeInaccessibleElements(schema); schema.validate(); - expect(schema.elementByCoordinate("@directive(someArg:)")).toBeDefined(); + expect(schema.elementByCoordinate('@directive(someArg:)')).toBeDefined(); expect( - schema.elementByCoordinate("@directive(privateArg:)") + schema.elementByCoordinate('@directive(privateArg:)'), ).toBeUndefined(); expect( - schema.elementByCoordinate("@directiveDefault(someArg:)") + schema.elementByCoordinate('@directiveDefault(someArg:)'), ).toBeDefined(); expect( - schema.elementByCoordinate("@directiveDefault(privateArg:)") + schema.elementByCoordinate('@directiveDefault(privateArg:)'), ).toBeUndefined(); }); diff --git a/internals-js/src/__tests__/schemaUpgrader.test.ts b/internals-js/src/__tests__/schemaUpgrader.test.ts index 4ac656950..008e09a76 100644 --- a/internals-js/src/__tests__/schemaUpgrader.test.ts +++ b/internals-js/src/__tests__/schemaUpgrader.test.ts @@ -1,11 +1,22 @@ -import { FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS_UPGRADED, printSchema } from '..'; +import { + FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS_UPGRADED, + printSchema, +} from '..'; import { ObjectType } from '../definitions'; import { buildSubgraph, Subgraphs } from '../federation'; -import { UpgradeChangeID, UpgradeResult, upgradeSubgraphsIfNecessary } from '../schemaUpgrader'; - -function changeMessages(res: UpgradeResult, subgraphName: string, id: UpgradeChangeID): string[] { +import { + UpgradeChangeID, + UpgradeResult, + upgradeSubgraphsIfNecessary, +} from '../schemaUpgrader'; + +function changeMessages( + res: UpgradeResult, + subgraphName: string, + id: UpgradeChangeID, +): string[] { const changes = res.changes?.get(subgraphName)?.get(id); - return changes?.map(c => c.toString()) ?? []; + return changes?.map((c) => c.toString()) ?? []; } /** @@ -57,36 +68,46 @@ test('upgrade complex schema', () => { const res = upgradeSubgraphsIfNecessary(subgraphs); expect(res.errors).toBeUndefined(); - expect(changeMessages(res, 's1', 'EXTERNAL_ON_TYPE_EXTENSION_REMOVAL')).toStrictEqual([ - 'Removed @external from field "Product.upc" as it is a key of an extension type' + expect( + changeMessages(res, 's1', 'EXTERNAL_ON_TYPE_EXTENSION_REMOVAL'), + ).toStrictEqual([ + 'Removed @external from field "Product.upc" as it is a key of an extension type', ]); expect(changeMessages(res, 's1', 'TYPE_EXTENSION_REMOVAL')).toStrictEqual([ - 'Switched type "Product" from an extension to a definition' + 'Switched type "Product" from an extension to a definition', ]); expect(changeMessages(res, 's1', 'UNUSED_EXTERNAL_REMOVAL')).toStrictEqual([ - 'Removed @external field "Product.name" as it was not used in any @key, @provides or @requires' + 'Removed @external field "Product.name" as it was not used in any @key, @provides or @requires', ]); - expect(changeMessages(res, 's1', 'EXTERNAL_ON_INTERFACE_REMOVAL')).toStrictEqual([ - 'Removed @external directive on interface type field "I.description": @external is nonsensical on interface fields' + expect( + changeMessages(res, 's1', 'EXTERNAL_ON_INTERFACE_REMOVAL'), + ).toStrictEqual([ + 'Removed @external directive on interface type field "I.description": @external is nonsensical on interface fields', ]); - expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_REMOVAL')).toStrictEqual([ - 'Removed directive @requires(fields: "upc") on "Product.inventory": none of the fields were truly @external' + expect( + changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_REMOVAL'), + ).toStrictEqual([ + 'Removed directive @requires(fields: "upc") on "Product.inventory": none of the fields were truly @external', ]); - expect(changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_FIELDS_REMOVAL')).toStrictEqual([ - 'Updated directive @provides(fields: "upc description") on "Query.products" to @provides(fields: "description"): removed fields that were not truly @external' + expect( + changeMessages(res, 's1', 'INACTIVE_PROVIDES_OR_REQUIRES_FIELDS_REMOVAL'), + ).toStrictEqual([ + 'Updated directive @provides(fields: "upc description") on "Query.products" to @provides(fields: "description"): removed fields that were not truly @external', ]); expect(changeMessages(res, 's1', 'KEY_ON_INTERFACE_REMOVAL')).toStrictEqual([ - 'Removed @key on interface "I": while allowed by federation 0.x, @key on interfaces were completely ignored/had no effect' + 'Removed @key on interface "I": while allowed by federation 0.x, @key on interfaces were completely ignored/had no effect', ]); - expect(changeMessages(res, 's1', 'PROVIDES_ON_NON_COMPOSITE_REMOVAL')).toStrictEqual([ - 'Removed @provides directive on field "Random.x" as it is of non-composite type "Int": while not rejected by federation 0.x, such @provide is nonsensical and was ignored' + expect( + changeMessages(res, 's1', 'PROVIDES_ON_NON_COMPOSITE_REMOVAL'), + ).toStrictEqual([ + 'Removed @provides directive on field "Random.x" as it is of non-composite type "Int": while not rejected by federation 0.x, such @provide is nonsensical and was ignored', ]); expect(res.subgraphs?.get('s1')?.toString()).toMatchString(` @@ -140,7 +161,9 @@ test('update federation directive non-string arguments', () => { const res = upgradeSubgraphsIfNecessary(subgraphs); expect(res.errors).toBeUndefined(); - expect(changeMessages(res, 's', 'FIELDS_ARGUMENT_COERCION_TO_STRING')).toStrictEqual([ + expect( + changeMessages(res, 's', 'FIELDS_ARGUMENT_COERCION_TO_STRING'), + ).toStrictEqual([ 'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: id) to @key(fields: "id")', 'Coerced "fields" argument for directive @key for "A" into a string: coerced from @key(fields: ["id", "x"]) to @key(fields: "id x")', ]); @@ -164,7 +187,7 @@ test('update federation directive non-string arguments', () => { x: Int } `); -}) +}); test('remove tag on external field if found on definition', () => { const s1 = ` @@ -196,11 +219,21 @@ test('remove tag on external field if found on definition', () => { 'Removed @tag(name: "a tag") application on @external "A.y" as the @tag application is on another definition', ]); - const typeAInS1 = res.subgraphs?.get('s1')?.schema.type("A") as ObjectType; - const typeAInS2 = res.subgraphs?.get('s2')?.schema.type("A") as ObjectType; - expect(typeAInS1.field("y")?.appliedDirectivesOf('tag').map((d) => d.toString())).toStrictEqual([]); - expect(typeAInS2.field("y")?.appliedDirectivesOf('tag').map((d) => d.toString())).toStrictEqual([ '@tag(name: "a tag")' ]); -}) + const typeAInS1 = res.subgraphs?.get('s1')?.schema.type('A') as ObjectType; + const typeAInS2 = res.subgraphs?.get('s2')?.schema.type('A') as ObjectType; + expect( + typeAInS1 + .field('y') + ?.appliedDirectivesOf('tag') + .map((d) => d.toString()), + ).toStrictEqual([]); + expect( + typeAInS2 + .field('y') + ?.appliedDirectivesOf('tag') + .map((d) => d.toString()), + ).toStrictEqual(['@tag(name: "a tag")']); +}); test('reject @interfaceObject usage if not all subgraphs are fed2', () => { // Note that this test both validates the rejection of fed1 subgraph when @interfaceObject is used somewhere, but also @@ -240,10 +273,10 @@ test('reject @interfaceObject usage if not all subgraphs are fed2', () => { subgraphs.add(buildSubgraph('s2', 'http://s2', s2)); const res = upgradeSubgraphsIfNecessary(subgraphs); expect(res.errors?.map((e) => e.message)).toStrictEqual([ - 'The @interfaceObject directive can only be used if all subgraphs have federation 2 subgraph schema (schema with a `@link` to "https://specs.apollo.dev/federation" version 2.0 or newer): ' - + '@interfaceObject is used in subgraph "s1" but subgraph "s2" is not a federation 2 subgraph schema.' + 'The @interfaceObject directive can only be used if all subgraphs have federation 2 subgraph schema (schema with a `@link` to "https://specs.apollo.dev/federation" version 2.0 or newer): ' + + '@interfaceObject is used in subgraph "s1" but subgraph "s2" is not a federation 2 subgraph schema.', ]); -}) +}); test('handles the addition of @shareable when an @external is used on a type', () => { const s1 = ` @@ -278,22 +311,27 @@ test('handles the addition of @shareable when an @external is used on a type', ( // 2. field `T.x` in s1 must be marked @shareable since it is resolved by s2 (since again, it's @external annotation is ignored). const s2Upgraded = res.subgraphs?.get('s2')!; - expect(s2Upgraded.schema.type('T')?.hasAppliedDirective('external')).toBe(false); + expect(s2Upgraded.schema.type('T')?.hasAppliedDirective('external')).toBe( + false, + ); const s1Upgraded = res.subgraphs?.get('s1')!; - expect((s1Upgraded.schema.type('T') as ObjectType).field('x')?.hasAppliedDirective('shareable')).toBe(true); - -}) + expect( + (s1Upgraded.schema.type('T') as ObjectType) + .field('x') + ?.hasAppliedDirective('shareable'), + ).toBe(true); +}); -test("fully upgrades a schema with no @link directive", () => { +test('fully upgrades a schema with no @link directive', () => { const subgraph = buildSubgraph( - "subgraph", - "", + 'subgraph', + '', `#graphql type Query { hello: String } - ` + `, ); const subgraphs = new Subgraphs(); @@ -316,12 +354,12 @@ test("fully upgrades a schema with no @link directive", () => { // router that supports the build pipeline they're upgrading to, but that // mechanism isn't in place yet. // - Trevor - expect(printSchema(result.subgraphs!.get("subgraph")!.schema!)).toContain( -`schema + expect(printSchema(result.subgraphs!.get('subgraph')!.schema!)).toContain( + `schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) { query: Query -}` +}`, ); }); diff --git a/internals-js/src/__tests__/subgraphValidation.test.ts b/internals-js/src/__tests__/subgraphValidation.test.ts index 9232ff84a..ab0292e12 100644 --- a/internals-js/src/__tests__/subgraphValidation.test.ts +++ b/internals-js/src/__tests__/subgraphValidation.test.ts @@ -1,13 +1,13 @@ import { DocumentNode } from 'graphql'; import gql from 'graphql-tag'; import { Subgraph } from '..'; -import { buildSubgraph } from "../federation" +import { buildSubgraph } from '../federation'; import { defaultPrintOptions, printSchema } from '../print'; import { buildForErrors } from './testUtils'; describe('fieldset-based directives', () => { it('rejects field defined with arguments in @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -15,14 +15,17 @@ describe('fieldset-based directives', () => { type T @key(fields: "f") { f(x: Int): Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_FIELDS_HAS_ARGS', '[S] On type "T", for @key(fields: "f"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @key)'] + [ + 'KEY_FIELDS_HAS_ARGS', + '[S] On type "T", for @key(fields: "f"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @key)', + ], ]); }); it('rejects field defined with arguments in @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "f") } @@ -30,14 +33,17 @@ describe('fieldset-based directives', () => { type T { f(x: Int): Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_FIELDS_HAS_ARGS', '[S] On field "Query.t", for @provides(fields: "f"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @provides)'] + [ + 'PROVIDES_FIELDS_HAS_ARGS', + '[S] On field "Query.t", for @provides(fields: "f"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @provides)', + ], ]); }); it('rejects @provides on non-external fields', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "f") } @@ -45,14 +51,17 @@ describe('fieldset-based directives', () => { type T { f: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_FIELDS_MISSING_EXTERNAL', '[S] On field "Query.t", for @provides(fields: "f"): field "T.f" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)'] + [ + 'PROVIDES_FIELDS_MISSING_EXTERNAL', + '[S] On field "Query.t", for @provides(fields: "f"): field "T.f" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)', + ], ]); }); it('rejects @requires on non-external fields', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -61,14 +70,19 @@ describe('fieldset-based directives', () => { f: Int g: Int @requires(fields: "f") } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_FIELDS_MISSING_EXTERNAL', '[S] On field "T.g", for @requires(fields: "f"): field "T.f" should not be part of a @requires since it is already provided by this subgraph (it is not marked @external)'] + [ + 'REQUIRES_FIELDS_MISSING_EXTERNAL', + '[S] On field "T.g", for @requires(fields: "f"): field "T.f" should not be part of a @requires since it is already provided by this subgraph (it is not marked @external)', + ], ]); }); - it.each(['2.0', '2.1', '2.2'])('rejects @key on interfaces _in the %p spec_', (version) => { - const subgraph = gql` + it.each(['2.0', '2.1', '2.2'])( + 'rejects @key on interfaces _in the %p spec_', + (version) => { + const subgraph = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v${version}", import: ["@key"]) @@ -79,14 +93,18 @@ describe('fieldset-based directives', () => { interface T @key(fields: "f") { f: Int } - ` - expect(buildForErrors(subgraph, { asFed2: false })).toStrictEqual([ - ['KEY_UNSUPPORTED_ON_INTERFACE', '[S] Cannot use @key on interface "T": @key is not yet supported on interfaces'], - ]); - }); + `; + expect(buildForErrors(subgraph, { asFed2: false })).toStrictEqual([ + [ + 'KEY_UNSUPPORTED_ON_INTERFACE', + '[S] Cannot use @key on interface "T": @key is not yet supported on interfaces', + ], + ]); + }, + ); it('rejects @provides on interfaces', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -98,14 +116,17 @@ describe('fieldset-based directives', () => { type U { g: Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_UNSUPPORTED_ON_INTERFACE', '[S] Cannot use @provides on field "T.f" of parent type "T": @provides is not yet supported within interfaces'], + [ + 'PROVIDES_UNSUPPORTED_ON_INTERFACE', + '[S] Cannot use @provides on field "T.f" of parent type "T": @provides is not yet supported within interfaces', + ], ]); }); it('rejects @requires on interfaces', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -114,15 +135,21 @@ describe('fieldset-based directives', () => { f: Int @external g: Int @requires(fields: "f") } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_UNSUPPORTED_ON_INTERFACE', '[S] Cannot use @requires on field "T.g" of parent type "T": @requires is not yet supported within interfaces' ], - ['EXTERNAL_ON_INTERFACE', '[S] Interface type field "T.f" is marked @external but @external is not allowed on interface fields (it is nonsensical).' ], + [ + 'REQUIRES_UNSUPPORTED_ON_INTERFACE', + '[S] Cannot use @requires on field "T.g" of parent type "T": @requires is not yet supported within interfaces', + ], + [ + 'EXTERNAL_ON_INTERFACE', + '[S] Interface type field "T.f" is marked @external but @external is not allowed on interface fields (it is nonsensical).', + ], ]); }); it('rejects unused @external', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -130,14 +157,17 @@ describe('fieldset-based directives', () => { type T { f: Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).'], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); it('rejects @provides on non-object fields', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: Int @provides(fields: "f") } @@ -145,14 +175,17 @@ describe('fieldset-based directives', () => { type T { f: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_ON_NON_OBJECT_FIELD', '[S] Invalid @provides directive on field "Query.t": field has type "Int" which is not a Composite Type'], + [ + 'PROVIDES_ON_NON_OBJECT_FIELD', + '[S] Invalid @provides directive on field "Query.t": field has type "Int" which is not a Composite Type', + ], ]); }); it('rejects a non-string argument to @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -160,14 +193,17 @@ describe('fieldset-based directives', () => { type T @key(fields: ["f"]) { f: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_INVALID_FIELDS_TYPE', '[S] On type "T", for @key(fields: ["f"]): Invalid value for argument "fields": must be a string.'], + [ + 'KEY_INVALID_FIELDS_TYPE', + '[S] On type "T", for @key(fields: ["f"]): Invalid value for argument "fields": must be a string.', + ], ]); }); it('rejects a non-string argument to @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: ["f"]) } @@ -175,18 +211,24 @@ describe('fieldset-based directives', () => { type T { f: Int @external } - ` + `; // Note: since the error here is that we cannot parse the key `fields`, this also mean that @external on // `f` will appear unused and we get an error for it. It's kind of hard to avoid cleanly and hopefully // not a big deal (having errors dependencies is not exactly unheard of). expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_INVALID_FIELDS_TYPE', '[S] On field "Query.t", for @provides(fields: ["f"]): Invalid value for argument "fields": must be a string.'], - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ], + [ + 'PROVIDES_INVALID_FIELDS_TYPE', + '[S] On field "Query.t", for @provides(fields: ["f"]): Invalid value for argument "fields": must be a string.', + ], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); it('rejects a non-string argument to @requires', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -195,20 +237,26 @@ describe('fieldset-based directives', () => { f: Int @external g: Int @requires(fields: ["f"]) } - ` + `; // Note: since the error here is that we cannot parse the key `fields`, this also mean that @external on // `f` will appear unused and we get an error for it. It's kind of hard to avoid cleanly and hopefully // not a big deal (having errors dependencies is not exactly unheard of). expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_INVALID_FIELDS_TYPE', '[S] On field "T.g", for @requires(fields: ["f"]): Invalid value for argument "fields": must be a string.'], - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ], + [ + 'REQUIRES_INVALID_FIELDS_TYPE', + '[S] On field "T.g", for @requires(fields: ["f"]): Invalid value for argument "fields": must be a string.', + ], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); // Special case of non-string argument, specialized because it hits a different // code-path due to enum values being parsed as string and requiring special care. it('rejects an enum-like argument to @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -216,16 +264,19 @@ describe('fieldset-based directives', () => { type T @key(fields: f) { f: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_INVALID_FIELDS_TYPE', '[S] On type "T", for @key(fields: f): Invalid value for argument "fields": must be a string.'], + [ + 'KEY_INVALID_FIELDS_TYPE', + '[S] On type "T", for @key(fields: f): Invalid value for argument "fields": must be a string.', + ], ]); }); // Special case of non-string argument, specialized because it hits a different // code-path due to enum values being parsed as string and requiring special care. it('rejects an enum-lik argument to @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: f) } @@ -233,20 +284,26 @@ describe('fieldset-based directives', () => { type T { f: Int @external } - ` + `; // Note: since the error here is that we cannot parse the key `fields`, this also mean that @external on // `f` will appear unused and we get an error for it. It's kind of hard to avoid cleanly and hopefully // not a big deal (having errors dependencies is not exactly unheard of). expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_INVALID_FIELDS_TYPE', '[S] On field "Query.t", for @provides(fields: f): Invalid value for argument "fields": must be a string.'], - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ], + [ + 'PROVIDES_INVALID_FIELDS_TYPE', + '[S] On field "Query.t", for @provides(fields: f): Invalid value for argument "fields": must be a string.', + ], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); // Special case of non-string argument, specialized because it hits a different // code-path due to enum values being parsed as string and requiring special care. it('rejects an enum-like argument to @requires', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -255,18 +312,24 @@ describe('fieldset-based directives', () => { f: Int @external g: Int @requires(fields: f) } - ` + `; // Note: since the error here is that we cannot parse the key `fields`, this also mean that @external on // `f` will appear unused and we get an error for it. It's kind of hard to avoid cleanly and hopefully // not a big deal (having errors dependencies is not exactly unheard of). expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_INVALID_FIELDS_TYPE', '[S] On field "T.g", for @requires(fields: f): Invalid value for argument "fields": must be a string.'], - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ], + [ + 'REQUIRES_INVALID_FIELDS_TYPE', + '[S] On field "T.g", for @requires(fields: f): Invalid value for argument "fields": must be a string.', + ], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); it('rejects an invalid `fields` argument to @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -274,14 +337,17 @@ describe('fieldset-based directives', () => { type T @key(fields: ":f") { f: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_INVALID_FIELDS', '[S] On type "T", for @key(fields: ":f"): Syntax Error: Expected Name, found ":".'], + [ + 'KEY_INVALID_FIELDS', + '[S] On type "T", for @key(fields: ":f"): Syntax Error: Expected Name, found ":".', + ], ]); }); it('rejects an invalid `fields` argument to @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "{{f}}") } @@ -289,15 +355,21 @@ describe('fieldset-based directives', () => { type T { f: Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_INVALID_FIELDS', '[S] On field "Query.t", for @provides(fields: "{{f}}"): Syntax Error: Expected Name, found "{".'], - ['EXTERNAL_UNUSED', '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).' ], + [ + 'PROVIDES_INVALID_FIELDS', + '[S] On field "Query.t", for @provides(fields: "{{f}}"): Syntax Error: Expected Name, found "{".', + ], + [ + 'EXTERNAL_UNUSED', + '[S] Field "T.f" is marked @external but is not used in any federation directive (@key, @provides, @requires) or to satisfy an interface; the field declaration has no use and should be removed (or the field should not be @external).', + ], ]); }); it('rejects an invalid `fields` argument to @requires', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -306,14 +378,17 @@ describe('fieldset-based directives', () => { f: Int @external g: Int @requires(fields: "f b") } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_INVALID_FIELDS', '[S] On field "T.g", for @requires(fields: "f b"): Cannot query field "b" on type "T" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).'], + [ + 'REQUIRES_INVALID_FIELDS', + '[S] On field "T.g", for @requires(fields: "f b"): Cannot query field "b" on type "T" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).', + ], ]); }); it('rejects @key on an interface field', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -325,14 +400,17 @@ describe('fieldset-based directives', () => { interface I { i: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_FIELDS_SELECT_INVALID_TYPE', '[S] On type "T", for @key(fields: "f"): field "T.f" is a Interface type which is not allowed in @key'], + [ + 'KEY_FIELDS_SELECT_INVALID_TYPE', + '[S] On type "T", for @key(fields: "f"): field "T.f" is a Interface type which is not allowed in @key', + ], ]); }); it('rejects @key on an union field', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -342,14 +420,17 @@ describe('fieldset-based directives', () => { } union U = Query | T - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_FIELDS_SELECT_INVALID_TYPE', '[S] On type "T", for @key(fields: "f"): field "T.f" is a Union type which is not allowed in @key'], + [ + 'KEY_FIELDS_SELECT_INVALID_TYPE', + '[S] On type "T", for @key(fields: "f"): field "T.f" is a Union type which is not allowed in @key', + ], ]); }); it('rejects directive applications in @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -362,14 +443,17 @@ describe('fieldset-based directives', () => { x: Int y: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['KEY_DIRECTIVE_IN_FIELDS_ARG', '[S] On type "T", for @key(fields: "v { x ... @include(if: false) { y }}"): cannot have directive applications in the @key(fields:) argument but found @include(if: false).'], + [ + 'KEY_DIRECTIVE_IN_FIELDS_ARG', + '[S] On type "T", for @key(fields: "v { x ... @include(if: false) { y }}"): cannot have directive applications in the @key(fields:) argument but found @include(if: false).', + ], ]); }); it('rejects directive applications in @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "v { ... on V @skip(if: true) { x y } }") } @@ -383,14 +467,17 @@ describe('fieldset-based directives', () => { x: Int y: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_DIRECTIVE_IN_FIELDS_ARG', '[S] On field "Query.t", for @provides(fields: "v { ... on V @skip(if: true) { x y } }"): cannot have directive applications in the @provides(fields:) argument but found @skip(if: true).'], + [ + 'PROVIDES_DIRECTIVE_IN_FIELDS_ARG', + '[S] On field "Query.t", for @provides(fields: "v { ... on V @skip(if: true) { x y } }"): cannot have directive applications in the @provides(fields:) argument but found @skip(if: true).', + ], ]); }); it('rejects directive applications in @requires', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -400,14 +487,17 @@ describe('fieldset-based directives', () => { a: Int @requires(fields: "... @skip(if: false) { b }") b: Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['REQUIRES_DIRECTIVE_IN_FIELDS_ARG', '[S] On field "T.a", for @requires(fields: "... @skip(if: false) { b }"): cannot have directive applications in the @requires(fields:) argument but found @skip(if: false).'], + [ + 'REQUIRES_DIRECTIVE_IN_FIELDS_ARG', + '[S] On field "T.a", for @requires(fields: "... @skip(if: false) { b }"): cannot have directive applications in the @requires(fields:) argument but found @skip(if: false).', + ], ]); }); it('can collect multiple errors in a single `fields` argument', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "f(x: 3)") } @@ -416,15 +506,21 @@ describe('fieldset-based directives', () => { id: ID f(x: Int): Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['PROVIDES_FIELDS_HAS_ARGS', '[S] On field "Query.t", for @provides(fields: "f(x: 3)"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @provides)'], - ['PROVIDES_FIELDS_MISSING_EXTERNAL', '[S] On field "Query.t", for @provides(fields: "f(x: 3)"): field "T.f" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)'], + [ + 'PROVIDES_FIELDS_HAS_ARGS', + '[S] On field "Query.t", for @provides(fields: "f(x: 3)"): field T.f cannot be included because it has arguments (fields with argument are not allowed in @provides)', + ], + [ + 'PROVIDES_FIELDS_MISSING_EXTERNAL', + '[S] On field "Query.t", for @provides(fields: "f(x: 3)"): field "T.f" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)', + ], ]); }); it('rejects aliases in @key', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -432,14 +528,17 @@ describe('fieldset-based directives', () => { type T @key(fields: "foo: id") { id: ID! } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - [ 'KEY_INVALID_FIELDS', '[S] On type "T", for @key(fields: "foo: id"): Cannot use alias "foo" in "foo: id": aliases are not currently supported in @key' ], + [ + 'KEY_INVALID_FIELDS', + '[S] On type "T", for @key(fields: "foo: id"): Cannot use alias "foo" in "foo: id": aliases are not currently supported in @key', + ], ]); }); it('rejects aliases in @provides', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T @provides(fields: "bar: x") } @@ -448,14 +547,17 @@ describe('fieldset-based directives', () => { id: ID! x: Int @external } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - [ 'PROVIDES_INVALID_FIELDS', '[S] On field "Query.t", for @provides(fields: "bar: x"): Cannot use alias "bar" in "bar: x": aliases are not currently supported in @provides' ], + [ + 'PROVIDES_INVALID_FIELDS', + '[S] On field "Query.t", for @provides(fields: "bar: x"): Cannot use alias "bar" in "bar: x": aliases are not currently supported in @provides', + ], ]); }); it('rejects aliases in @requires', () => { - const subgraph = gql` + const subgraph = gql` type Query { t: T } @@ -471,17 +573,23 @@ describe('fieldset-based directives', () => { a: Int b: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - [ 'REQUIRES_INVALID_FIELDS', '[S] On field "T.g", for @requires(fields: "foo: y"): Cannot use alias "foo" in "foo: y": aliases are not currently supported in @requires' ], - [ 'REQUIRES_INVALID_FIELDS', '[S] On field "T.h", for @requires(fields: "x { m: a n: b }"): Cannot use alias "m" in "m: a": aliases are not currently supported in @requires' ], + [ + 'REQUIRES_INVALID_FIELDS', + '[S] On field "T.g", for @requires(fields: "foo: y"): Cannot use alias "foo" in "foo: y": aliases are not currently supported in @requires', + ], + [ + 'REQUIRES_INVALID_FIELDS', + '[S] On field "T.h", for @requires(fields: "x { m: a n: b }"): Cannot use alias "m" in "m: a": aliases are not currently supported in @requires', + ], ]); }); }); describe('root types', () => { it('rejects using Query as type name if not the query root', () => { - const subgraph = gql` + const subgraph = gql` schema { query: MyQuery } @@ -493,14 +601,17 @@ describe('root types', () => { type Query { g: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['ROOT_QUERY_USED', '[S] The schema has a type named "Query" but it is not set as the query root type ("MyQuery" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.'], + [ + 'ROOT_QUERY_USED', + '[S] The schema has a type named "Query" but it is not set as the query root type ("MyQuery" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.', + ], ]); }); it('rejects using Mutation as type name if not the mutation root', () => { - const subgraph = gql` + const subgraph = gql` schema { mutation: MyMutation } @@ -512,14 +623,17 @@ describe('root types', () => { type Mutation { g: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['ROOT_MUTATION_USED', '[S] The schema has a type named "Mutation" but it is not set as the mutation root type ("MyMutation" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.'], + [ + 'ROOT_MUTATION_USED', + '[S] The schema has a type named "Mutation" but it is not set as the mutation root type ("MyMutation" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.', + ], ]); }); it('rejects using Subscription as type name if not the subscription root', () => { - const subgraph = gql` + const subgraph = gql` schema { subscription: MySubscription } @@ -531,32 +645,49 @@ describe('root types', () => { type Subscription { g: Int } - ` + `; expect(buildForErrors(subgraph)).toStrictEqual([ - ['ROOT_SUBSCRIPTION_USED', '[S] The schema has a type named "Subscription" but it is not set as the subscription root type ("MySubscription" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.'], + [ + 'ROOT_SUBSCRIPTION_USED', + '[S] The schema has a type named "Subscription" but it is not set as the subscription root type ("MySubscription" is instead): this is not supported by federation. If a root type does not use its default name, there should be no other type with that default name.', + ], ]); }); }); describe('custom error message for misnamed directives', () => { it.each([ - { name: 'fed1', extraMsg: ' If so, note that it is a federation 2 directive but this schema is a federation 1 one. To be a federation 2 schema, it needs to @link to the federation specifcation v2.' }, + { + name: 'fed1', + extraMsg: + ' If so, note that it is a federation 2 directive but this schema is a federation 1 one. To be a federation 2 schema, it needs to @link to the federation specifcation v2.', + }, { name: 'fed2', extraMsg: '' }, - - ])('has suggestions if a federation directive name is mispelled in $name', ({name, extraMsg}) => { - const subgraph = gql` - type T @keys(fields: "id") { - id: Int @foo - foo: String @sharable - } - `; - - expect(buildForErrors(subgraph, { asFed2 : name === 'fed2' })).toStrictEqual([ - ['INVALID_GRAPHQL', `[S] Unknown directive "@foo".`], - ['INVALID_GRAPHQL', `[S] Unknown directive "@sharable". Did you mean "@shareable"?${extraMsg}`], - ['INVALID_GRAPHQL', `[S] Unknown directive "@keys". Did you mean "@key"?`], - ]); - }); + ])( + 'has suggestions if a federation directive name is mispelled in $name', + ({ name, extraMsg }) => { + const subgraph = gql` + type T @keys(fields: "id") { + id: Int @foo + foo: String @sharable + } + `; + + expect( + buildForErrors(subgraph, { asFed2: name === 'fed2' }), + ).toStrictEqual([ + ['INVALID_GRAPHQL', `[S] Unknown directive "@foo".`], + [ + 'INVALID_GRAPHQL', + `[S] Unknown directive "@sharable". Did you mean "@shareable"?${extraMsg}`, + ], + [ + 'INVALID_GRAPHQL', + `[S] Unknown directive "@keys". Did you mean "@key"?`, + ], + ]); + }, + ); it('has suggestions if a fed2 directive is used in fed1', () => { const subgraph = gql` @@ -566,16 +697,21 @@ describe('custom error message for misnamed directives', () => { } `; - expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([ - ['INVALID_GRAPHQL', `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation 2 directive, note that this schema is a federation 1 schema. To be a federation 2 schema, it needs to @link to the federation specifcation v2.`], + expect(buildForErrors(subgraph, { asFed2: false })).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation 2 directive, note that this schema is a federation 1 schema. To be a federation 2 schema, it needs to @link to the federation specifcation v2.`, + ], ]); }); it('has suggestions if a fed2 directive is used under the wrong name (for the schema)', () => { const subgraph = gql` extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0", - import: [ { name: "@key", as: "@myKey"} ]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: [{ name: "@key", as: "@myKey" }] + ) type T @key(fields: "id") { id: Int @@ -584,9 +720,15 @@ describe('custom error message for misnamed directives', () => { `; // Note: it's a fed2 schema, but we manually add the @link, so we pass `asFed2: false` to avoid having added twice. - expect(buildForErrors(subgraph, { asFed2 : false })).toStrictEqual([ - ['INVALID_GRAPHQL', `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation directive, you should use fully-qualified name "@federation__shareable" or add "@shareable" to the \`import\` argument of the @link to the federation specification.`], - ['INVALID_GRAPHQL', `[S] Unknown directive "@key". If you meant the "@key" federation directive, you should use "@myKey" as it is imported under that name in the @link to the federation specification of this schema.`], + expect(buildForErrors(subgraph, { asFed2: false })).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + `[S] Unknown directive "@shareable". If you meant the \"@shareable\" federation directive, you should use fully-qualified name "@federation__shareable" or add "@shareable" to the \`import\` argument of the @link to the federation specification.`, + ], + [ + 'INVALID_GRAPHQL', + `[S] Unknown directive "@key". If you meant the "@key" federation directive, you should use "@myKey" as it is imported under that name in the @link to the federation specification of this schema.`, + ], ]); }); }); @@ -659,13 +801,18 @@ describe('@core/@link handling', () => { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } - ` + `; const validateFullSchema = (subgraph: Subgraph) => { // Note: we merge types and extensions to avoid having to care whether the @link are on a schema definition or schema extension // as 1) this will vary (we add them to extensions in our test, but when auto-added, they are added to the schema definition) // and 2) it doesn't matter in practice, it's valid in all cases. - expect(printSchema(subgraph.schema, { ...defaultPrintOptions, mergeTypesAndExtensions: true})).toMatchString(expectedFullSchema); - } + expect( + printSchema(subgraph.schema, { + ...defaultPrintOptions, + mergeTypesAndExtensions: true, + }), + ).toMatchString(expectedFullSchema); + }; it('expands everything if only the federation spec is linked', () => { const doc = gql` @@ -703,13 +850,19 @@ describe('@core/@link handling', () => { gql` extend schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! } - directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + directive @key( + fields: federation__FieldSet! + resolvable: Boolean = true + ) repeatable on OBJECT | INTERFACE scalar federation__FieldSet @@ -718,7 +871,10 @@ describe('@core/@link handling', () => { gql` extend schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! @@ -728,7 +884,10 @@ describe('@core/@link handling', () => { `, gql` extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! @@ -738,17 +897,21 @@ describe('@core/@link handling', () => { `, gql` extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! } - directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION + directive @federation__external( + reason: String + ) on OBJECT | FIELD_DEFINITION `, gql` - extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0") + extend schema @link(url: "https://specs.apollo.dev/federation/v2.0") type T { k: ID! @@ -775,13 +938,18 @@ describe('@core/@link handling', () => { gql` extend schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! } - directive @key(fields: federation__FieldSet!) repeatable on OBJECT | INTERFACE + directive @key( + fields: federation__FieldSet! + ) repeatable on OBJECT | INTERFACE scalar federation__FieldSet `, @@ -789,7 +957,10 @@ describe('@core/@link handling', () => { gql` extend schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@inaccessible"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@inaccessible"] + ) type T { k: ID! @inaccessible @@ -800,26 +971,38 @@ describe('@core/@link handling', () => { // @key is repeatable, but you're welcome to restrict yourself to never repeating it. gql` extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! } - directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) on OBJECT | INTERFACE + directive @key( + fields: federation__FieldSet! + resolvable: Boolean = true + ) on OBJECT | INTERFACE scalar federation__FieldSet `, // @key `resolvable` argument is optional, but you're welcome to force users to always provide it. gql` extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k", resolvable: true) { k: ID! } - directive @key(fields: federation__FieldSet!, resolvable: Boolean!) repeatable on OBJECT | INTERFACE + directive @key( + fields: federation__FieldSet! + resolvable: Boolean! + ) repeatable on OBJECT | INTERFACE scalar federation__FieldSet `, @@ -828,13 +1011,21 @@ describe('@core/@link handling', () => { gql` extend schema @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"]) + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@key"] + ) type T @key(fields: "k") { k: ID! } - directive @link(url: String!, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + directive @link( + url: String! + as: String + for: link__Purpose + import: [link__Import] + ) repeatable on SCHEMA scalar link__Import scalar link__Purpose @@ -854,14 +1045,18 @@ describe('@core/@link handling', () => { k: ID! } - directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION | SCHEMA + directive @federation__external( + reason: String + ) on OBJECT | FIELD_DEFINITION | SCHEMA `; // @external is not allowed on 'schema' and likely never will. - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ 'DIRECTIVE_DEFINITION_INVALID', '[S] Invalid definition for directive "@federation__external": "@federation__external" should have locations OBJECT, FIELD_DEFINITION, but found (non-subset) OBJECT, FIELD_DEFINITION, SCHEMA', - ]]); + ], + ]); }); it('errors on invalid non-repeatable directive marked repeateable', () => { @@ -877,10 +1072,12 @@ describe('@core/@link handling', () => { `; // @external is not repeatable (and has no reason to be since it has no arguments). - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'DIRECTIVE_DEFINITION_INVALID', - '[S] Invalid definition for directive "@federation__external": "@federation__external" should not be repeatable', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'DIRECTIVE_DEFINITION_INVALID', + '[S] Invalid definition for directive "@federation__external": "@federation__external" should not be repeatable', + ], + ]); }); it('errors on unknown argument of known directive', () => { @@ -895,10 +1092,12 @@ describe('@core/@link handling', () => { directive @federation__external(foo: Int) on OBJECT | FIELD_DEFINITION `; - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'DIRECTIVE_DEFINITION_INVALID', - '[S] Invalid definition for directive "@federation__external": unknown/unsupported argument "foo"', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'DIRECTIVE_DEFINITION_INVALID', + '[S] Invalid definition for directive "@federation__external": unknown/unsupported argument "foo"', + ], + ]); }); it('errors on invalid type for a known argument', () => { @@ -910,13 +1109,18 @@ describe('@core/@link handling', () => { k: ID! } - directive @key(fields: String!, resolvable: String) repeatable on OBJECT | INTERFACE + directive @key( + fields: String! + resolvable: String + ) repeatable on OBJECT | INTERFACE `; - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'DIRECTIVE_DEFINITION_INVALID', - '[S] Invalid definition for directive "@key": argument "resolvable" should have type "Boolean" but found type "String"', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'DIRECTIVE_DEFINITION_INVALID', + '[S] Invalid definition for directive "@key": argument "resolvable" should have type "Boolean" but found type "String"', + ], + ]); }); it('errors on a required argument defined as optional', () => { @@ -928,21 +1132,25 @@ describe('@core/@link handling', () => { k: ID! } - directive @key(fields: federation__FieldSet, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + directive @key( + fields: federation__FieldSet + resolvable: Boolean = true + ) repeatable on OBJECT | INTERFACE scalar federation__FieldSet `; - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'DIRECTIVE_DEFINITION_INVALID', - '[S] Invalid definition for directive "@key": argument "fields" should have type "federation__FieldSet!" but found type "federation__FieldSet"', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'DIRECTIVE_DEFINITION_INVALID', + '[S] Invalid definition for directive "@key": argument "fields" should have type "federation__FieldSet!" but found type "federation__FieldSet"', + ], + ]); }); it('errors on invalid definition for @link Purpose', () => { const doc = gql` - extend schema - @link(url: "https://specs.apollo.dev/federation/v2.0") + extend schema @link(url: "https://specs.apollo.dev/federation/v2.0") type T { k: ID! @@ -954,10 +1162,12 @@ describe('@core/@link handling', () => { } `; - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'TYPE_DEFINITION_INVALID', - '[S] Invalid definition for type "Purpose": expected values [EXECUTION, SECURITY] but found [EXECUTION, RANDOM].', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'TYPE_DEFINITION_INVALID', + '[S] Invalid definition for type "Purpose": expected values [EXECUTION, SECURITY] but found [EXECUTION, RANDOM].', + ], + ]); }); it('allows any (non-scalar) type in redefinition when expected type is a scalar', () => { @@ -970,7 +1180,10 @@ describe('@core/@link handling', () => { } # 'fields' should be of type 'federation_FieldSet!', but ensure we allow 'String!' alternatively. - directive @key(fields: String!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + directive @key( + fields: String! + resolvable: Boolean = true + ) repeatable on OBJECT | INTERFACE `; // Just making sure this don't error out. @@ -987,18 +1200,21 @@ describe('@core/@link handling', () => { directive @key(fields: String!) on OBJECT `; - // Test for fed2 (with @key being @link-ed) - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] The directive "@key" can only be used once at this location.', - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] The directive "@key" can only be used once at this location.', + ], + ]); // Test for fed1 - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] The directive "@key" can only be used once at this location.', - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] The directive "@key" can only be used once at this location.', + ], + ]); }); }); @@ -1069,12 +1285,14 @@ describe('federation 1 schema', () => { directive @key(fields: _FieldSet!, unknown: Int) on OBJECT | INTERFACE `; - expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([[ - 'DIRECTIVE_DEFINITION_INVALID', - '[S] Invalid definition for directive "@key": unknown/unsupported argument "unknown"' - ]]); + expect(buildForErrors(doc, { asFed2: false })).toStrictEqual([ + [ + 'DIRECTIVE_DEFINITION_INVALID', + '[S] Invalid definition for directive "@key": unknown/unsupported argument "unknown"', + ], + ]); }); -}) +}); describe('@shareable', () => { it('can only be applied to fields of object types', () => { @@ -1084,10 +1302,12 @@ describe('@shareable', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_SHAREABLE_USAGE', - '[S] Invalid use of @shareable on field "I.a": only object type fields can be marked with @shareable' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_SHAREABLE_USAGE', + '[S] Invalid use of @shareable on field "I.a": only object type fields can be marked with @shareable', + ], + ]); }); it('rejects duplicate @shareable on the same definition declaration', () => { @@ -1098,10 +1318,12 @@ describe('@shareable', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_SHAREABLE_USAGE', - '[S] Invalid duplicate application of @shareable on the same type declaration of "E": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_SHAREABLE_USAGE', + '[S] Invalid duplicate application of @shareable on the same type declaration of "E": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration', + ], + ]); }); it('rejects duplicate @shareable on the same extension declaration', () => { @@ -1115,10 +1337,12 @@ describe('@shareable', () => { b: Int } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_SHAREABLE_USAGE', - '[S] Invalid duplicate application of @shareable on the same type declaration of "E": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_SHAREABLE_USAGE', + '[S] Invalid duplicate application of @shareable on the same type declaration of "E": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration', + ], + ]); }); it('rejects duplicate @shareable on a field', () => { @@ -1128,10 +1352,12 @@ describe('@shareable', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_SHAREABLE_USAGE', - '[S] Invalid duplicate application of @shareable on field "E.a": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_SHAREABLE_USAGE', + '[S] Invalid duplicate application of @shareable on field "E.a": @shareable is only repeatable on types so it can be used simultaneously on a type definition and its extensions, but it should not be duplicated on the same definition/extension declaration', + ], + ]); }); }); @@ -1162,10 +1388,12 @@ describe('@interfaceObject/@key on interfaces validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INTERFACE_KEY_NOT_ON_IMPLEMENTATION', - '[S] Key @key(fields: "id1") on interface type "I" is missing on implementation types "A" and "C".', - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INTERFACE_KEY_NOT_ON_IMPLEMENTATION', + '[S] Key @key(fields: "id1") on interface type "I" is missing on implementation types "A" and "C".', + ], + ]); }); it('@key on interfaces with @key on some implementation non resolvable', () => { @@ -1190,10 +1418,12 @@ describe('@interfaceObject/@key on interfaces validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INTERFACE_KEY_NOT_ON_IMPLEMENTATION', - '[S] Key @key(fields: "id1") on interface type "I" should be resolvable on all implementation types, but is declared with argument "@key(resolvable:)" set to false in type "C".', - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INTERFACE_KEY_NOT_ON_IMPLEMENTATION', + '[S] Key @key(fields: "id1") on interface type "I" should be resolvable on all implementation types, but is declared with argument "@key(resolvable:)" set to false in type "C".', + ], + ]); }); it('ensures order of fields in key does not matter', () => { @@ -1246,9 +1476,11 @@ describe('@interfaceObject/@key on interfaces validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INTERFACE_OBJECT_USAGE_ERROR', - '[S] The @interfaceObject directive can only be applied to entity types but type "B" has no @key in this subgraph.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INTERFACE_OBJECT_USAGE_ERROR', + '[S] The @interfaceObject directive can only be applied to entity types but type "B" has no @key in this subgraph.', + ], + ]); }); }); diff --git a/internals-js/src/__tests__/toAPISchema.test.ts b/internals-js/src/__tests__/toAPISchema.test.ts index b25173aa5..d1e4a50d0 100644 --- a/internals-js/src/__tests__/toAPISchema.test.ts +++ b/internals-js/src/__tests__/toAPISchema.test.ts @@ -15,17 +15,16 @@ describe('toAPISchema', () => { let schema: Schema; beforeAll(() => { - const schemaPath = path.join( - __dirname, - 'supergraphSdl.graphql', - ); + const schemaPath = path.join(__dirname, 'supergraphSdl.graphql'); const supergraphSdl = fs.readFileSync(schemaPath, 'utf8'); schema = buildSchema(supergraphSdl).toAPISchema(); }); it(`doesn't include core directives`, () => { - expect(directiveNames(schema)).toEqual(expect.not.arrayContaining(['core'])); + expect(directiveNames(schema)).toEqual( + expect.not.arrayContaining(['core']), + ); }); it(`doesn't include join directives`, () => { @@ -47,7 +46,7 @@ describe('toAPISchema', () => { it(`does pass through other custom directives`, () => { expect(directiveNames(schema)).toEqual( - expect.arrayContaining([ 'transform', 'stream' ]), + expect.arrayContaining(['transform', 'stream']), ); }); }); diff --git a/internals-js/src/__tests__/utils.test.ts b/internals-js/src/__tests__/utils.test.ts index 227da95b2..ba78630bf 100644 --- a/internals-js/src/__tests__/utils.test.ts +++ b/internals-js/src/__tests__/utils.test.ts @@ -27,8 +27,18 @@ describe('OrderedMap', () => { orderedMap.add('nine', 9); // keys are in alphabetical order - expect(orderedMap.keys()).toEqual(['eight', 'five', 'four', 'nine', 'one', 'seven', 'six', 'three', 'two']); - const sortedArr = [8,5,4,9,1,7,6,3,2]; + expect(orderedMap.keys()).toEqual([ + 'eight', + 'five', + 'four', + 'nine', + 'one', + 'seven', + 'six', + 'three', + 'two', + ]); + const sortedArr = [8, 5, 4, 9, 1, 7, 6, 3, 2]; expect(orderedMap.values()).toEqual(sortedArr); // test using spread operator to make sure iterator is performing correctly @@ -50,21 +60,23 @@ describe('OrderedMap', () => { }); it('sort by string length', () => { - const orderedMap = new OrderedMap((a: string, b: string) => { - if (a.length < b.length) { - return -1; - } else if (b.length < a.length) { - return 1; - } - return 0; - }); + const orderedMap = new OrderedMap( + (a: string, b: string) => { + if (a.length < b.length) { + return -1; + } else if (b.length < a.length) { + return 1; + } + return 0; + }, + ); orderedMap.add('eight', 8); orderedMap.add('seventy', 70); orderedMap.add('six', 6); orderedMap.add('four', 4); expect(orderedMap.keys()).toEqual(['six', 'four', 'eight', 'seventy']); - const sortedArr = [6,4,8,70]; + const sortedArr = [6, 4, 8, 70]; expect(orderedMap.values()).toEqual(sortedArr); // test using spread operator to make sure iterator is performing correctly @@ -83,8 +95,8 @@ describe('OrderedMap', () => { orderedMap.add(5, 50); orderedMap.add(8, 80); - const keys = [1,2,3,4,5,6,7,8,9]; - const values = [10,20,30,40,50,60,70,80,90]; + const keys = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + const values = [10, 20, 30, 40, 50, 60, 70, 80, 90]; expect(orderedMap.keys()).toEqual(keys); expect(orderedMap.values()).toEqual(values); expect([...orderedMap]).toEqual(values); diff --git a/internals-js/src/__tests__/values.test.ts b/internals-js/src/__tests__/values.test.ts index 021fcddb9..eaab39aa7 100644 --- a/internals-js/src/__tests__/values.test.ts +++ b/internals-js/src/__tests__/values.test.ts @@ -1,6 +1,4 @@ -import { - Schema, -} from '../definitions'; +import { Schema } from '../definitions'; import { buildSchema } from '../buildSchema'; import { parseOperation } from '../operations'; import gql from 'graphql-tag'; @@ -27,11 +25,14 @@ test('handles non-list value for list argument (as singleton)', () => { } `); - const operation = parseOperation(schema, ` + const operation = parseOperation( + schema, + ` query { f(v: MONDAY) } - `); + `, + ); expect(operation.toString(false, false)).toBe('{ f(v: [MONDAY]) }'); expect(operation.selectionSet.toSelectionSetNode()).toMatchInlineSnapshot(` @@ -79,10 +80,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: "foo") provided for argument Query.f(a:) of type Int.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: "foo") provided for argument Query.f(a:) of type Int.', + ], + ]); }); it('errors on invalid default value in directive argument', () => { @@ -94,10 +97,12 @@ describe('default value validation', () => { directive @myDirective(a: Int = "foo") on FIELD `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: "foo") provided for argument @myDirective(a:) of type Int.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: "foo") provided for argument @myDirective(a:) of type Int.', + ], + ]); }); it('errors on invalid default value in input field', () => { @@ -107,10 +112,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: "foo") provided for input field I.x of type Int.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: "foo") provided for input field I.x of type Int.', + ], + ]); }); it('errors on invalid default value for existing input field', () => { @@ -125,10 +132,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: {x: 2, y: "3"}) provided for argument Query.f(i:) of type I.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: {x: 2, y: "3"}) provided for argument Query.f(i:) of type I.', + ], + ]); }); it('errors on default value containing unexpected input fields', () => { @@ -143,10 +152,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: {x: 1, y: 2, z: 3}) provided for argument Query.f(i:) of type I.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: {x: 1, y: 2, z: 3}) provided for argument Query.f(i:) of type I.', + ], + ]); }); it('errors on default value being unknown enum value', () => { @@ -168,10 +179,12 @@ describe('default value validation', () => { // it in the error message). We could fix this someday if we change to using a specific class/object for // enum values internally (though this might have backward compatbility constraints), but in the meantime, // it's unlikely to trip users too much. - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: "THREE") provided for argument Query.f(e:) of type E.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: "THREE") provided for argument Query.f(e:) of type E.', + ], + ]); }); it('errors on default value being unknown enum value (as string)', () => { @@ -186,10 +199,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: "TWOO") provided for argument Query.f(e:) of type E.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: "TWOO") provided for argument Query.f(e:) of type E.', + ], + ]); }); it('accepts default value enum value as string, if a valid enum value', () => { @@ -242,7 +257,7 @@ describe('default value validation', () => { it('accepts any value for a custom scalar in an input field', () => { const doc = gql` input I { - x: Scalar = { z: { a: 4} } + x: Scalar = { z: { a: 4 } } } scalar Scalar @@ -278,16 +293,18 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: 2) provided for argument Query.f(x:) of type [[[String]!]]!.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: 2) provided for argument Query.f(x:) of type [[[String]!]]!.', + ], + ]); }); it('accepts default value coercible to its type but needing multiple/nested coercions', () => { const doc = gql` type Query { - f(x: I = { j: {x: 1, z: "Foo"} }): Int + f(x: I = { j: { x: 1, z: "Foo" } }): Int } input I { @@ -333,10 +350,12 @@ describe('default value validation', () => { } `; - expect(buildForErrors(doc)).toStrictEqual([[ - 'INVALID_GRAPHQL', - '[S] Invalid default value (got: null) provided for argument Query.f(i:) of type Int!.' - ]]); + expect(buildForErrors(doc)).toStrictEqual([ + [ + 'INVALID_GRAPHQL', + '[S] Invalid default value (got: null) provided for argument Query.f(i:) of type Int!.', + ], + ]); }); it('Accepts null default value for nullable input', () => { @@ -361,9 +380,9 @@ describe('values printing', () => { FOO BAR } - ` + `; expect(printSchema(parseSchema(sdl))).toMatchString(sdl); - }) + }); it('prints enums value when its coercible to list through multiple coercions', () => { const sdl = ` @@ -375,16 +394,25 @@ describe('values printing', () => { FOO BAR } - ` + `; expect(printSchema(parseSchema(sdl))).toMatchString(sdl); - }) + }); }); describe('objectEquals tests', () => { it('simple object equality tests', () => { - expect(valueEquals({ foo: 'foo' }, { foo: 'foo'})).toBe(true); - expect(valueEquals({ foo: 'foo', bar: undefined }, { foo: 'foo', bar: undefined})).toBe(true); - expect(valueEquals({ foo: 'foo' }, { foo: 'foo', bar: undefined})).toBe(false); - expect(valueEquals({ foo: 'foo', bar: undefined }, { foo: 'foo' })).toBe(false); + expect(valueEquals({ foo: 'foo' }, { foo: 'foo' })).toBe(true); + expect( + valueEquals( + { foo: 'foo', bar: undefined }, + { foo: 'foo', bar: undefined }, + ), + ).toBe(true); + expect(valueEquals({ foo: 'foo' }, { foo: 'foo', bar: undefined })).toBe( + false, + ); + expect(valueEquals({ foo: 'foo', bar: undefined }, { foo: 'foo' })).toBe( + false, + ); }); }); diff --git a/internals-js/src/specs/__tests__/coreSpec.test.ts b/internals-js/src/specs/__tests__/coreSpec.test.ts index 1a987d58f..e4814fa84 100644 --- a/internals-js/src/specs/__tests__/coreSpec.test.ts +++ b/internals-js/src/specs/__tests__/coreSpec.test.ts @@ -1,10 +1,16 @@ -import { DocumentNode, GraphQLError } from "graphql"; -import gql from "graphql-tag"; -import { buildSubgraph } from "../../federation"; -import { assert } from "../../utils"; -import { buildSchemaFromAST } from "../../buildSchema"; -import { removeAllCoreFeatures, FeatureDefinitions, FeatureVersion, FeatureDefinition, FeatureUrl } from "../coreSpec"; -import { errorCauses } from "../../error"; +import { DocumentNode, GraphQLError } from 'graphql'; +import gql from 'graphql-tag'; +import { buildSubgraph } from '../../federation'; +import { assert } from '../../utils'; +import { buildSchemaFromAST } from '../../buildSchema'; +import { + removeAllCoreFeatures, + FeatureDefinitions, + FeatureVersion, + FeatureDefinition, + FeatureUrl, +} from '../coreSpec'; +import { errorCauses } from '../../error'; function expectErrors( subgraphDefs: DocumentNode, @@ -16,7 +22,7 @@ function expectErrors( // Note: we use buildSubgraph because currently it's the only one that does auto-magic import of // directive definition, and we don't want to bother with adding the @link definition to every // example. - buildSubgraph('S', '', subgraphDefs) + buildSubgraph('S', '', subgraphDefs); } catch (e) { // Kind-of ugly, but if Jest has a better option, I haven't found it. thrownError = e; @@ -29,22 +35,25 @@ function expectErrors( assert(causes, 'Should have some causes'); // Note: all the received message with start with "[S] ", so the `slice` below // strips the extra prefix. This avoid leaking the subgraph name to leak to the tests themselves. - expect(causes.map((e) => e.message.slice(4))).toStrictEqual(expectedErrorMessages); + expect(causes.map((e) => e.message.slice(4))).toStrictEqual( + expectedErrorMessages, + ); } describe('@link(import:) argument', () => { test('errors on misformed values', () => { const schema = gql` - extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: [ - 2, - { foo: "bar" }, - { name: "@key", badName: "foo"}, - { name: 42 }, - { as: "bar" }, - ] - ) + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: [ + 2 + { foo: "bar" } + { name: "@key", badName: "foo" } + { name: 42 } + { as: "bar" } + ] + ) type Query { q: Int @@ -62,13 +71,14 @@ describe('@link(import:) argument', () => { test('errors on mismatch between name and alias', () => { const schema = gql` - extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: [ - { name: "@key", as: "myKey" }, - { name: "FieldSet", as: "@fieldSet" }, - ] - ) + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: [ + { name: "@key", as: "myKey" } + { name: "FieldSet", as: "@fieldSet" } + ] + ) type Query { q: Int @@ -83,10 +93,11 @@ describe('@link(import:) argument', () => { test('errors on importing unknown elements for known features', () => { const schema = gql` - extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: [ "@foo", "key", { name: "@sharable" } ] - ) + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.0" + import: ["@foo", "key", { name: "@sharable" }] + ) type Query { q: Int @@ -96,7 +107,7 @@ describe('@link(import:) argument', () => { expectErrors(schema, [ 'Cannot import unknown element "@foo".', 'Cannot import unknown element "key". Did you mean directive "@key"?', - 'Cannot import unknown element "@sharable\". Did you mean "@shareable"?', + 'Cannot import unknown element "@sharable". Did you mean "@shareable"?', ]); }); }); @@ -104,7 +115,12 @@ describe('@link(import:) argument', () => { describe('removeAllCoreFeatures', () => { it('removes core (and only core) feature definitions, accounting for aliasing', () => { const schema = buildSchemaFromAST(gql` - directive @lonk(url: String, as: String, for: Porpoise, import: [lonk__Import]) repeatable on SCHEMA + directive @lonk( + url: String + as: String + for: Porpoise + import: [lonk__Import] + ) repeatable on SCHEMA scalar lonk__Import @@ -122,20 +138,18 @@ describe('removeAllCoreFeatures', () => { extend schema @lonk( - url: "https://specs.apollo.dev/link/v1.0", - as: "lonk", - import: [ - { name: "Purpose", as: "Porpoise" } - ] + url: "https://specs.apollo.dev/link/v1.0" + as: "lonk" + import: [{ name: "Purpose", as: "Porpoise" }] ) @lonk( - url: "https://localhost/foobar/v1.0", + url: "https://localhost/foobar/v1.0" as: "foo" import: [ - "bar", - "@baz", - { name: "qux", as: "qax" }, - { name: "@quz", as: "@qaz" }, + "bar" + "@baz" + { name: "qux", as: "qax" } + { name: "@quz", as: "@qaz" } ] ) @@ -185,29 +199,29 @@ describe('removeAllCoreFeatures', () => { removeAllCoreFeatures(schema); schema.validate(); - expect(schema.elementByCoordinate("@lonk")).toBeUndefined(); - expect(schema.elementByCoordinate("lonk__Import")).toBeUndefined(); - expect(schema.elementByCoordinate("Porpoise")).toBeUndefined(); - expect(schema.elementByCoordinate("foobar")).toBeDefined(); - expect(schema.elementByCoordinate("foobar__Scalar")).toBeDefined(); - expect(schema.elementByCoordinate("@foobar")).toBeDefined(); - expect(schema.elementByCoordinate("@foobar__directive")).toBeDefined(); - expect(schema.elementByCoordinate("foo")).toBeDefined(); - expect(schema.elementByCoordinate("foo__Scalar")).toBeUndefined(); - expect(schema.elementByCoordinate("@foo")).toBeUndefined(); - expect(schema.elementByCoordinate("@foo__directive")).toBeUndefined(); - expect(schema.elementByCoordinate("bar")).toBeUndefined(); - expect(schema.elementByCoordinate("foo__bar")).toBeUndefined(); - expect(schema.elementByCoordinate("@baz")).toBeUndefined(); - expect(schema.elementByCoordinate("@foo__baz")).toBeUndefined(); - expect(schema.elementByCoordinate("qux")).toBeDefined(); - expect(schema.elementByCoordinate("@quz")).toBeDefined(); - expect(schema.elementByCoordinate("qax")).toBeUndefined(); - expect(schema.elementByCoordinate("foo__qax")).toBeUndefined(); - expect(schema.elementByCoordinate("foo__qux")).toBeUndefined(); - expect(schema.elementByCoordinate("@qaz")).toBeUndefined(); - expect(schema.elementByCoordinate("@foo__qaz")).toBeUndefined(); - expect(schema.elementByCoordinate("@foo__quz")).toBeUndefined(); + expect(schema.elementByCoordinate('@lonk')).toBeUndefined(); + expect(schema.elementByCoordinate('lonk__Import')).toBeUndefined(); + expect(schema.elementByCoordinate('Porpoise')).toBeUndefined(); + expect(schema.elementByCoordinate('foobar')).toBeDefined(); + expect(schema.elementByCoordinate('foobar__Scalar')).toBeDefined(); + expect(schema.elementByCoordinate('@foobar')).toBeDefined(); + expect(schema.elementByCoordinate('@foobar__directive')).toBeDefined(); + expect(schema.elementByCoordinate('foo')).toBeDefined(); + expect(schema.elementByCoordinate('foo__Scalar')).toBeUndefined(); + expect(schema.elementByCoordinate('@foo')).toBeUndefined(); + expect(schema.elementByCoordinate('@foo__directive')).toBeUndefined(); + expect(schema.elementByCoordinate('bar')).toBeUndefined(); + expect(schema.elementByCoordinate('foo__bar')).toBeUndefined(); + expect(schema.elementByCoordinate('@baz')).toBeUndefined(); + expect(schema.elementByCoordinate('@foo__baz')).toBeUndefined(); + expect(schema.elementByCoordinate('qux')).toBeDefined(); + expect(schema.elementByCoordinate('@quz')).toBeDefined(); + expect(schema.elementByCoordinate('qax')).toBeUndefined(); + expect(schema.elementByCoordinate('foo__qax')).toBeUndefined(); + expect(schema.elementByCoordinate('foo__qux')).toBeUndefined(); + expect(schema.elementByCoordinate('@qaz')).toBeUndefined(); + expect(schema.elementByCoordinate('@foo__qaz')).toBeUndefined(); + expect(schema.elementByCoordinate('@foo__quz')).toBeUndefined(); }); }); @@ -229,12 +243,7 @@ describe('FeatureVersion', () => { // version.toString() strings, but do not perform numeric lexicographic // comparison of the major and minor numbers, so 'v10...' < 'v2...' and the // following comparisons produce unintuitive results. - expect([ - v2_3 < v10_0, - v2_3 <= v10_0, - v2_3 > v10_0, - v2_3 >= v10_0, - ]).toEqual( + expect([v2_3 < v10_0, v2_3 <= v10_0, v2_3 > v10_0, v2_3 >= v10_0]).toEqual( // This should really be [true, true, false, false], if JavaScript // supported more flexible/general operator overloading. [false, false, true, true], @@ -266,25 +275,75 @@ describe('getMinimumRequiredVersion tests', () => { it('various combinations', () => { const versions = new FeatureDefinitions('test') .add(new TestFeatureDefinition(new FeatureVersion(0, 1))) - .add(new TestFeatureDefinition(new FeatureVersion(0, 2), new FeatureVersion(1, 0))) - .add(new TestFeatureDefinition(new FeatureVersion(0, 3), new FeatureVersion(2,0))) - .add(new TestFeatureDefinition(new FeatureVersion(0, 4), new FeatureVersion(2,1))) - .add(new TestFeatureDefinition(new FeatureVersion(0, 5), new FeatureVersion(2,2))); - - expect(versions.getMinimumRequiredVersion(new FeatureVersion(0, 1)).version).toEqual(new FeatureVersion(0, 1)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(1, 0)).version).toEqual(new FeatureVersion(0, 2)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(1, 1)).version).toEqual(new FeatureVersion(0, 2)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 0)).version).toEqual(new FeatureVersion(0, 3)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 1)).version).toEqual(new FeatureVersion(0, 4)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 2)).version).toEqual(new FeatureVersion(0, 5)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 3)).version).toEqual(new FeatureVersion(0, 5)); + .add( + new TestFeatureDefinition( + new FeatureVersion(0, 2), + new FeatureVersion(1, 0), + ), + ) + .add( + new TestFeatureDefinition( + new FeatureVersion(0, 3), + new FeatureVersion(2, 0), + ), + ) + .add( + new TestFeatureDefinition( + new FeatureVersion(0, 4), + new FeatureVersion(2, 1), + ), + ) + .add( + new TestFeatureDefinition( + new FeatureVersion(0, 5), + new FeatureVersion(2, 2), + ), + ); + + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(0, 1)).version, + ).toEqual(new FeatureVersion(0, 1)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(1, 0)).version, + ).toEqual(new FeatureVersion(0, 2)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(1, 1)).version, + ).toEqual(new FeatureVersion(0, 2)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 0)).version, + ).toEqual(new FeatureVersion(0, 3)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 1)).version, + ).toEqual(new FeatureVersion(0, 4)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 2)).version, + ).toEqual(new FeatureVersion(0, 5)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 3)).version, + ).toEqual(new FeatureVersion(0, 5)); // now add a new major version and test again. All previous version should be forced to the new major - versions.add(new TestFeatureDefinition(new FeatureVersion(1, 0), new FeatureVersion(2, 4))); - versions.add(new TestFeatureDefinition(new FeatureVersion(1, 1), new FeatureVersion(2, 5))); - - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 3)).version).toEqual(new FeatureVersion(1, 0)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 4)).version).toEqual(new FeatureVersion(1, 0)); - expect(versions.getMinimumRequiredVersion(new FeatureVersion(2, 5)).version).toEqual(new FeatureVersion(1, 1)); - }) -}) + versions.add( + new TestFeatureDefinition( + new FeatureVersion(1, 0), + new FeatureVersion(2, 4), + ), + ); + versions.add( + new TestFeatureDefinition( + new FeatureVersion(1, 1), + new FeatureVersion(2, 5), + ), + ); + + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 3)).version, + ).toEqual(new FeatureVersion(1, 0)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 4)).version, + ).toEqual(new FeatureVersion(1, 0)); + expect( + versions.getMinimumRequiredVersion(new FeatureVersion(2, 5)).version, + ).toEqual(new FeatureVersion(1, 1)); + }); +});