graphql-language-service
Version:
The official, runtime independent Language Service for GraphQL
1,022 lines (952 loc) • 30.6 kB
text/typescript
/**
* Copyright (c) 2021 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import {
AutocompleteSuggestionOptions,
CompletionItem,
} from 'graphql-language-service';
import fs from 'node:fs';
import {
buildSchema,
FragmentDefinitionNode,
GraphQLSchema,
parse,
version as graphQLVersion,
GraphQLString,
GraphQLInt,
GraphQLBoolean,
GraphQLDeprecatedDirective,
GraphQLSkipDirective,
GraphQLIncludeDirective,
} from 'graphql';
import { Position } from '../../utils';
import path from 'node:path';
import { getAutocompleteSuggestions } from '../getAutocompleteSuggestions';
// import { InsertTextFormat } from 'vscode-languageserver-types';
const expectedResults = {
droid: {
label: 'droid',
detail: 'Droid',
},
hero: {
label: 'hero',
detail: 'Character',
},
human: {
label: 'human',
detail: 'Human',
},
inputTypeTest: {
label: 'inputTypeTest',
detail: 'TestType',
},
appearsIn: {
label: 'appearsIn',
detail: '[Episode]',
},
friends: {
label: 'friends',
detail: '[Character]',
},
union: {
label: 'union',
detail: 'TestUnion',
},
__typename: {
label: '__typename',
detail: 'String!',
documentation: 'The name of the current Object type at runtime.',
},
};
const suggestionCommand = {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
};
describe('getAutocompleteSuggestions', () => {
let schema: GraphQLSchema;
beforeEach(async () => {
// graphQLVersion = pkg.version;
const schemaIDL = fs.readFileSync(
path.join(__dirname, '__schema__/StarWarsSchema.graphql'),
'utf8',
);
schema = buildSchema(schemaIDL);
});
// Returns a sorted autocomplete suggestions in an increasing order.
function testSuggestions(
query: string,
point: Position,
externalFragments?: FragmentDefinitionNode[],
options?: AutocompleteSuggestionOptions & { ignoreInsert?: boolean },
): Array<CompletionItem> {
return getAutocompleteSuggestions(
schema,
query,
point,
undefined,
externalFragments,
options,
)
.filter(field => !['__schema', '__type'].includes(field.label))
.sort((a, b) => a.label.localeCompare(b.label))
.map(suggestion => {
// TODO: A PR where we do `const { type, ..rest} = suggestion; return rest;`
// and validate the entire completion object - kinds, documentation, etc
const response = { label: suggestion.label } as CompletionItem;
if (suggestion.detail) {
response.detail = String(suggestion.detail);
}
if (suggestion.insertText && !options?.ignoreInsert) {
response.insertText = suggestion.insertText;
}
if (suggestion.insertTextFormat && !options?.ignoreInsert) {
response.insertTextFormat = suggestion.insertTextFormat;
}
if (suggestion.command && !options?.ignoreInsert) {
response.command = suggestion.command;
}
if (suggestion.documentation?.length) {
response.documentation = suggestion.documentation;
}
if (suggestion.labelDetails && !options?.ignoreInsert) {
response.labelDetails = suggestion.labelDetails;
}
return response;
});
}
describe('with Operation types', () => {
const expectedDirectiveSuggestions = [
{ label: 'include', documentation: GraphQLIncludeDirective.description },
{ label: 'skip', documentation: GraphQLSkipDirective.description },
];
// TODO: remove this once defer and stream are merged to `graphql`
if (graphQLVersion.startsWith('16.0.0-experimental-stream-defer')) {
// @ts-expect-error
expectedDirectiveSuggestions.push({ label: 'stream' }, { label: 'test' });
} else {
// @ts-expect-error
expectedDirectiveSuggestions.push({ label: 'test' });
}
it('provides correct sortText response', () => {
const result = getAutocompleteSuggestions(
schema,
'{ h',
new Position(0, 3),
).map(({ sortText, label, detail }) => ({ sortText, label, detail }));
expect(result).toEqual([
{
sortText: '0hero',
label: 'hero',
detail: 'Character',
},
{
sortText: '1human',
label: 'human',
detail: 'Human',
},
{
sortText: '7__schema',
label: '__schema',
detail: '__Schema!',
},
]);
});
it('provides correct initial keywords', () => {
expect(testSuggestions('', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'extend' },
{ label: 'fragment' },
{ label: 'input' },
{ label: 'interface' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'scalar' },
{ label: 'schema' },
{ label: 'subscription' },
{ label: 'type' },
{ label: 'union' },
]);
expect(testSuggestions('q', new Position(0, 1))).toEqual([
{ label: '{' },
{ label: 'query' },
]);
});
it('provides correct top level suggestions when a simple query is already present', () => {
// Below should provide initial keywords
expect(testSuggestions(' { id }', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'fragment' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'subscription' },
]);
// Below should provide root field names
expect(
testSuggestions(' {}', new Position(0, 2), [], {
ignoreInsert: true,
}),
).toEqual([
expectedResults.__typename,
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
expectedResults.union,
]);
// Test for query text with empty lines
expect(
testSuggestions(
`
query name {
...testFragment
}
`,
new Position(2, 0),
[],
{
ignoreInsert: true,
},
),
).toEqual([
expectedResults.__typename,
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
expectedResults.union,
]);
});
it('provides correct field name suggestions', () => {
const result = testSuggestions('{ ', new Position(0, 2), [], {
ignoreInsert: true,
});
expect(result).toEqual([
expectedResults.__typename,
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
expectedResults.union,
]);
});
it('provides correct field name suggestions after filtered', () => {
const result = testSuggestions('{ h ', new Position(0, 3), [], {
ignoreInsert: true,
});
expect(result).toEqual([expectedResults.hero, expectedResults.human]);
});
it('provides correct field name suggestions with alias', () => {
const result = testSuggestions(
'{ alias: human(id: "1") { ',
new Position(0, 26),
[],
{
ignoreInsert: true,
},
);
expect(result).toEqual([
expectedResults.__typename,
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
it('provides correct field name suggestions with insertText', () => {
const result = testSuggestions('{ ', new Position(0, 2), [], {
ignoreInsert: false,
fillLeafsOnComplete: true,
});
expect(result).toEqual([
{
...expectedResults.__typename,
command: suggestionCommand,
insertTextFormat: 2,
insertText: '__typename\n',
labelDetails: { detail: ' String!' },
},
{
...expectedResults.droid,
command: suggestionCommand,
insertTextFormat: 2,
insertText: 'droid(id: $1) {\n $1\n}',
labelDetails: { detail: ' Droid' },
},
{
...expectedResults.hero,
command: suggestionCommand,
insertTextFormat: 2,
insertText: 'hero {\n $1\n}',
labelDetails: { detail: ' Character' },
},
{
...expectedResults.human,
command: suggestionCommand,
insertTextFormat: 2,
insertText: 'human(id: $1) {\n $1\n}',
labelDetails: { detail: ' Human' },
},
{
...expectedResults.inputTypeTest,
command: suggestionCommand,
insertTextFormat: 2,
insertText: 'inputTypeTest {\n $1\n}',
labelDetails: { detail: ' TestType' },
},
{
label: 'union',
insertTextFormat: 2,
insertText: 'union {\n $1\n}',
detail: 'TestUnion',
command: suggestionCommand,
labelDetails: {
detail: ' TestUnion',
},
},
]);
});
it('provides correct type suggestions for fragments', () => {
const result = testSuggestions('fragment test on ', new Position(0, 17));
expect(result).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
{ label: 'Query' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]);
});
it('provides correct field suggestions for fragments', () => {
const result = testSuggestions(
'fragment test on Human { ',
new Position(0, 25),
[],
{
ignoreInsert: true,
},
);
expect(result).toEqual([
expectedResults.__typename,
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
it('provides correct argument suggestions', () => {
const result = testSuggestions('{ human (', new Position(0, 9));
expect(result).toEqual([
{
label: 'id',
insertText: 'id: ',
command: suggestionCommand,
insertTextFormat: 2,
labelDetails: { detail: ' String!' },
},
]);
});
it('provides correct argument suggestions when using aliases', () => {
const result = testSuggestions(
'{ aliasTest: human( ',
new Position(0, 20),
);
expect(result).toEqual([
{
label: 'id',
command: suggestionCommand,
insertText: 'id: ',
insertTextFormat: 2,
labelDetails: { detail: ' String!' },
},
]);
});
const metaArgs = [
{
label: '__DirectiveLocation',
documentation:
'A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.',
},
{
label: '__TypeKind',
documentation:
'An enum describing what kind of type a given `__Type` is.',
},
];
it('provides correct input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: ) { ',
new Position(0, 24),
);
expect(result).toEqual([
...metaArgs,
{ label: 'Boolean', documentation: GraphQLBoolean.description },
{ label: 'Episode' },
{ label: 'InputType' },
{ label: 'Int', documentation: GraphQLInt.description },
{ label: 'String', documentation: GraphQLString.description },
]);
});
it('provides filtered input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: In) { ',
new Position(0, 26),
);
expect(result).toEqual([
...metaArgs,
{ label: 'InputType' },
{ label: 'Int', documentation: GraphQLInt.description },
{ label: 'String', documentation: GraphQLString.description },
]);
});
it('provides correct typeCondition suggestions', () => {
const suggestionsOnQuery = testSuggestions(
'{ ... on ',
new Position(0, 9),
);
expect(
suggestionsOnQuery.filter(({ label }) => !label.startsWith('__')),
).toEqual([{ label: 'Query' }]);
const suggestionsOnCompositeType = testSuggestions(
'{ hero(episode: JEDI) { ... on } }',
new Position(0, 31),
);
expect(suggestionsOnCompositeType).toEqual([
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
]);
expect(
testSuggestions(
'fragment Foo on Character { ... on }',
new Position(0, 35),
),
).toEqual([
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
]);
});
it('provides correct typeCondition suggestions on fragment', () => {
const result = testSuggestions('fragment Foo on {}', new Position(0, 16));
expect(result.filter(({ label }) => !label.startsWith('__'))).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
{ label: 'Query' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]);
});
it('provides correct enum suggestions', () => {
const result = testSuggestions('{ hero(episode: ', new Position(0, 16));
expect(result).toEqual([
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
]);
});
it('provides correct suggestions for declared variables upon typing $', () => {
const result = testSuggestions(
'query($id: String, $ep: Episode!){ hero(episode: $ }',
new Position(0, 51),
);
expect(result).toEqual([
{ label: '$ep', insertText: 'ep', detail: 'Episode' },
]);
});
it('provides correct suggestions for variables based on argument context', () => {
const result = testSuggestions(
'query($id: String!, $episode: Episode!){ hero(episode: ',
new Position(0, 55),
);
expect(result).toEqual([
{ label: '$episode', detail: 'Episode', insertText: '$episode' },
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
// no $id here, it's not compatible :P
]);
});
it('provides fragment name suggestion', () => {
const fragmentDef = 'fragment Foo on Human { id }';
// Test on concrete types
expect(
testSuggestions(
`${fragmentDef} query { human(id: "1") { ...`,
new Position(0, 57),
),
).toEqual([
{
label: 'Foo',
detail: 'Human',
documentation: 'fragment Foo on Human',
labelDetails: { detail: 'fragment Foo on Human' },
},
]);
expect(
testSuggestions(
`query { human(id: "1") { ... }} ${fragmentDef}`,
new Position(0, 28),
),
).toEqual([
{
label: 'Foo',
detail: 'Human',
documentation: 'fragment Foo on Human',
labelDetails: { detail: 'fragment Foo on Human' },
},
]);
// Test on abstract type
expect(
testSuggestions(
`${fragmentDef} query { hero(episode: JEDI) { ...`,
new Position(0, 62),
),
).toEqual([
{
label: 'Foo',
detail: 'Human',
documentation: 'fragment Foo on Human',
labelDetails: { detail: 'fragment Foo on Human' },
},
]);
});
it('provides correct fragment name suggestions for external fragments', () => {
const externalFragments = parse(`
fragment CharacterDetails on Human {
name
}
fragment CharacterDetails2 on Human {
name
}
`).definitions as FragmentDefinitionNode[];
const result = testSuggestions(
'query { human(id: "1") { ... }}',
new Position(0, 28),
externalFragments,
);
expect(result).toEqual([
{
label: 'CharacterDetails',
detail: 'Human',
documentation: 'fragment CharacterDetails on Human',
labelDetails: { detail: 'fragment CharacterDetails on Human' },
},
{
label: 'CharacterDetails2',
detail: 'Human',
documentation: 'fragment CharacterDetails2 on Human',
labelDetails: { detail: 'fragment CharacterDetails2 on Human' },
},
]);
});
it('provides correct directive suggestions', () => {
expect(testSuggestions('{ test @ }', new Position(0, 8))).toEqual(
expectedDirectiveSuggestions,
);
expect(testSuggestions('{ test @', new Position(0, 8))).toEqual(
expectedDirectiveSuggestions,
);
expect(
testSuggestions('{ aliasTest: test @ }', new Position(0, 19)),
).toEqual(expectedDirectiveSuggestions);
expect(testSuggestions('query @', new Position(0, 7))).toEqual([]);
});
it('provides correct directive field suggestions', () => {
expect(
testSuggestions('{ test @deprecated(', new Position(0, 19)),
).toEqual([
{
command: suggestionCommand,
label: 'reason',
insertTextFormat: 2,
insertText: 'reason: ',
documentation: GraphQLDeprecatedDirective.args[0].description,
labelDetails: {
detail: ' String',
},
},
]);
});
const inputArgs = [
{
label: 'key',
detail: 'String!',
insertText: 'key: ',
insertTextFormat: 2,
command: suggestionCommand,
},
{
detail: 'InputType',
label: 'obj',
insertText: 'obj: {\n $1\n}',
command: suggestionCommand,
insertTextFormat: 2,
},
{
label: 'value',
detail: 'Int',
insertText: 'value: ',
insertTextFormat: 2,
command: suggestionCommand,
},
];
it('provides correct testInput type field suggestions', () => {
expect(
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)),
).toEqual(inputArgs);
});
it('provides correct nested testInput type field suggestions', () => {
expect(
testSuggestions('{ inputTypeTest(args: { obj: {', new Position(0, 30)),
).toEqual(inputArgs);
});
it('provides correct field name suggestion inside inline fragment', () => {
expect(
testSuggestions(
'fragment Foo on Character { ... on Human { }}',
new Position(0, 42),
[],
{
ignoreInsert: true,
},
),
).toEqual([
expectedResults.__typename,
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
// Type-less inline fragment assumes the type automatically
expect(
testSuggestions(
'fragment Foo on Droid { ... { ',
new Position(0, 30),
[],
{
ignoreInsert: true,
},
),
).toEqual([
expectedResults.__typename,
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'instructions', detail: '[String]!' },
{ label: 'name', detail: 'String' },
{ label: 'primaryFunction', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
});
describe('with SDL types', () => {
it('provides correct initial keywords w/ graphqls', () => {
expect(
testSuggestions('', new Position(0, 0), [], { uri: 'schema.graphqls' }),
).toEqual([
{ label: 'extend' },
{ label: 'input' },
{ label: 'interface' },
{ label: 'scalar' },
{ label: 'schema' },
{ label: 'type' },
{ label: 'union' },
]);
});
it('provides correct initial keywords w/out graphqls', () => {
expect(
testSuggestions('', new Position(0, 0), [], { uri: 'schema.graphql' }),
).toEqual([
{ label: '{' },
{ label: 'extend' },
{ label: 'fragment' },
{ label: 'input' },
{ label: 'interface' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'scalar' },
{ label: 'schema' },
{ label: 'subscription' },
{ label: 'type' },
{ label: 'union' },
]);
});
it('provides correct initial definition keywords', () => {
expect(
testSuggestions('type Type { field: String }\n\n', new Position(0, 31)),
).toEqual([
{ label: 'extend' },
{ label: 'input' },
{ label: 'interface' },
{ label: 'scalar' },
{ label: 'schema' },
{ label: 'type' },
{ label: 'union' },
]);
});
it('provides correct extension keywords', () => {
expect(testSuggestions('extend ', new Position(0, 7))).toEqual([
{ label: 'input' },
{ label: 'interface' },
{ label: 'scalar' },
{ label: 'schema' },
{ label: 'type' },
{ label: 'union' },
]);
});
it('provides scalars to be extended', () => {
expect(testSuggestions('extend scalar ', new Position(0, 14))).toEqual([
{ label: 'Boolean' },
{ label: 'Int' },
{ label: 'String' },
]);
});
it('provides object types to be extended', () => {
expect(testSuggestions('extend type ', new Position(0, 12))).toEqual([
{ label: 'Droid' },
{ label: 'Human' },
{ label: 'Query' },
{ label: 'TestType' },
]);
});
it('does not provide object type names once extending a type', () => {
expect(
testSuggestions('extend type Query {', new Position(0, 19)),
).toEqual([]);
});
it('provides interfaces to be extended', () => {
expect(testSuggestions('extend interface ', new Position(0, 17))).toEqual(
[
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
],
);
});
it('provides unions to be extended', () => {
expect(testSuggestions('extend union ', new Position(0, 13))).toEqual([
{ label: 'TestUnion' },
]);
});
it('provides enums to be extended', () => {
expect(testSuggestions('extend enum ', new Position(0, 12))).toEqual([
{ label: 'Episode' },
]);
});
it('provides input objects to be extended', () => {
expect(testSuggestions('extend input ', new Position(0, 13))).toEqual([
{ label: 'InputType' },
]);
});
it('provides correct directive suggestions on definitions', () =>
expect(testSuggestions('type Type @', new Position(0, 11))).toEqual([
{ label: 'onAllDefs' },
]));
it('provides correct suggestions on object field w/ .graphqls', () =>
expect(
testSuggestions('type Type {\n aField: s', new Position(0, 23), [], {
uri: 'schema.graphqls',
ignoreInsert: true,
}),
).toEqual([
{ label: 'Episode' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
it('provides correct argument type suggestions on directive definitions', () =>
expect(
testSuggestions(
'directive @skip(if: ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT',
new Position(0, 19),
[],
{
ignoreInsert: true,
},
),
).toEqual([
{ label: 'Boolean' },
{ label: 'Episode' },
{ label: 'InputType' },
{ label: 'Int' },
{ label: 'String' },
]));
it('provides correct suggestions on object fields', () =>
expect(
testSuggestions('type Type {\n aField: s', new Position(0, 23), [], {
uri: 'schema.graphql',
ignoreInsert: true,
}),
).toEqual([
{ label: 'Episode' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
// TODO: shouldn't TestType and TestUnion be available here?
it('provides correct filtered suggestions on object fields in regular SDL files', () =>
expect(
testSuggestions('type Type {\n aField: s', new Position(0, 23), [], {
uri: 'schema.graphql',
ignoreInsert: true,
}),
).toEqual([
{ label: 'Episode' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
it('provides correct unfiltered suggestions on object fields in regular SDL files', () =>
expect(
testSuggestions('type Type {\n aField: ', new Position(0, 22), [], {
uri: 'schema.graphql',
ignoreInsert: true,
}),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Boolean' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Episode' },
{ label: 'Human' },
{ label: 'Int' },
// TODO: maybe filter out types attached to top level schema?
{ label: 'Query' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
it('provides correct suggestions on object fields that are arrays', () =>
expect(
testSuggestions('type Type {\n aField: []', new Position(0, 25), [], {
uri: 'schema.graphqls',
ignoreInsert: true,
}),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Boolean' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Episode' },
{ label: 'Human' },
{ label: 'Int' },
{ label: 'Query' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
it('provides correct suggestions on object fields that are arrays in SDL context', () =>
expect(
testSuggestions('type Type {\n aField: []', new Position(0, 25), [], {
uri: 'schema.graphql',
ignoreInsert: true,
}),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Boolean' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Episode' },
{ label: 'Human' },
{ label: 'Int' },
{ label: 'Query' },
{ label: 'String' },
{ label: 'TestInterface' },
{ label: 'TestType' },
{ label: 'TestUnion' },
]));
it('provides correct suggestions on input object fields', () =>
expect(
testSuggestions('input Type {\n aField: s', new Position(0, 23), [], {
uri: 'schema.graphqls',
}),
).toEqual([
{ label: 'Episode' },
{ label: 'String', documentation: GraphQLString.description },
]));
it('provides correct directive suggestions on args definitions', () =>
expect(
testSuggestions('type Type { field(arg: String @', new Position(0, 31)),
).toEqual([
{
label: 'deprecated',
documentation: GraphQLDeprecatedDirective.description,
},
{ label: 'onAllDefs' },
{ label: 'onArg' },
]));
it('provides correct interface suggestions when extending with an interface', () =>
expect(
testSuggestions('type Type implements ', new Position(0, 20)),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
it('provides correct interface suggestions when extending a type with multiple interfaces', () =>
expect(
testSuggestions(
'type Type implements TestInterface & ',
new Position(0, 37),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides correct interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & ',
new Position(0, 46),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides filtered interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & Inter',
new Position(0, 48),
),
).toEqual([{ label: 'AnotherInterface' }]));
it('provides no interface suggestions when using implements and there are no & or { characters present', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface ',
new Position(0, 44),
),
).toEqual([]));
it('provides fragment completion after a list of interfaces to extend', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & AnotherInterface @f',
new Position(0, 65),
),
).toEqual([{ label: 'onAllDefs' }]));
it('provides correct interface suggestions when extending an interface with an inline interface', () =>
expect(
testSuggestions(
'interface A { id: String }\ninterface MyInterface implements ',
new Position(1, 33),
),
).toEqual([
{ label: 'A' },
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
});
});