graphql 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +31 -41
  3. data/lib/graphql/argument.rb +23 -21
  4. data/lib/graphql/base_type.rb +5 -8
  5. data/lib/graphql/define/assign_argument.rb +5 -2
  6. data/lib/graphql/define/type_definer.rb +2 -1
  7. data/lib/graphql/directive.rb +34 -36
  8. data/lib/graphql/directive/include_directive.rb +3 -7
  9. data/lib/graphql/directive/skip_directive.rb +3 -7
  10. data/lib/graphql/enum_type.rb +78 -76
  11. data/lib/graphql/execution_error.rb +1 -3
  12. data/lib/graphql/field.rb +99 -95
  13. data/lib/graphql/input_object_type.rb +49 -47
  14. data/lib/graphql/interface_type.rb +31 -34
  15. data/lib/graphql/introspection.rb +19 -18
  16. data/lib/graphql/introspection/directive_location_enum.rb +8 -0
  17. data/lib/graphql/introspection/directive_type.rb +1 -3
  18. data/lib/graphql/introspection/field_type.rb +1 -1
  19. data/lib/graphql/introspection/fields_field.rb +1 -1
  20. data/lib/graphql/introspection/introspection_query.rb +1 -3
  21. data/lib/graphql/introspection/possible_types_field.rb +7 -1
  22. data/lib/graphql/introspection/schema_field.rb +13 -9
  23. data/lib/graphql/introspection/type_by_name_field.rb +13 -17
  24. data/lib/graphql/introspection/typename_field.rb +12 -8
  25. data/lib/graphql/language.rb +5 -9
  26. data/lib/graphql/language/lexer.rb +668 -0
  27. data/lib/graphql/language/lexer.rl +149 -0
  28. data/lib/graphql/language/parser.rb +842 -116
  29. data/lib/graphql/language/parser.y +264 -0
  30. data/lib/graphql/language/token.rb +21 -0
  31. data/lib/graphql/list_type.rb +33 -31
  32. data/lib/graphql/non_null_type.rb +33 -31
  33. data/lib/graphql/object_type.rb +52 -55
  34. data/lib/graphql/query.rb +83 -80
  35. data/lib/graphql/query/context.rb +5 -1
  36. data/lib/graphql/query/directive_resolution.rb +16 -0
  37. data/lib/graphql/query/executor.rb +3 -3
  38. data/lib/graphql/query/input_validation_result.rb +17 -15
  39. data/lib/graphql/query/serial_execution.rb +5 -5
  40. data/lib/graphql/query/serial_execution/execution_context.rb +4 -3
  41. data/lib/graphql/query/serial_execution/selection_resolution.rb +19 -21
  42. data/lib/graphql/query/serial_execution/value_resolution.rb +1 -1
  43. data/lib/graphql/query/type_resolver.rb +22 -18
  44. data/lib/graphql/query/variable_validation_error.rb +14 -12
  45. data/lib/graphql/schema.rb +87 -77
  46. data/lib/graphql/schema/each_item_validator.rb +16 -12
  47. data/lib/graphql/schema/field_validator.rb +14 -10
  48. data/lib/graphql/schema/implementation_validator.rb +26 -22
  49. data/lib/graphql/schema/middleware_chain.rb +2 -1
  50. data/lib/graphql/schema/possible_types.rb +34 -0
  51. data/lib/graphql/schema/printer.rb +122 -120
  52. data/lib/graphql/schema/type_expression.rb +1 -0
  53. data/lib/graphql/schema/type_map.rb +3 -10
  54. data/lib/graphql/schema/type_reducer.rb +65 -81
  55. data/lib/graphql/schema/type_validator.rb +45 -41
  56. data/lib/graphql/static_validation.rb +7 -9
  57. data/lib/graphql/static_validation/all_rules.rb +29 -24
  58. data/lib/graphql/static_validation/arguments_validator.rb +39 -35
  59. data/lib/graphql/static_validation/literal_validator.rb +44 -40
  60. data/lib/graphql/static_validation/message.rb +30 -26
  61. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +15 -11
  62. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +14 -10
  63. data/lib/graphql/static_validation/rules/directives_are_defined.rb +16 -12
  64. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +59 -0
  65. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +25 -21
  66. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +28 -24
  67. data/lib/graphql/static_validation/rules/fields_will_merge.rb +84 -80
  68. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +49 -43
  69. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +22 -17
  70. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +19 -15
  71. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +25 -20
  72. data/lib/graphql/static_validation/rules/fragments_are_used.rb +36 -23
  73. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +29 -25
  74. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +21 -17
  75. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +79 -70
  76. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +24 -20
  77. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +122 -119
  78. data/lib/graphql/static_validation/type_stack.rb +138 -129
  79. data/lib/graphql/static_validation/validator.rb +29 -25
  80. data/lib/graphql/type_kinds.rb +42 -40
  81. data/lib/graphql/union_type.rb +22 -16
  82. data/lib/graphql/version.rb +1 -1
  83. data/readme.md +12 -27
  84. data/spec/graphql/base_type_spec.rb +3 -3
  85. data/spec/graphql/directive_spec.rb +10 -18
  86. data/spec/graphql/enum_type_spec.rb +7 -7
  87. data/spec/graphql/execution_error_spec.rb +1 -1
  88. data/spec/graphql/field_spec.rb +14 -13
  89. data/spec/graphql/id_type_spec.rb +6 -6
  90. data/spec/graphql/input_object_type_spec.rb +39 -39
  91. data/spec/graphql/interface_type_spec.rb +16 -32
  92. data/spec/graphql/introspection/directive_type_spec.rb +5 -9
  93. data/spec/graphql/introspection/input_value_type_spec.rb +10 -4
  94. data/spec/graphql/introspection/introspection_query_spec.rb +2 -2
  95. data/spec/graphql/introspection/schema_type_spec.rb +2 -2
  96. data/spec/graphql/introspection/type_type_spec.rb +34 -6
  97. data/spec/graphql/language/parser_spec.rb +299 -105
  98. data/spec/graphql/language/visitor_spec.rb +4 -4
  99. data/spec/graphql/list_type_spec.rb +11 -11
  100. data/spec/graphql/object_type_spec.rb +10 -10
  101. data/spec/graphql/query/arguments_spec.rb +7 -7
  102. data/spec/graphql/query/context_spec.rb +11 -3
  103. data/spec/graphql/query/executor_spec.rb +26 -19
  104. data/spec/graphql/query/serial_execution/execution_context_spec.rb +6 -6
  105. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
  106. data/spec/graphql/query/type_resolver_spec.rb +3 -3
  107. data/spec/graphql/query_spec.rb +6 -38
  108. data/spec/graphql/scalar_type_spec.rb +28 -19
  109. data/spec/graphql/schema/field_validator_spec.rb +1 -1
  110. data/spec/graphql/schema/middleware_chain_spec.rb +12 -1
  111. data/spec/graphql/schema/printer_spec.rb +12 -4
  112. data/spec/graphql/schema/rescue_middleware_spec.rb +1 -1
  113. data/spec/graphql/schema/type_expression_spec.rb +2 -2
  114. data/spec/graphql/schema/type_reducer_spec.rb +21 -36
  115. data/spec/graphql/schema/type_validator_spec.rb +9 -9
  116. data/spec/graphql/schema_spec.rb +1 -1
  117. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -4
  118. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
  119. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +5 -5
  120. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +39 -0
  121. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +5 -5
  122. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -4
  123. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  124. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +1 -1
  125. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
  126. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
  127. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +2 -2
  128. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +3 -3
  129. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
  130. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -5
  131. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +3 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -4
  133. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
  134. data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
  135. data/spec/graphql/static_validation/validator_spec.rb +26 -6
  136. data/spec/graphql/union_type_spec.rb +5 -4
  137. data/spec/spec_helper.rb +2 -5
  138. data/spec/support/dairy_app.rb +30 -9
  139. data/spec/support/dairy_data.rb +1 -1
  140. data/spec/support/star_wars_data.rb +26 -26
  141. data/spec/support/star_wars_schema.rb +1 -1
  142. metadata +40 -21
  143. data/lib/graphql/language/transform.rb +0 -113
  144. data/lib/graphql/query/directive_chain.rb +0 -44
  145. data/lib/graphql/repl.rb +0 -27
  146. data/spec/graphql/language/transform_spec.rb +0 -156
