Skip to content

Commit 649344e

Browse files
committed
Tests for visitWithTypeInfo, support for editing
Related GraphQL-js commit: graphql/graphql-js@32a5492
1 parent a509dab commit 649344e

File tree

5 files changed

+188
-4
lines changed

5 files changed

+188
-4
lines changed

graphql/core/language/visitor.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,13 @@ def __init__(self, type_info, visitor):
213213
def enter(self, node, key, parent, path, ancestors):
214214
self.type_info.enter(node)
215215
result = self.visitor.enter(node, key, parent, path, ancestors)
216-
if result is False:
216+
if result is not None:
217217
self.type_info.leave(node)
218-
return False
218+
if isinstance(result, ast.Node):
219+
self.type_info.enter(result)
220+
return result
219221

220222
def leave(self, node, key, parent, path, ancestors):
221-
self.visitor.leave(node, key, parent, path, ancestors)
223+
result = self.visitor.leave(node, key, parent, path, ancestors)
222224
self.type_info.leave(node)
225+
return result

graphql/core/type/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
GraphQLInputObjectField,
1313
GraphQLList,
1414
GraphQLNonNull,
15+
get_named_type,
1516
is_abstract_type,
1617
is_composite_type,
1718
is_input_type,

tests/core_language/test_visitor.py

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
from graphql.core.language.ast import Field, Name, SelectionSet
22
from graphql.core.language.parser import parse
3+
from graphql.core.language.printer import print_ast
34
from graphql.core.language.visitor import (
4-
BREAK, REMOVE, Visitor, visit, ParallelVisitor)
5+
BREAK, REMOVE, Visitor, visit, ParallelVisitor, TypeInfoVisitor)
6+
from graphql.core.utils.type_info import TypeInfo
7+
from graphql.core.type import is_composite_type, get_named_type
58

69
from .fixtures import KITCHEN_SINK
10+
from ..core_validation.utils import test_schema
711

812

