import { describe, it } from 'mocha';
import type { GraphQLSchema } from '../../type/schema.js';
import { buildSchema } from '../../utilities/buildASTSchema.js';
import {
KnownArgumentNamesOnDirectivesRule,
KnownArgumentNamesRule,
} from '../rules/KnownArgumentNamesRule.js';
import {
expectSDLValidationErrors,
expectValidationErrors,
} from './harness.js';
function expectErrors(queryStr: string) {
return expectValidationErrors(KnownArgumentNamesRule, queryStr);
}
function expectValid(queryStr: string) {
expectErrors(queryStr).toDeepEqual([]);
}
function expectSDLErrors(sdlStr: string, schema?: GraphQLSchema) {
return expectSDLValidationErrors(
schema,
KnownArgumentNamesOnDirectivesRule,
sdlStr,
);
}
function expectValidSDL(sdlStr: string) {
expectSDLErrors(sdlStr).toDeepEqual([]);
}
describe('Validate: Known argument names', () => {
it('single arg is known', () => {
expectValid(`
fragment argOnRequiredArg on Dog {
doesKnowCommand(dogCommand: SIT)
}
`);
});
it('multiple args are known', () => {
expectValid(`
fragment multipleArgs on ComplicatedArgs {
multipleReqs(req1: 1, req2: 2)
}
`);
});
it('ignores args of unknown fields', () => {
expectValid(`
fragment argOnUnknownField on Dog {
unknownField(unknownArg: SIT)
}
`);
});
it('multiple args in reverse order are known', () => {
expectValid(`
fragment multipleArgsReverseOrder on ComplicatedArgs {
multipleReqs(req2: 2, req1: 1)
}
`);
});
it('no args on optional arg', () => {
expectValid(`
fragment noArgOnOptionalArg on Dog {
isHouseTrained
}
`);
});
it('args are known deeply', () => {
expectValid(`
{
dog {
doesKnowCommand(dogCommand: SIT)
}
human {
pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
}
}
}
`);
});
it('directive args are known', () => {
expectValid(`
{
dog @skip(if: true)
}
`);
});
it('field args are invalid', () => {
expectErrors(`
{
dog @skip(unless: true)
}
`).toDeepEqual([
{
message: 'Unknown argument "unless" on directive "@skip".',
locations: [{ line: 3, column: 19 }],
},
]);
});
it('directive without args is valid', () => {
expectValid(`
{
dog @onField
}
`);
});
it('arg passed to directive without arg is reported', () => {
expectErrors(`
{
dog @onField(if: true)
}
`).toDeepEqual([
{
message: 'Unknown argument "if" on directive "@onField".',
locations: [{ line: 3, column: 22 }],
},
]);
});
it('misspelled directive args are reported', () => {
expectErrors(`
{
dog @skip(iff: true)
}
`).toDeepEqual([
{
message:
'Unknown argument "iff" on directive "@skip". Did you mean "if"?',
locations: [{ line: 3, column: 19 }],
},
]);
});
it('invalid arg name', () => {
expectErrors(`
fragment invalidArgName on Dog {
doesKnowCommand(unknown: true)
}
`).toDeepEqual([
{
message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".',
locations: [{ line: 3, column: 25 }],
},
]);
});
it('misspelled arg name is reported', () => {
expectErrors(`
fragment invalidArgName on Dog {
doesKnowCommand(DogCommand: true)
}
`).toDeepEqual([
{
message:
'Unknown argument "DogCommand" on field "Dog.doesKnowCommand". Did you mean "dogCommand"?',
locations: [{ line: 3, column: 25 }],
},
]);
});
it('unknown args amongst known args', () => {
expectErrors(`
fragment oneGoodArgOneInvalidArg on Dog {
doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true)
}
`).toDeepEqual([
{
message: 'Unknown argument "whoKnows" on field "Dog.doesKnowCommand".',
locations: [{ line: 3, column: 25 }],
},
{
message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".',
locations: [{ line: 3, column: 55 }],
},
]);
});
it('unknown args deeply', () => {
expectErrors(`
{
dog {
doesKnowCommand(unknown: true)
}
human {
pet {
... on Dog {
doesKnowCommand(unknown: true)
}
}
}
}
`).toDeepEqual([
{
message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".',
locations: [{ line: 4, column: 27 }],
},
{
message: 'Unknown argument "unknown" on field "Dog.doesKnowCommand".',
locations: [{ line: 9, column: 31 }],
},
]);
});
describe('within SDL', () => {
it('known arg on directive defined inside SDL', () => {
expectValidSDL(`
type Query {
foo: String @test(arg: "")
}
directive @test(arg: String) on FIELD_DEFINITION
`);
});
it('unknown arg on directive defined inside SDL', () => {
expectSDLErrors(`
type Query {
foo: String @test(unknown: "")
}
directive @test(arg: String) on FIELD_DEFINITION
`).toDeepEqual([
{
message: 'Unknown argument "unknown" on directive "@test".',
locations: [{ line: 3, column: 29 }],
},
]);
});
it('misspelled arg name is reported on directive defined inside SDL', () => {
expectSDLErrors(`
type Query {
foo: String @test(agr: "")
}
directive @test(arg: String) on FIELD_DEFINITION
`).toDeepEqual([
{
message:
'Unknown argument "agr" on directive "@test". Did you mean "arg"?',
locations: [{ line: 3, column: 29 }],
},
]);
});
it('unknown arg on standard directive', () => {
expectSDLErrors(`
type Query {
foo: String @deprecated(unknown: "")
}
`).toDeepEqual([
{
message: 'Unknown argument "unknown" on directive "@deprecated".',
locations: [{ line: 3, column: 35 }],
},
]);
});
it('unknown arg on overridden standard directive', () => {
expectSDLErrors(`
type Query {
foo: String @deprecated(reason: "")
}
directive @deprecated(arg: String) on FIELD
`).toDeepEqual([
{
message: 'Unknown argument "reason" on directive "@deprecated".',
locations: [{ line: 3, column: 35 }],
},
]);
});
it('unknown 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(unknown: "")
`,
schema,
).toDeepEqual([
{
message: 'Unknown argument "unknown" on directive "@test".',
locations: [{ line: 4, column: 36 }],
},
]);
});
it('unknown 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(unknown: "")
`,
schema,
).toDeepEqual([
{
message: 'Unknown argument "unknown" on directive "@test".',
locations: [{ line: 2, column: 35 }],
},
]);
});
});
});