@@ -1,138 +1,332 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe GraphQL::Language::Parser do
4
- let(:parser) { GraphQL::PARSER }
5
-
6
- it 'parses documents' do
7
- assert(parser.parse_with_debug(%|
8
- # let's make a big query:
9
- # a read-only:
10
- query getStuff {id, name @skip(if: true)}
11
- # a mutation:
12
- mutation changeStuff(
13
- $override: Boolean = true,
14
- $cucumbers: [Vegetable]!,
15
- $input: SomeInputType = {key: "value"},
16
- ) @veggie, @healthy(vitamins: true) {
17
- # change the cucumber
18
- changeStuff(
19
- thing: $cucumbers
20
- ) {
21
- id,
22
- name,
23
- ... on Species { color },
24
- ... family # background info, of course
4
+ let(:document) { GraphQL::Language::Parser.parse(query_string) }
5
+ let(:query_string) {%|
6
+ query getStuff($someVar: Int = 1, $anotherVar: [String!] ) @skip(if: false) {
7
+ myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
8
+
9
+ anotherField(someArg: [1,2,3]) {
10
+ nestedField
11
+ ... moreNestedFields @skip(if: true)
25
12
  }
26
- }
27
- subscription watchStuff {
28
- field(emptyObj: {}), otherField
29
- }
30
13
 
31
- # a fragment:
32
- fragment family on Species {
33
- family {
34
- name, # name of the family
35
- members(first: 3, query: {isPlant: true}) # some of the other examples
14
+ ... on OtherType @include(unless: false){
15
+ field(arg: [{key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER}])
16
+ anotherField
17
+ }
18
+
19
+ ... {
20
+ id
36
21
  }
37
22
  }
38
23
 
39
- fragment nonsense on NonsenseType @skip(if: true) { bogus }
40
- |), 'gets a document with lots of comments')
24
+ fragment moreNestedFields on NestedType @or(something: "ok") {
25
+ anotherNestedField
26
+ }
27
+ |}
41
28
 
42
- assert(parser.parse_with_debug("{fields, only, inThisOne}"), 'fetch-only query')
43
- end
29
+ describe ".parse" do
30
+ let(:_query_string) { '
31
+ query getStuff($fragment: Int!, $false: String = "h\"😸i") @skip(ok: 1) {
32
+ myField(
33
+ arg1: 4.5,
34
+ arg2: -3,
35
+ arg3: "hello ☀︎ \uD83C\uDF40",
36
+ arg4: 4.5e-12,
37
+ arg5: true
38
+ arg6: $false
39
+ arg7: [true, false],
40
+ arg8: {key: "val", ok: true, whatever: $fragment}
41
+ arg9: ENUM_VALUE
42
+ ) {
43
+ aliasName: childField @skip(on: true)
44
+ ... description
45
+ },
46
+ # Comment!
47
+ #
48
+ otherField
49
+ }
44
50
 
51
+ fragment thingStuff on Thing {
52
+ whatever
53
+ }
54
+ '}
45
55
 
46
- it 'parses operation definitions' do
47
- assert(parser.operation_definition.parse_with_debug(%|{id, name, ...people}|), "just a selection")
48
- assert(parser.operation_definition.parse_with_debug(%|query personStuff {id, name, ...people, ... stuff}|), "named fetch")
49
- assert(parser.operation_definition.parse_with_debug(%|subscription personStuff @flagDirective {id, name, ...people}|), "with a directive")
50
- assert(parser.operation_definition.parse_with_debug(%|mutation changeStuff($stuff: Int = 1 $things: [SomeType]! = [{something: 1}, {something: 2}], $another: Sometype = {something: 3}) { id }|), "mutation with arguments")
51
- assert(parser.operation_definition.parse_with_debug(%|mutation { id }|), "unnamed")
52
- end
56
+ it "parses queries" do
57
+ assert document
58
+ end
53
59
 
54
- it 'parses fragment definitions' do
55
- assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food { fat, sodium, carbohydrates, vitamins { a, b } }|))
56
- assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food @directive(key: 1) { fat, sodium, carbohydrates, vitamins { a, b } }|), 'gets directives')
57
- end
60
+ describe "visited nodes" do
61
+ let(:query) { document.definitions.first }
62
+ let(:fragment_def) { document.definitions.last }
58
63
 
