import { describe, it } from 'node:test';
import { expect } from 'chai';
import { expectJSON } from '../../__testUtils__/expectJSON.ts';
import { GraphQLError } from '../../error/GraphQLError.ts';
import type { DirectiveNode } from '../../language/ast.ts';
import { parse } from '../../language/parser.ts';
import { buildSchema } from '../../utilities/buildASTSchema.ts';
import { validate } from '../validate.ts';
import type { ValidationContext } from '../ValidationContext.ts';
import { testSchema } from './harness.ts';
describe('Validate: Supports full validation', () => {
it('validates queries', () => {
const doc = parse(`
query {
human {
pets {
... on Cat {
meowsVolume
}
... on Dog {
barkVolume
}
}
}
}
`);
const errors = validate(testSchema, doc);
expectJSON(errors).toDeepEqual([]);
});
it('detects unknown fields', () => {
const doc = parse(`
{
unknown
}
`);
const errors = validate(testSchema, doc);
expectJSON(errors).toDeepEqual([
{
locations: [{ line: 3, column: 9 }],
message: 'Cannot query field "unknown" on type "QueryRoot".',
},
]);
});
it('validates using a custom rule', () => {
const schema = buildSchema(`
directive @custom(arg: String) on FIELD
type Query {
foo: String
}
`);
const doc = parse(`
query {
name @custom
}
`);
function customRule(context: ValidationContext) {
return {
Directive(node: DirectiveNode) {
const directiveDef = context.getDirective();
const error = new GraphQLError(
'Reporting directive: ' + String(directiveDef),
{ nodes: node },
);
context.reportError(error);
},
};
}
const errors = validate(schema, doc, [customRule]);
expectJSON(errors).toDeepEqual([
{
message: 'Reporting directive: @custom',
locations: [{ line: 3, column: 14 }],
},
]);
});
});
describe('Validate: Limit maximum number of validation errors', () => {
const query = `
{
firstUnknownField
secondUnknownField
thirdUnknownField
}
`;
const doc = parse(query, { noLocation: true });
function validateDocument(options: { maxErrors?: number }) {
return validate(testSchema, doc, undefined, options);
}
function invalidFieldError(fieldName: string) {
return {
message: `Cannot query field "${fieldName}" on type "QueryRoot".`,
};
}
it('when maxErrors is equal to number of errors', () => {
const errors = validateDocument({ maxErrors: 3 });
expectJSON(errors).toDeepEqual([
invalidFieldError('firstUnknownField'),
invalidFieldError('secondUnknownField'),
invalidFieldError('thirdUnknownField'),
]);
});
it('when maxErrors is less than number of errors', () => {
const errors = validateDocument({ maxErrors: 2 });
expectJSON(errors).toDeepEqual([
invalidFieldError('firstUnknownField'),
invalidFieldError('secondUnknownField'),
{
message:
'Too many validation errors, error limit reached. Validation aborted.',
},
]);
});
it('passthrough exceptions from rules', () => {
function customRule() {
return {
Field() {
throw new Error('Error from custom rule!');
},
};
}
expect(() =>
validate(testSchema, doc, [customRule], { maxErrors: 1 }),
).to.throw(/^Error from custom rule!$/);
});
});
describe('operation and variable definition descriptions', () => {
it('validates operation with description and variable descriptions', () => {
const schema = buildSchema(
'type Query { field(a: Int, b: String): String }',
);
const query = `
"Operation description"
query myQuery(
"Variable a description"
$a: Int,
"""Variable b\nmultiline description"""
$b: String
) {
field(a: $a, b: $b)
}
`;
const ast = parse(query);
const errors = validate(schema, ast);
expect(errors.length).to.equal(0);
});
});