913
def test_allows_for_editing_on_enter():
@@ -786,3 +790,178 @@ def leave(self, node, key, parent, *args):
786790
['leave', 'OperationDefinition', None],
787791
['leave', 'Document', None]
788792
]
793+
794+
795+
def test_visits_with_typeinfo_maintains_type_info_during_visit():
796+
visited = []
797+
ast = parse('{ human(id: 4) { name, pets { name }, unknown } }')
798+
799+
type_info = TypeInfo(test_schema)
800+
801+
class TestVisitor(Visitor):
802+
803+
def enter(self, node, key, parent, *args):
804+
parent_type = type_info.get_parent_type()
805+
_type = type_info.get_type()
806+
input_type = type_info.get_input_type()
807+
visited.append([
808+
'enter',
809+
type(node).__name__,
810+
node.value if type(node).__name__ == "Name" else None,
811+
str(parent_type) if parent_type else None,
812+
str(_type) if _type else None,
813+
str(input_type) if input_type else None
814+
])
815+
816+
def leave(self, node, key, parent, *args):
817+
parent_type = type_info.get_parent_type()
818+
_type = type_info.get_type()
819+
input_type = type_info.get_input_type()
820+
visited.append([
821+
'leave',
822+
type(node).__name__,
823+
node.value if type(node).__name__ == "Name" else None,
824+
str(parent_type) if parent_type else None,
825+
str(_type) if _type else None,
826+
str(input_type) if input_type else None
827+
])
828+
829+
visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
830+
assert visited == [
831+
['enter', 'Document', None, None, None, None],
832+
['enter', 'OperationDefinition', None, None, 'QueryRoot', None],
833+
['enter', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None],
834+
['enter', 'Field', None, 'QueryRoot', 'Human', None],
835+
['enter', 'Name', 'human', 'QueryRoot', 'Human', None],
836+
['leave', 'Name', 'human', 'QueryRoot', 'Human', None],
837+
['enter', 'Argument', None, 'QueryRoot', 'Human', 'ID'],
838+
['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
839+
['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
840+
['enter', 'IntValue', None, 'QueryRoot', 'Human', 'ID'],
841+
['leave', 'IntValue', None, 'QueryRoot', 'Human', 'ID'],
842+
['leave', 'Argument', None, 'QueryRoot', 'Human', 'ID'],
843+
['enter', 'SelectionSet', None, 'Human', 'Human', None],
844+
['enter', 'Field', None, 'Human', 'String', None],
845+
['enter', 'Name', 'name', 'Human', 'String', None],
846+
['leave', 'Name', 'name', 'Human', 'String', None],
847+
['leave', 'Field', None, 'Human', 'String', None],
848+
['enter', 'Field', None, 'Human', '[Pet]', None],
849+
['enter', 'Name', 'pets', 'Human', '[Pet]', None],
850+
['leave', 'Name', 'pets', 'Human', '[Pet]', None],
851+
['enter', 'SelectionSet', None, 'Pet', '[Pet]', None],
852+
['enter', 'Field', None, 'Pet', 'String', None],
853+
['enter', 'Name', 'name', 'Pet', 'String', None],
854+
['leave', 'Name', 'name', 'Pet', 'String', None],
855+
['leave', 'Field', None, 'Pet', 'String', None],
856+
['leave', 'SelectionSet', None, 'Pet', '[Pet]', None],
857+
['leave', 'Field', None, 'Human', '[Pet]', None],
858+
['enter', 'Field', None, 'Human', None, None],
859+
['enter', 'Name', 'unknown', 'Human', None, None],
860+
['leave', 'Name', 'unknown', 'Human', None, None],
861+
['leave', 'Field', None, 'Human', None, None],
862+
['leave', 'SelectionSet', None, 'Human', 'Human', None],
863+
['leave', 'Field', None, 'QueryRoot', 'Human', None],
864+
['leave', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None],
865+
['leave', 'OperationDefinition', None, None, 'QueryRoot', None],
866+
['leave', 'Document', None, None, None, None]
867+
]
868+
869+
870+
def test_visits_with_typeinfo_maintains_type_info_during_edit():
871+
visited = []
872+
ast = parse('{ human(id: 4) { name, pets }, alien }')
873+
874+
type_info = TypeInfo(test_schema)
875+
876+
class TestVisitor(Visitor):
877+
878+
def enter(self, node, key, parent, *args):
879+
parent_type = type_info.get_parent_type()
880+
_type = type_info.get_type()
881+
input_type = type_info.get_input_type()
882+
visited.append([
883+
'enter',
884+
type(node).__name__,
885+
node.value if type(node).__name__ == "Name" else None,
886+
str(parent_type) if parent_type else None,
887+
str(_type) if _type else None,
888+
str(input_type) if input_type else None
889+
])
890+
891+
# Make a query valid by adding missing selection sets.
892+
if type(node).__name__ == "Field" and not node.selection_set and is_composite_type(get_named_type(_type)):
893+
return Field(
894+
alias=node.alias,
895+
name=node.name,
896+
arguments=node.arguments,
897+
directives=node.directives,
898+
selection_set=SelectionSet(
899+
[Field(name=Name(value='__typename'))]
900+
)
901+
)
902+
903+
def leave(self, node, key, parent, *args):
904+
parent_type = type_info.get_parent_type()
905+
_type = type_info.get_type()
906+
input_type = type_info.get_input_type()
907+
visited.append([
908+
'leave',
909+
type(node).__name__,
910+
node.value if type(node).__name__ == "Name" else None,
911+
str(parent_type) if parent_type else None,
912+
str(_type) if _type else None,
913+
str(input_type) if input_type else None
914+
])
915+
916+
edited_ast = visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
917+
918+
# assert print_ast(ast) == print_ast(parse(
919+
# '{ human(id: 4) { name, pets }, alien }'
920+
# ))
921+
assert print_ast(edited_ast) == print_ast(parse(
922+
'{ human(id: 4) { name, pets { __typename } }, alien { __typename } }'
923+
))
924+
assert visited == [
925+
['enter', 'Document', None, None, None, None],
926+
['enter', 'OperationDefinition', None, None, 'QueryRoot', None],
927+
['enter', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None],
928+
['enter', 'Field', None, 'QueryRoot', 'Human', None],
929+
['enter', 'Name', 'human', 'QueryRoot', 'Human', None],
930+
['leave', 'Name', 'human', 'QueryRoot', 'Human', None],
931+
['enter', 'Argument', None, 'QueryRoot', 'Human', 'ID'],
932+
['enter', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
933+
['leave', 'Name', 'id', 'QueryRoot', 'Human', 'ID'],
934+
['enter', 'IntValue', None, 'QueryRoot', 'Human', 'ID'],
935+
['leave', 'IntValue', None, 'QueryRoot', 'Human', 'ID'],
936+
['leave', 'Argument', None, 'QueryRoot', 'Human', 'ID'],
937+
['enter', 'SelectionSet', None, 'Human', 'Human', None],
938+
['enter', 'Field', None, 'Human', 'String', None],
939+
['enter', 'Name', 'name', 'Human', 'String', None],
940+
['leave', 'Name', 'name', 'Human', 'String', None],
941+
['leave', 'Field', None, 'Human', 'String', None],
942+
['enter', 'Field', None, 'Human', '[Pet]', None],
943+
['enter', 'Name', 'pets', 'Human', '[Pet]', None],
944+
['leave', 'Name', 'pets', 'Human', '[Pet]', None],
945+
['enter', 'SelectionSet', None, 'Pet', '[Pet]', None],
946+
['enter', 'Field', None, 'Pet', 'String!', None],
947+
['enter', 'Name', '__typename', 'Pet', 'String!', None],
948+
['leave', 'Name', '__typename', 'Pet', 'String!', None],
949+
['leave', 'Field', None, 'Pet', 'String!', None],
950+
['leave', 'SelectionSet', None, 'Pet', '[Pet]', None],
951+
['leave', 'Field', None, 'Human', '[Pet]', None],
952+
['leave', 'SelectionSet', None, 'Human', 'Human', None],
953+
['leave', 'Field', None, 'QueryRoot', 'Human', None],
954+
['enter', 'Field', None, 'QueryRoot', 'Alien', None],
955+
['enter', 'Name', 'alien', 'QueryRoot', 'Alien', None],
956+
['leave', 'Name', 'alien', 'QueryRoot', 'Alien', None],
957+
['enter', 'SelectionSet', None, 'Alien', 'Alien', None],
958+
['enter', 'Field', None, 'Alien', 'String!', None],
959+
['enter', 'Name', '__typename', 'Alien', 'String!', None],
960+
['leave', 'Name', '__typename', 'Alien', 'String!', None],
961+
['leave', 'Field', None, 'Alien', 'String!', None],
962+
['leave', 'SelectionSet', None, 'Alien', 'Alien', None],
963+
['leave', 'Field', None, 'QueryRoot', 'Alien', None],
964+
['leave', 'SelectionSet', None, 'QueryRoot', 'QueryRoot', None],
965+
['leave', 'OperationDefinition', None, None, 'QueryRoot', None],
966+
['leave', 'Document', None, None, None, None]
967+
]

tests/core_validation/__init__.py

Whitespace-only changes.

tests/core_validation/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
}),
164164
'dog': GraphQLField(Dog),
165165
'pet': GraphQLField(Pet),
166+
'alien': GraphQLField(Alien),
166167
'catOrDog': GraphQLField(CatOrDog),
167168
'humanOrAlien': GraphQLField(HumanOrAlien),
168169
'complicatedArgs': GraphQLField(ComplicatedArgs),

0 commit comments

Comments
 (0)