59
- it 'parses selections' do
60
- assert(parser.selections.parse_with_debug(%|{id, name, people { count }}|), 'gets nested fields')
61
- assert(parser.selections.parse_with_debug(%|{id, ... myFragment }|), 'gets fragment spreads')
62
- assert(parser.selections.parse_with_debug(%|{id, ... on User @myFlag { name, photo } }|), 'gets inline fragments')
63
- assert(parser.selections.parse_with_debug(%|{id @skip(if: true), ... myFragment @include(if: $something)}|), 'gets directives')
64
- end
64
+ it "creates a valid document" do
65
+ assert document.is_a?(GraphQL::Language::Nodes::Document)
66
+ assert_equal 2, document.definitions.length
67
+ end
65
68
 
66
- it 'parses directives' do
67
- assert(parser.directives.parse_with_debug("@doSomething"), 'gets without argument')
68
- assert(parser.directives.parse_with_debug('@doSomething(why: "\"forSomeReason\"")'), 'gets with argument')
69
- assert(parser.directives.parse_with_debug('@myFlag, @doSomething(why: "forSomeReason")'), 'gets multiple')
70
- end
69
+ it "creates a valid operation" do
70
+ assert query.is_a?(GraphQL::Language::Nodes::OperationDefinition)
71
+ assert_equal "getStuff", query.name
72
+ assert_equal "query", query.operation_type
73
+ assert_equal 2, query.variables.length
74
+ assert_equal 4, query.selections.length
75
+ assert_equal 1, query.directives.length
76
+ assert_equal [2, 5], [query.line, query.col]
77
+ end
71
78
 
