diff --git a/src/execution/__tests__/executor-test.js b/src/execution/__tests__/executor-test.js index 9a70425c258..88f88b27556 100644 --- a/src/execution/__tests__/executor-test.js +++ b/src/execution/__tests__/executor-test.js @@ -1054,7 +1054,7 @@ describe('Execute: Handles basic execution tasks', () => { errors: [ { message: - 'Expected value of type "SpecialType" but got: [object Object].', + 'Expected value of type "SpecialType" but got: {value: "bar"}.', locations: [{ line: 1, column: 3 }], path: ['specials', 1], }, diff --git a/src/execution/__tests__/variables-test.js b/src/execution/__tests__/variables-test.js index 52b6006a267..9a72069f580 100644 --- a/src/execution/__tests__/variables-test.js +++ b/src/execution/__tests__/variables-test.js @@ -371,7 +371,7 @@ describe('Execute: Handles inputs', () => { { message: 'Variable "$input" got invalid value ' + - '{"a":"foo","b":"bar","c":null}; ' + + '{a: "foo", b: "bar", c: null}; ' + 'Expected non-nullable type String! not to be null at value.c.', locations: [{ line: 2, column: 16 }], }, @@ -401,7 +401,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' + + 'Variable "$input" got invalid value {a: "foo", b: "bar"}; ' + 'Field value.c of required type String! was not provided.', locations: [{ line: 2, column: 16 }], }, @@ -421,13 +421,13 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' + + 'Variable "$input" got invalid value {na: {a: "foo"}}; ' + 'Field value.na.c of required type String! was not provided.', locations: [{ line: 2, column: 18 }], }, { message: - 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' + + 'Variable "$input" got invalid value {na: {a: "foo"}}; ' + 'Field value.nb of required type String! was not provided.', locations: [{ line: 2, column: 18 }], }, @@ -446,7 +446,7 @@ describe('Execute: Handles inputs', () => { { message: 'Variable "$input" got invalid value ' + - '{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' + + '{a: "foo", b: "bar", c: "baz", extra: "dog"}; ' + 'Field "extra" is not defined by type TestInputObject.', locations: [{ line: 2, column: 16 }], }, @@ -693,8 +693,8 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$value" got invalid value [1,2,3]; Expected type ' + - 'String; String cannot represent an array value: [1,2,3]', + 'Variable "$value" got invalid value [1, 2, 3]; Expected type ' + + 'String; String cannot represent an array value: [1, 2, 3]', locations: [{ line: 2, column: 16 }], }, ], @@ -841,7 +841,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$input" got invalid value ["A",null,"B"]; ' + + 'Variable "$input" got invalid value ["A", null, "B"]; ' + 'Expected non-nullable type String! not to be null at value[1].', locations: [{ line: 2, column: 16 }], }, @@ -891,7 +891,7 @@ describe('Execute: Handles inputs', () => { errors: [ { message: - 'Variable "$input" got invalid value ["A",null,"B"]; ' + + 'Variable "$input" got invalid value ["A", null, "B"]; ' + 'Expected non-nullable type String! not to be null at value[1].', locations: [{ line: 2, column: 16 }], }, diff --git a/src/execution/execute.js b/src/execution/execute.js index fb04bd6f36f..cf557c18a88 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -1046,7 +1046,7 @@ function ensureValidRuntimeType( throw new GraphQLError( `Abstract type ${returnType.name} must resolve to an Object type at ` + `runtime for field ${info.parentType.name}.${info.fieldName} with ` + - `value "${inspect(result)}", received "${inspect(runtimeType)}". ` + + `value ${inspect(result)}, received "${inspect(runtimeType)}". ` + `Either the ${returnType.name} type should provide a "resolveType" ` + 'function or each possible types should provide an ' + '"isTypeOf" function.', diff --git a/src/execution/values.js b/src/execution/values.js index 5df24406789..426faac4ab6 100644 --- a/src/execution/values.js +++ b/src/execution/values.js @@ -99,7 +99,7 @@ export function getVariableValues( coercionErrors.forEach(error => { error.message = `Variable "$${varName}" got invalid ` + - `value ${JSON.stringify(value)}; ${error.message}`; + `value ${inspect(value)}; ${error.message}`; }); errors.push(...coercionErrors); } else { diff --git a/src/jsutils/inspect.js b/src/jsutils/inspect.js index 70bc6bd21d3..2de18f1e05d 100644 --- a/src/jsutils/inspect.js +++ b/src/jsutils/inspect.js @@ -7,9 +7,21 @@ * @flow strict */ +/** + * Used to print values in error messages. + */ export default function inspect(value: mixed): string { - if (Array.isArray(value)) { - return '[' + String(value) + ']'; - } - return String(value); + return Array.isArray(value) + ? '[' + value.map(inspect).join(', ') + ']' + : value && typeof value === 'object' + ? typeof value.inspect === 'function' + ? value.inspect() + : '{' + + Object.keys(value) + .map(k => `${k}: ${inspect(value[k])}`) + .join(', ') + + '}' + : typeof value === 'string' + ? '"' + value + '"' + : String(value); } diff --git a/src/language/__tests__/parser-test.js b/src/language/__tests__/parser-test.js index 620ec841de6..76fb9fefd35 100644 --- a/src/language/__tests__/parser-test.js +++ b/src/language/__tests__/parser-test.js @@ -29,9 +29,7 @@ describe('Parser', () => { }); it('asserts that a source to parse was provided', () => { - expect(() => parse({})).to.throw( - 'Must provide Source. Received: [object Object]', - ); + expect(() => parse({})).to.throw('Must provide Source. Received: {}'); }); it('parse provides useful errors', () => { diff --git a/src/subscription/__tests__/subscribe-test.js b/src/subscription/__tests__/subscribe-test.js index 06a4d9e50f0..45f5addb1b8 100644 --- a/src/subscription/__tests__/subscribe-test.js +++ b/src/subscription/__tests__/subscribe-test.js @@ -373,7 +373,7 @@ describe('Subscription Initialization Phase', () => { await expectPromiseToThrow( () => createSubscription(pubsub, invalidEmailSchema), - 'Subscription field must return Async Iterable. Received: test', + 'Subscription field must return Async Iterable. Received: "test"', ); }); @@ -474,7 +474,7 @@ describe('Subscription Initialization Phase', () => { { message: 'Variable "$priority" got invalid value "meow"; Expected ' + - 'type Int; Int cannot represent non-integer value: meow', + 'type Int; Int cannot represent non-integer value: "meow"', locations: [{ line: 2, column: 21 }], }, ], diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.js index 610c8112f31..f5f1d28a6a8 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.js @@ -23,6 +23,7 @@ import { import { describe, it } from 'mocha'; import { expect } from 'chai'; +import inspect from '../../jsutils/inspect'; import { isObjectType, isInputType, isOutputType } from '../definition'; const BlogImage = new GraphQLObjectType({ @@ -657,7 +658,7 @@ describe('Type System: Object fields must have valid resolve values', () => { it('rejects an empty Object field resolver', () => { expect(() => schemaWithObjectWithFieldResolver({})).to.throw( 'BadResolver.badField field resolver must be a function if provided, ' + - 'but got: [object Object].', + 'but got: {}.', ); }); @@ -1139,7 +1140,7 @@ describe('Type System: List must accept only types', () => { notTypes.forEach(type => { it(`rejects a non-type as item type of list: ${type}`, () => { expect(() => GraphQLList(type)).to.throw( - `Expected ${type} to be a GraphQL type.`, + `Expected ${inspect(type)} to be a GraphQL type.`, ); }); }); @@ -1175,7 +1176,7 @@ describe('Type System: NonNull must only accept non-nullable types', () => { notNullableTypes.forEach(type => { it(`rejects a non-type as nullable type of non-null: ${type}`, () => { expect(() => GraphQLNonNull(type)).to.throw( - `Expected ${type} to be a GraphQL nullable type.`, + `Expected ${inspect(type)} to be a GraphQL nullable type.`, ); }); }); diff --git a/src/type/__tests__/enumType-test.js b/src/type/__tests__/enumType-test.js index 3763a2249ee..ab6eca14b89 100644 --- a/src/type/__tests__/enumType-test.js +++ b/src/type/__tests__/enumType-test.js @@ -205,7 +205,7 @@ describe('Type System: Enum Values', () => { data: { colorEnum: null }, errors: [ { - message: 'Expected a value of type "Color" but received: GREEN', + message: 'Expected a value of type "Color" but received: "GREEN"', locations: [{ line: 1, column: 3 }], path: ['colorEnum'], }, @@ -384,7 +384,7 @@ describe('Type System: Enum Values', () => { errors: [ { message: - 'Expected a value of type "Complex" but received: [object Object]', + 'Expected a value of type "Complex" but received: {someRandomValue: 123}', locations: [{ line: 6, column: 9 }], path: ['bad'], }, diff --git a/src/type/__tests__/serialization-test.js b/src/type/__tests__/serialization-test.js index ad0f618ef17..d6bf307a9db 100644 --- a/src/type/__tests__/serialization-test.js +++ b/src/type/__tests__/serialization-test.js @@ -38,7 +38,7 @@ describe('Type System: Scalar coercion', () => { 'Int cannot represent non-integer value: -1.1', ); expect(() => GraphQLInt.serialize('-1.1')).to.throw( - 'Int cannot represent non-integer value: -1.1', + 'Int cannot represent non-integer value: "-1.1"', ); // Maybe a safe JavaScript int, but bigger than 2^32, so not // representable as a GraphQL Int @@ -56,7 +56,7 @@ describe('Type System: Scalar coercion', () => { 'Int cannot represent non 32-bit signed integer value: -1e+100', ); expect(() => GraphQLInt.serialize('one')).to.throw( - 'Int cannot represent non-integer value: one', + 'Int cannot represent non-integer value: "one"', ); // Doesn't represent number expect(() => GraphQLInt.serialize('')).to.throw( @@ -92,7 +92,7 @@ describe('Type System: Scalar coercion', () => { 'Float cannot represent non numeric value: Infinity', ); expect(() => GraphQLFloat.serialize('one')).to.throw( - 'Float cannot represent non numeric value: one', + 'Float cannot represent non numeric value: "one"', ); expect(() => GraphQLFloat.serialize('')).to.throw( 'Float cannot represent non numeric value: (empty string)', diff --git a/src/type/__tests__/validation-test.js b/src/type/__tests__/validation-test.js index c1d328582ae..e290267380d 100644 --- a/src/type/__tests__/validation-test.js +++ b/src/type/__tests__/validation-test.js @@ -383,7 +383,7 @@ describe('Type System: A Schema must have Object root types', () => { }); expect(validateSchema(schema)).to.deep.equal([ { - message: 'Expected directive but got: somedirective.', + message: 'Expected directive but got: "somedirective".', }, ]); }); diff --git a/src/utilities/__tests__/coerceValue-test.js b/src/utilities/__tests__/coerceValue-test.js index d23f0341f5d..7adb1155da0 100644 --- a/src/utilities/__tests__/coerceValue-test.js +++ b/src/utilities/__tests__/coerceValue-test.js @@ -35,7 +35,7 @@ describe('coerceValue', () => { it('returns error for array input as string', () => { const result = coerceValue([1, 2, 3], scalar); expectErrors(result).to.deep.equal([ - `Expected type ${scalar}; String cannot represent an array value: [1,2,3]`, + `Expected type ${scalar}; String cannot represent an array value: [1, 2, 3]`, ]); }); }); @@ -79,7 +79,7 @@ describe('coerceValue', () => { it('returns a single error for float input as int', () => { const result = coerceValue('1.5', GraphQLInt); expectErrors(result).to.deep.equal([ - 'Expected type Int; Int cannot represent non-integer value: 1.5', + 'Expected type Int; Int cannot represent non-integer value: "1.5"', ]); }); @@ -100,14 +100,14 @@ describe('coerceValue', () => { it('returns a single error for char input', () => { const result = coerceValue('a', GraphQLInt); expectErrors(result).to.deep.equal([ - 'Expected type Int; Int cannot represent non-integer value: a', + 'Expected type Int; Int cannot represent non-integer value: "a"', ]); }); it('returns a single error for char input', () => { const result = coerceValue('meow', GraphQLInt); expectErrors(result).to.deep.equal([ - 'Expected type Int; Int cannot represent non-integer value: meow', + 'Expected type Int; Int cannot represent non-integer value: "meow"', ]); }); }); @@ -157,14 +157,14 @@ describe('coerceValue', () => { it('returns a single error for char input', () => { const result = coerceValue('a', GraphQLFloat); expectErrors(result).to.deep.equal([ - 'Expected type Float; Float cannot represent non numeric value: a', + 'Expected type Float; Float cannot represent non numeric value: "a"', ]); }); it('returns a single error for char input', () => { const result = coerceValue('meow', GraphQLFloat); expectErrors(result).to.deep.equal([ - 'Expected type Float; Float cannot represent non numeric value: meow', + 'Expected type Float; Float cannot represent non numeric value: "meow"', ]); }); }); @@ -226,15 +226,15 @@ describe('coerceValue', () => { it('returns no error for an invalid field', () => { const result = coerceValue({ foo: 'abc' }, TestInputObject); expectErrors(result).to.deep.equal([ - 'Expected type Int at value.foo; Int cannot represent non-integer value: abc', + 'Expected type Int at value.foo; Int cannot represent non-integer value: "abc"', ]); }); it('returns multiple errors for multiple invalid fields', () => { const result = coerceValue({ foo: 'abc', bar: 'def' }, TestInputObject); expectErrors(result).to.deep.equal([ - 'Expected type Int at value.foo; Int cannot represent non-integer value: abc', - 'Expected type Int at value.bar; Int cannot represent non-integer value: def', + 'Expected type Int at value.foo; Int cannot represent non-integer value: "abc"', + 'Expected type Int at value.bar; Int cannot represent non-integer value: "def"', ]); }); diff --git a/src/validation/rules/FragmentsOnCompositeTypes.js b/src/validation/rules/FragmentsOnCompositeTypes.js index 9d285348ef0..6f2e0b87f57 100644 --- a/src/validation/rules/FragmentsOnCompositeTypes.js +++ b/src/validation/rules/FragmentsOnCompositeTypes.js @@ -7,28 +7,24 @@ * @flow strict */ -import inspect from '../../jsutils/inspect'; import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import { print } from '../../language/printer'; import type { ASTVisitor } from '../../language/visitor'; import { isCompositeType } from '../../type/definition'; -import type { GraphQLType } from '../../type/definition'; import { typeFromAST } from '../../utilities/typeFromAST'; -export function inlineFragmentOnNonCompositeErrorMessage( - type: GraphQLType, -): string { - return `Fragment cannot condition on non composite type "${inspect(type)}".`; +export function inlineFragmentOnNonCompositeErrorMessage(type: string): string { + return `Fragment cannot condition on non composite type "${type}".`; } export function fragmentOnNonCompositeErrorMessage( fragName: string, - type: GraphQLType, + type: string, ): string { return ( `Fragment "${fragName}" cannot condition on non composite ` + - `type "${inspect(type)}".` + `type "${type}".` ); } diff --git a/src/validation/rules/PossibleFragmentSpreads.js b/src/validation/rules/PossibleFragmentSpreads.js index 95ee3212a9e..4461d5aeb33 100644 --- a/src/validation/rules/PossibleFragmentSpreads.js +++ b/src/validation/rules/PossibleFragmentSpreads.js @@ -14,26 +14,25 @@ import type { ASTVisitor } from '../../language/visitor'; import { doTypesOverlap } from '../../utilities/typeComparators'; import { typeFromAST } from '../../utilities/typeFromAST'; import { isCompositeType } from '../../type/definition'; -import type { GraphQLType } from '../../type/definition'; export function typeIncompatibleSpreadMessage( fragName: string, - parentType: GraphQLType, - fragType: GraphQLType, + parentType: string, + fragType: string, ): string { return ( `Fragment "${fragName}" cannot be spread here as objects of ` + - `type "${inspect(parentType)}" can never be of type "${inspect(fragType)}".` + `type "${parentType}" can never be of type "${fragType}".` ); } export function typeIncompatibleAnonSpreadMessage( - parentType: GraphQLType, - fragType: GraphQLType, + parentType: string, + fragType: string, ): string { return ( 'Fragment cannot be spread here as objects of ' + - `type "${inspect(parentType)}" can never be of type "${inspect(fragType)}".` + `type "${parentType}" can never be of type "${fragType}".` ); } @@ -58,7 +57,10 @@ export function PossibleFragmentSpreads( ) { context.reportError( new GraphQLError( - typeIncompatibleAnonSpreadMessage(parentType, fragType), + typeIncompatibleAnonSpreadMessage( + inspect(parentType), + inspect(fragType), + ), [node], ), ); @@ -75,7 +77,11 @@ export function PossibleFragmentSpreads( ) { context.reportError( new GraphQLError( - typeIncompatibleSpreadMessage(fragName, parentType, fragType), + typeIncompatibleSpreadMessage( + fragName, + inspect(parentType), + inspect(fragType), + ), [node], ), ); diff --git a/src/validation/rules/ProvidedRequiredArguments.js b/src/validation/rules/ProvidedRequiredArguments.js index b6b60a09bb3..9c4f26904cb 100644 --- a/src/validation/rules/ProvidedRequiredArguments.js +++ b/src/validation/rules/ProvidedRequiredArguments.js @@ -12,28 +12,27 @@ import { GraphQLError } from '../../error'; import inspect from '../../jsutils/inspect'; import keyMap from '../../jsutils/keyMap'; import { isNonNullType } from '../../type/definition'; -import type { GraphQLType } from '../../type/definition'; import type { ASTVisitor } from '../../language/visitor'; export function missingFieldArgMessage( fieldName: string, argName: string, - type: GraphQLType, + type: string, ): string { return ( `Field "${fieldName}" argument "${argName}" of type ` + - `"${inspect(type)}" is required but not provided.` + `"${type}" is required but not provided.` ); } export function missingDirectiveArgMessage( directiveName: string, argName: string, - type: GraphQLType, + type: string, ): string { return ( `Directive "@${directiveName}" argument "${argName}" of type ` + - `"${inspect(type)}" is required but not provided.` + `"${type}" is required but not provided.` ); } @@ -69,7 +68,7 @@ export function ProvidedRequiredArguments( missingFieldArgMessage( node.name.value, argDef.name, - argDef.type, + inspect(argDef.type), ), [node], ), @@ -101,7 +100,7 @@ export function ProvidedRequiredArguments( missingDirectiveArgMessage( node.name.value, argDef.name, - argDef.type, + inspect(argDef.type), ), [node], ), diff --git a/src/validation/rules/ScalarLeafs.js b/src/validation/rules/ScalarLeafs.js index 85f8f38fb1c..ba2818d00a5 100644 --- a/src/validation/rules/ScalarLeafs.js +++ b/src/validation/rules/ScalarLeafs.js @@ -12,25 +12,24 @@ import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { FieldNode } from '../../language/ast'; import { getNamedType, isLeafType } from '../../type/definition'; -import type { GraphQLType } from '../../type/definition'; import type { ASTVisitor } from '../../language/visitor'; export function noSubselectionAllowedMessage( fieldName: string, - type: GraphQLType, + type: string, ): string { return ( `Field "${fieldName}" must not have a selection since ` + - `type "${inspect(type)}" has no subfields.` + `type "${type}" has no subfields.` ); } export function requiredSubselectionMessage( fieldName: string, - type: GraphQLType, + type: string, ): string { return ( - `Field "${fieldName}" of type "${inspect(type)}" must have a ` + + `Field "${fieldName}" of type "${type}" must have a ` + `selection of subfields. Did you mean "${fieldName} { ... }"?` ); } @@ -51,7 +50,7 @@ export function ScalarLeafs(context: ValidationContext): ASTVisitor { if (selectionSet) { context.reportError( new GraphQLError( - noSubselectionAllowedMessage(node.name.value, type), + noSubselectionAllowedMessage(node.name.value, inspect(type)), [selectionSet], ), ); @@ -59,7 +58,7 @@ export function ScalarLeafs(context: ValidationContext): ASTVisitor { } else if (!selectionSet) { context.reportError( new GraphQLError( - requiredSubselectionMessage(node.name.value, type), + requiredSubselectionMessage(node.name.value, inspect(type)), [node], ), ); diff --git a/src/validation/rules/VariablesInAllowedPosition.js b/src/validation/rules/VariablesInAllowedPosition.js index 21c04d55700..b7eec382636 100644 --- a/src/validation/rules/VariablesInAllowedPosition.js +++ b/src/validation/rules/VariablesInAllowedPosition.js @@ -21,12 +21,12 @@ import type { GraphQLSchema } from '../../type/schema'; export function badVarPosMessage( varName: string, - varType: GraphQLType, - expectedType: GraphQLType, + varType: string, + expectedType: string, ): string { return ( - `Variable "$${varName}" of type "${inspect(varType)}" used in ` + - `position expecting type "${inspect(expectedType)}".` + `Variable "$${varName}" of type "${varType}" used in ` + + `position expecting type "${expectedType}".` ); } @@ -68,10 +68,10 @@ export function VariablesInAllowedPosition( ) ) { context.reportError( - new GraphQLError(badVarPosMessage(varName, varType, type), [ - varDef, - node, - ]), + new GraphQLError( + badVarPosMessage(varName, inspect(varType), inspect(type)), + [varDef, node], + ), ); } }