import { describe, it } from 'mocha';
import type { GraphQLSchema } from '../../type/schema.js';
import { buildSchema } from '../../utilities/buildASTSchema.js';
import {
ProvidedRequiredArgumentsOnDirectivesRule,
ProvidedRequiredArgumentsRule,
} from '../rules/ProvidedRequiredArgumentsRule.js';
import {
expectSDLValidationErrors,
expectValidationErrors,
} from './harness.js';
function expectErrors(queryStr: string) {
return expectValidationErrors(ProvidedRequiredArgumentsRule, queryStr);
}
function expectValid(queryStr: string) {
expectErrors(queryStr).toDeepEqual([]);
}
function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) {
return expectSDLValidationErrors(
schema,
ProvidedRequiredArgumentsOnDirectivesRule,
sdlStr,
);
}
function expectValidSDL(sdlStr: string) {
expectSDLErrors(sdlStr).toDeepEqual([]);
}
describe('Validate: Provided required arguments', () => {
it('ignores unknown arguments', () => {
expectValid(`
{
dog {
isHouseTrained(unknownArgument: true)
}
}
`);
});
describe('Valid non-nullable value', () => {
it('Arg on optional arg', () => {
expectValid(`
{
dog {
isHouseTrained(atOtherHomes: true)
}
}
`);
});
it('No Arg on optional arg', () => {
expectValid(`
{
dog {
isHouseTrained
}
}
`);
});
it('No arg on non-null field with default', () => {
expectValid(`
{
complicatedArgs {
nonNullFieldWithDefault
}
}
`);
});
it('Multiple args', () => {
expectValid(`
{
complicatedArgs {
multipleReqs(req1: 1, req2: 2)
}
}
`);
});
it('Multiple args reverse order', () => {
expectValid(`
{
complicatedArgs {
multipleReqs(req2: 2, req1: 1)
}
}
`);
});
it('No args on multiple optional', () => {
expectValid(`
{
complicatedArgs {
multipleOpts
}
}
`);
});
it('One arg on multiple optional', () => {
expectValid(`
{
complicatedArgs {
multipleOpts(opt1: 1)
}
}
`);
});
it('Second arg on multiple optional', () => {
expectValid(`
{
complicatedArgs {
multipleOpts(opt2: 1)
}
}
`);
});
it('Multiple required args on mixedList', () => {
expectValid(`
{
complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4)
}
}
`);
});
it('Multiple required and one optional arg on mixedList', () => {
expectValid(`
{
complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
}
}
`);
});
it('All required and optional args on mixedList', () => {
expectValid(`
{
complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
}
}
`);
});
});
describe('Invalid non-nullable value', () => {
it('Missing one non-nullable argument', () => {
expectErrors(`
{
complicatedArgs {
multipleReqs(req2: 2)
}
}
`).toDeepEqual([
{
message:
'Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided.',
locations: [{ line: 4, column: 13 }],
},
]);
});
it('Missing multiple non-nullable arguments', () => {
expectErrors(`
{
complicatedArgs {
multipleReqs
}
}
`).toDeepEqual([
{
message:
'Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided.',
locations: [{ line: 4, column: 13 }],
},
{
message:
'Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided.',
locations: [{ line: 4, column: 13 }],
},
]);
});
it('Incorrect value and missing argument', () => {
expectErrors(`
{
complicatedArgs {
multipleReqs(req1: "one")
}
}
`).toDeepEqual([
{
message:
'Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided.',
locations: [{ line: 4, column: 13 }],
},
]);
});
});
describe('Directive arguments', () => {
it('ignores unknown directives', () => {
expectValid(`
{
dog @unknown
}
`);
});
it('with directives of valid types', () => {
expectValid(`
{
dog @include(if: true) {
name
}
human @skip(if: false) {
name
}
}
`);
});
it('with directive with missing types', () => {
expectErrors(`
{
dog @include {
name @skip
}
}
`).toDeepEqual([
{
message:
'Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided.',
locations: [{ line: 3, column: 15 }],
},
{
message:
'Directive "@skip" argument "if" of type "Boolean!" is required, but it was not provided.',
locations: [{ line: 4, column: 18 }],
},
]);
});
});
describe('within SDL', () => {
it('Missing optional args on directive defined inside SDL', () => {
expectValidSDL(`
type Query {
foo: String @test
}
directive @test(arg1: String, arg2: String! = "") on FIELD_DEFINITION
`);
});
it('Missing arg on directive defined inside SDL', () => {
expectSDLErrors(`
type Query {
foo: String @test
}
directive @test(arg: String!) on FIELD_DEFINITION
`).toDeepEqual([
{
message:
'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.',
locations: [{ line: 3, column: 23 }],
},
]);
});
it('Missing arg on standard directive', () => {
expectSDLErrors(`
type Query {
foo: String @include
}
`).toDeepEqual([
{
message:
'Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided.',
locations: [{ line: 3, column: 23 }],
},
]);
});
it('Missing arg on overridden standard directive', () => {
expectSDLErrors(`
type Query {
foo: String @deprecated
}
directive @deprecated(reason: String!) on FIELD
`).toDeepEqual([
{
message:
'Directive "@deprecated" argument "reason" of type "String!" is required, but it was not provided.',
locations: [{ line: 3, column: 23 }],
},
]);
});
it('Missing arg on directive defined in schema extension', () => {
const schema = buildSchema(`
type Query {
foo: String
}
`);
expectSDLErrors(
`
directive @test(arg: String!) on OBJECT
extend type Query @test
`,
schema,
).toDeepEqual([
{
message:
'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.',
locations: [{ line: 4, column: 30 }],
},
]);
});
it('Missing arg on directive used in schema extension', () => {
const schema = buildSchema(`
directive @test(arg: String!) on OBJECT
type Query {
foo: String
}
`);
expectSDLErrors(
`
extend type Query @test
`,
schema,
).toDeepEqual([
{
message:
'Directive "@test" argument "arg" of type "String!" is required, but it was not provided.',
locations: [{ line: 2, column: 29 }],
},
]);
});
});
});