72
- it 'parses fields' do
73
- assert(parser.field.parse_with_debug(%|myField { name, id }|), 'gets subselections')
74
- assert(parser.field.parse_with_debug(%{myAlias: myField}), 'gets an alias')
75
- assert(parser.field.parse_with_debug(%{myField(intKey: 1, floatKey: 1.1e5)}), 'gets arguments')
76
- assert(parser.field.parse_with_debug('myAlias: myField(stringKey: "\"my_string\"", emptyStringKey: "", boolKey: false, objKey: { key : true }, otherObjKey: {key: true})'), 'gets alias and arguments')
77
- assert(parser.field.parse_with_debug(%|myField @withFlag, @skip(if: true) { name, id }|), 'gets with directive')
78
- end
79
+ it "creates a valid fragment definition" do
80
+ assert fragment_def.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
81
+ assert_equal "moreNestedFields", fragment_def.name
82
+ assert_equal 1, fragment_def.selections.length
83
+ assert_equal "NestedType", fragment_def.type
84
+ assert_equal 1, fragment_def.directives.length
85
+ assert_equal [20, 5], fragment_def.position
86
+ end
87
+
88
+ describe "variable definitions" do
89
+ let(:optional_var) { query.variables.first }
90
+ it "gets name and type" do
91
+ assert_equal "someVar", optional_var.name
92
+ assert_equal "Int", optional_var.type.name
93
+ end
94
+
95
+ it "gets default value" do
96
+ assert_equal 1, optional_var.default_value
97
+ end
98
+
99
+ it "gets position info" do
100
+ assert_equal [2, 20], optional_var.position
101
+ end
102
+ end
103
+
104
+ describe "fields" do
105
+ let(:leaf_field) { query.selections.first }
106
+ let(:parent_field) { query.selections[1] }
107
+
108
+ it "gets name, alias, arguments and directives" do
109
+ assert_equal "someField", leaf_field.name
110
+ assert_equal "myField", leaf_field.alias
111
+ assert_equal 2, leaf_field.directives.length
112
+ assert_equal 2, leaf_field.arguments.length
113
+ end
114
+
115
+ it "gets nested fields" do
116
+ assert_equal 2, parent_field.selections.length
117
+ end
118
+
119
+ it "gets location info" do
120
+ assert_equal [3 ,7], leaf_field.position
121
+ end
122
+
123
+ describe "when the arguments list is empty" do
124
+ let(:query_string) { "{ field() }"}
125
+ let(:field) { query.selections.first }
126
+ it "has zero arguments" do
127
+ assert_equal 0, field.arguments.length
128
+ end
129
+ end
130
+
131
+ describe "when selections are empty" do
132
+ let(:query_string) { "{ field { } }"}
133
+ let(:field) { query.selections.first }
134
+ it "has zero selections" do
135
+ assert_equal 0, field.selections.length
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "arguments" do
141
+ let(:literal_argument) { query.selections.first.arguments.last }
142
+ let(:variable_argument) { query.selections.first.arguments.first }
143
+
144
+ it "gets name and literal value" do
145
+ assert_equal "ok", literal_argument.name
146
+ assert_equal 1.4, literal_argument.value
147
+ end
148
+
149
+ it "gets name and variable value" do
150
+ assert_equal "someArg", variable_argument.name
151
+ assert_equal "someVar", variable_argument.value.name
152
+ end
79
153
 
80
- it 'parses variable definitions' do
81
- assert(parser.operation_variable_definition.parse_with_debug("$myVar: Boolean = true"), "it gets variables with defaults")
82
- assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Int]"), "it gets list variables")
83
- assert(parser.operation_variable_definition.parse_with_debug("$myVar: Elephant!"), "it gets non-null variables")
84
- assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Food]!"), "it gets non-null list variables")
85
- assert(parser.operation_variable_definitions.parse_with_debug(%|($myVar: Elephant!, $myList: [Float], $myString: String = "Cheese")|), "it gets a list of defns")
86
- end
87
154
 
88
- describe 'value' do
89
- it 'gets ints' do
90
- assert(parser.value.parse_with_debug("100"), 'positive')
91
- assert(parser.value.parse_with_debug("-9"), 'negative')
92
- assert(parser.value.parse_with_debug("0"), 'zero')
155
+ it "gets position info" do
156
+ assert_equal [3, 26], variable_argument.position
157
+ end
158
+ end
159
+
160
+ describe "fragment spreads" do
161
+ let(:fragment_spread) { query.selections[1].selections.last }
162
+ it "gets the name and directives" do
163
+ assert_equal "moreNestedFields", fragment_spread.name
164
+ assert_equal 1, fragment_spread.directives.length
165
+ end
166
+
167
+ it "gets position info" do
168
+ assert_equal [7, 9], fragment_spread.position
169
+ end
170
+ end
171
+
172
+ describe "directives" do
173
+ let(:variable_directive) { query.selections.first.directives.first }
174
+
175
+ it "gets the name and arguments" do
176
+ assert_equal "skip", variable_directive.name
177
+ assert_equal "if", variable_directive.arguments.first.name
178
+ assert_equal 1, variable_directive.arguments.length
179
+ end
180
+
181
+ it "gets position info" do
182
+ assert_equal [3, 54], variable_directive.position
183
+ end
184
+ end
185
+
186
+ describe "inline fragments" do
187
+ let(:inline_fragment) { query.selections[2] }
188
+ let(:typeless_inline_fragment) { query.selections[3] }
189
+
190
+ it "gets the type and directives" do
191
+ assert_equal "OtherType", inline_fragment.type
192
+ assert_equal 2, inline_fragment.selections.length
193
+ assert_equal 1, inline_fragment.directives.length
194
+ end
195
+
196
+ it "gets inline fragments without type conditions" do
197
+ assert_equal nil, typeless_inline_fragment.type
198
+ assert_equal 1, typeless_inline_fragment.selections.length
199
+ assert_equal 0, typeless_inline_fragment.directives.length
200
+ end
201
+
202
+ it "gets position info" do
203
+ assert_equal [10, 7], inline_fragment.position
204
+ end
205
+ end
206
+
207
+ describe "inputs" do
208
+ let(:query_string) {%|
209
+ {
210
+ field(
211
+ int: 3,
212
+ float: 4.7e-24,
213
+ bool: false,
214
+ string: "☀︎🏆\\n escaped \\" unicode \\u00b6 /",
215
+ enum: ENUM_NAME,
216
+ array: [7, 8, 9]
217
+ object: {a: [1,2,3], b: {c: "4"}}
218
+ )
219
+ }
220
+ |}
221
+
222
+ let(:inputs) { document.definitions.first.selections.first.arguments }
223
+
224
+ it "parses ints" do
225
+ assert_equal 3, inputs[0].value
226
+ end
227
+
228
+ it "parses floats" do
229
+ assert_equal 0.47e-23, inputs[1].value
230
+ end
231
+
232
+ it "parses booleans" do
233
+ assert_equal false, inputs[2].value
234
+ end
235
+
236
+ it "parses UTF-8 strings" do
237
+ assert_equal %|☀︎🏆\n escaped " unicode ¶ /|, inputs[3].value
238
+ end
239
+
240
+ it "parses enums" do
241
+ assert_instance_of GraphQL::Language::Nodes::Enum, inputs[4].value
242
+ assert_equal "ENUM_NAME", inputs[4].value.name
243
+ end
244
+
245
+ it "parses arrays" do
246
+ assert_equal [7,8,9], inputs[5].value
247
+ end
248
+
249
+ it "parses objects" do
250
+ obj = inputs[6].value
251
+ assert_equal "a", obj.arguments[0].name
252
+ assert_equal [1,2,3], obj.arguments[0].value
253
+ assert_equal "b", obj.arguments[1].name
254
+ assert_equal "c", obj.arguments[1].value.arguments[0].name
255
+ assert_equal "4", obj.arguments[1].value.arguments[0].value
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "unnamed queries" do
261
+ let(:query_string) {%|
262
+ { name, age, height }
263
+ |}
264
+ let(:operation) { document.definitions.first }
265
+
266
+ it "parses unnamed queries" do
267
+ assert_equal 1, document.definitions.length
268
+ assert_equal "query", operation.operation_type
269
+ assert_equal nil, operation.name
270
+ assert_equal 3, operation.selections.length
271
+ end
93
272
  end
94
273
 
95
- it 'gets floats' do
96
- assert(parser.value.parse_with_debug("1.14"), 'no exponent')
97
- assert(parser.value.parse_with_debug("6.7e-9"), 'negative exponent')
98
- assert(parser.value.parse_with_debug("6.7e+9"), 'positive exponent')
99
- assert(parser.value.parse_with_debug("0.4e12"), 'exponent')
100
- assert(parser.value.parse_with_debug("6.7E+9"), 'big e')
274
+ describe "introspection query" do
275
+ let(:query_string) { GraphQL::Introspection::INTROSPECTION_QUERY }
276
+
277
+ it "parses a big ol' query" do
278
+ assert(document)
279
+ end
101
280
  end
281
+ end
102
282
 
103
- it 'gets booleans' do
104
- assert(parser.value.parse_with_debug("true"))
105
- assert(parser.value.parse_with_debug("false"))
283
+ describe "errors" do
284
+ let(:query_string) {%| query doSomething { bogus { } |}
285
+ it "raises a parse error" do
286
+ err = assert_raises(GraphQL::ParseError) { document }
106
287
  end
107
288
 
108
- it 'gets strings' do
109
- assert(parser.value.parse_with_debug('"my string"'), "plain strings")
110
- assert(parser.value.parse_with_debug('""'), "empty strings")
111
- assert(parser.value.parse_with_debug('"\"Hi!\"\n"'), "escaped strings")
112
- assert(parser.value.parse_with_debug('"\u0025\u0026"'), "escaped unicode")
289
+ it "correctly identifies parse error location and content" do
290
+ e = assert_raises(GraphQL::ParseError) do
291
+ GraphQL.parse("
292
+ query getCoupons {
293
+ allCoupons: {data{id}}
294
+ }
295
+ ")
296
+ end
297
+ assert_includes(e.message, '"{"')
298
+ assert_includes(e.message, "RCURLY")
299
+ assert_equal(3, e.line)
300
+ assert_equal(25, e.col)
113
301
  end
114
302
 
115
- it 'gets arrays' do
116
- assert(parser.value.parse_with_debug('[true, 1, "my string", -5.123e56]'), 'array of values')
117
- assert(parser.value.parse_with_debug('[ -5.123e56, $myVar ]'), 'array of values with whitespace')
118
- assert(parser.value.parse_with_debug('[]'), 'empty array')
119
- assert(parser.value.parse_with_debug('[[true, 1], ["my string", -5.123e56]]'), 'array of arrays')
303
+ it "handles unexpected ends" do
304
+ err = assert_raises { GraphQL.parse("{ ") }
305
+ assert_equal "Unexpected end of document", err.message
120
306
  end
307
+ end
308
+
121
309
 
122
- it 'gets variables' do
123
- assert(parser.value.parse_with_debug('$myVariable'), 'gets named variables')
310
+ describe "whitespace" do
311
+ describe "whitespace-only queries" do
312
+ let(:query_string) { " " }
313
+ it "doesn't blow up" do
314
+ assert_equal [], document.definitions
315
+ end
124
316
  end
125
317
 
126
- it 'gets objects' do
127
- assert(parser.value.parse_with_debug('{name: "tomato", calories: 50}'), 'gets scalar values')
128
- assert(parser.value.parse_with_debug('{listOfValues: [1, 2, [3]], nestedObject: {nestedKey: "nested{Value}"}}'), 'gets complex values')
129
- assert(parser.value.parse_with_debug('{variableKey: $variableValue}'), 'gets variables')
130
- assert(parser.value.parse_with_debug('{}'), 'gets empty')
131
- assert(parser.value.parse_with_debug('{ }'), 'gets empty')
318
+ describe "empty string queries" do
319
+ let(:query_string) { "" }
320
+ it "doesn't blow up" do
321
+ assert_equal [], document.definitions
322
+ end
132
323
  end
133
324
 
134
- it 'gets enums' do
135
- assert(parser.value.parse_with_debug("MY_ENUM"), 'gets enums')
325
+ describe "using tabs as whitespace" do
326
+ let(:query_string) { "\t{\t\tid, \tname}"}
327
+ it "parses the query" do
328
+ assert_equal 1, document.definitions.length
329
+ end
136
330
  end
137
331
  end
138
332
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe GraphQL::Language::Visitor do
4
4
  let(:document) { GraphQL.parse("
@@ -19,7 +19,7 @@ describe GraphQL::Language::Visitor do
19
19
  v
20
20
  end
21
21
 
22
- it 'calls hooks during a depth-first tree traversal' do
22
+ it "calls hooks during a depth-first tree traversal" do
23
23
  assert_equal(2, visitor[GraphQL::Language::Nodes::Argument].enter.length)
24
24
  visitor.visit(document)
25
25
  assert_equal(6, counts[:fields_entered])
@@ -29,8 +29,8 @@ describe GraphQL::Language::Visitor do
29
29
  assert(counts[:finished])
30
30
  end
31
31
 
32
- describe 'Visitor::SKIP' do
33
- it 'skips the rest of the node' do
32
+ describe "Visitor::SKIP" do
33
+ it "skips the rest of the node" do
34
34
  visitor[GraphQL::Language::Nodes::Document] << -> (node, parent) { GraphQL::Language::Visitor::SKIP }
35
35
  visitor.visit(document)
36
36
  assert_equal(0, counts[:fields_entered])