graphql 0.17.2 → 0.18.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -0
  3. data/lib/graphql/analysis/query_depth.rb +1 -1
  4. data/lib/graphql/base_type.rb +25 -1
  5. data/lib/graphql/define.rb +2 -0
  6. data/lib/graphql/define/assign_connection.rb +11 -0
  7. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  8. data/lib/graphql/define/assign_object_field.rb +21 -20
  9. data/lib/graphql/define/defined_object_proxy.rb +2 -2
  10. data/lib/graphql/define/instance_definable.rb +13 -3
  11. data/lib/graphql/field.rb +1 -1
  12. data/lib/graphql/language/generation.rb +57 -6
  13. data/lib/graphql/language/lexer.rb +434 -212
  14. data/lib/graphql/language/lexer.rl +18 -0
  15. data/lib/graphql/language/nodes.rb +75 -0
  16. data/lib/graphql/language/parser.rb +853 -341
  17. data/lib/graphql/language/parser.y +114 -17
  18. data/lib/graphql/query.rb +15 -1
  19. data/lib/graphql/relay.rb +13 -0
  20. data/lib/graphql/relay/array_connection.rb +80 -0
  21. data/lib/graphql/relay/base_connection.rb +138 -0
  22. data/lib/graphql/relay/connection_field.rb +54 -0
  23. data/lib/graphql/relay/connection_type.rb +25 -0
  24. data/lib/graphql/relay/edge.rb +22 -0
  25. data/lib/graphql/relay/edge_type.rb +14 -0
  26. data/lib/graphql/relay/global_id_resolve.rb +15 -0
  27. data/lib/graphql/relay/global_node_identification.rb +124 -0
  28. data/lib/graphql/relay/mutation.rb +146 -0
  29. data/lib/graphql/relay/page_info.rb +13 -0
  30. data/lib/graphql/relay/relation_connection.rb +98 -0
  31. data/lib/graphql/schema.rb +3 -0
  32. data/lib/graphql/schema/printer.rb +12 -2
  33. data/lib/graphql/static_validation/message.rb +9 -5
  34. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  35. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  37. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +7 -7
  38. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  39. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  40. data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -6
  41. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +17 -9
  42. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  43. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -1
  44. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  45. data/lib/graphql/static_validation/rules/fragments_are_used.rb +17 -6
  46. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  47. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -2
  48. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -5
  49. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  50. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +12 -11
  51. data/lib/graphql/static_validation/type_stack.rb +33 -2
  52. data/lib/graphql/static_validation/validation_context.rb +5 -0
  53. data/lib/graphql/version.rb +1 -1
  54. data/readme.md +16 -4
  55. data/spec/graphql/analysis/analyze_query_spec.rb +31 -2
  56. data/spec/graphql/analysis/query_complexity_spec.rb +24 -0
  57. data/spec/graphql/argument_spec.rb +1 -1
  58. data/spec/graphql/define/instance_definable_spec.rb +9 -0
  59. data/spec/graphql/field_spec.rb +1 -1
  60. data/spec/graphql/internal_representation/rewrite_spec.rb +3 -3
  61. data/spec/graphql/language/generation_spec.rb +25 -4
  62. data/spec/graphql/language/parser_spec.rb +116 -1
  63. data/spec/graphql/query_spec.rb +10 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +164 -0
  65. data/spec/graphql/relay/connection_type_spec.rb +37 -0
  66. data/spec/graphql/relay/global_node_identification_spec.rb +149 -0
  67. data/spec/graphql/relay/mutation_spec.rb +55 -0
  68. data/spec/graphql/relay/page_info_spec.rb +106 -0
  69. data/spec/graphql/relay/relation_connection_spec.rb +348 -0
  70. data/spec/graphql/schema/printer_spec.rb +8 -0
  71. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  72. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +12 -6
  73. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +8 -4
  74. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  75. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  76. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -2
  77. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -2
  78. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  79. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +5 -3
  80. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  81. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +5 -2
  82. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +10 -2
  83. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +6 -3
  84. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +8 -4
  85. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  86. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +6 -3
  87. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  88. data/spec/spec_helper.rb +7 -0
  89. data/spec/support/dairy_app.rb +11 -10
  90. data/spec/support/star_wars_data.rb +65 -58
  91. data/spec/support/star_wars_schema.rb +192 -54
  92. metadata +84 -2
@@ -0,0 +1,348 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Relay::RelationConnection do
4
+ def get_names(result)
5
+ ships = result["data"]["empire"]["bases"]["edges"]
6
+ names = ships.map { |e| e["node"]["name"] }
7
+ end
8
+
9
+ def get_page_info(result)
10
+ result["data"]["empire"]["bases"]["pageInfo"]
11
+ end
12
+
13
+ def get_first_cursor(result)
14
+ result["data"]["empire"]["bases"]["edges"].first["cursor"]
15
+ end
16
+
17
+ def get_last_cursor(result)
18
+ result["data"]["empire"]["bases"]["edges"].last["cursor"]
19
+ end
20
+
21
+ describe "results" do
22
+ let(:query_string) {%|
23
+ query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
24
+ empire {
25
+ bases(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) {
26
+ ... basesConnection
27
+ }
28
+ }
29
+ }
30
+
31
+ fragment basesConnection on BasesConnectionWithTotalCount {
32
+ totalCount,
33
+ edges {
34
+ cursor
35
+ node {
36
+ name
37
+ }
38
+ },
39
+ pageInfo {
40
+ hasNextPage
41
+ hasPreviousPage
42
+ startCursor
43
+ endCursor
44
+ }
45
+ }
46
+ |}
47
+
48
+ it 'limits the result' do
49
+ result = query(query_string, "first" => 2)
50
+ assert_equal(2, get_names(result).length)
51
+ assert_equal(true, get_page_info(result)["hasNextPage"])
52
+ assert_equal(false, get_page_info(result)["hasPreviousPage"])
53
+ assert_equal("MQ==", get_page_info(result)["startCursor"])
54
+ assert_equal("Mg==", get_page_info(result)["endCursor"])
55
+ assert_equal("MQ==", get_first_cursor(result))
56
+ assert_equal("Mg==", get_last_cursor(result))
57
+
58
+ result = query(query_string, "first" => 3)
59
+ assert_equal(3, get_names(result).length)
60
+ assert_equal(false, get_page_info(result)["hasNextPage"])
61
+ assert_equal(false, get_page_info(result)["hasPreviousPage"])
62
+ assert_equal("MQ==", get_page_info(result)["startCursor"])
63
+ assert_equal("Mw==", get_page_info(result)["endCursor"])
64
+ assert_equal("MQ==", get_first_cursor(result))
65
+ assert_equal("Mw==", get_last_cursor(result))
66
+ end
67
+
68
+ it 'provides custom fields on the connection type' do
69
+ result = query(query_string, "first" => 2)
70
+ assert_equal(
71
+ Base.where(faction_id: 2).count,
72
+ result["data"]["empire"]["bases"]["totalCount"]
73
+ )
74
+ end
75
+
76
+ it 'slices the result' do
77
+ result = query(query_string, "first" => 2)
78
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
79
+
80
+ # After the last result, find the next 2:
81
+ last_cursor = get_last_cursor(result)
82
+
83
+ result = query(query_string, "after" => last_cursor, "first" => 2)
84
+ assert_equal(["Headquarters"], get_names(result))
85
+
86
+ last_cursor = get_last_cursor(result)
87
+
88
+ result = query(query_string, "before" => last_cursor, "last" => 1)
89
+ assert_equal(["Shield Generator"], get_names(result))
90
+
91
+ result = query(query_string, "before" => last_cursor, "last" => 2)
92
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
93
+
94
+ result = query(query_string, "before" => last_cursor, "last" => 10)
95
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
96
+
97
+ end
98
+
99
+ it "applies custom arguments" do
100
+ result = query(query_string, "first" => 1, "nameIncludes" => "ea")
101
+ assert_equal(["Death Star"], get_names(result))
102
+
103
+ after = get_last_cursor(result)
104
+
105
+ result = query(query_string, "first" => 2, "nameIncludes" => "ea", "after" => after )
106
+ assert_equal(["Headquarters"], get_names(result))
107
+ before = get_last_cursor(result)
108
+
109
+ result = query(query_string, "last" => 1, "nameIncludes" => "ea", "before" => before)
110
+ assert_equal(["Death Star"], get_names(result))
111
+ end
112
+
113
+ it 'works without first/last/after/before' do
114
+ result = query(query_string)
115
+
116
+ assert_equal(3, result["data"]["empire"]["bases"]["edges"].length)
117
+ end
118
+
119
+ describe "applying max_page_size" do
120
+ let(:query_string) {%|
121
+ query getBases($first: Int, $after: String, $last: Int, $before: String){
122
+ empire {
123
+ bases: basesWithMaxLimitRelation(first: $first, after: $after, last: $last, before: $before) {
124
+ ... basesConnection
125
+ }
126
+ }
127
+ }
128
+
129
+ fragment basesConnection on BaseConnection {
130
+ edges {
131
+ cursor
132
+ node {
133
+ name
134
+ }
135
+ },
136
+ pageInfo {
137
+ hasNextPage
138
+ hasPreviousPage
139
+ startCursor
140
+ endCursor
141
+ }
142
+ }
143
+ |}
144
+
145
+ it "applies to queries by `first`" do
146
+ result = query(query_string, "first" => 100)
147
+ assert_equal(2, result["data"]["empire"]["bases"]["edges"].size)
148
+ assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"])
149
+
150
+ # Max page size is applied _without_ `first`, also
151
+ result = query(query_string)
152
+ assert_equal(2, result["data"]["empire"]["bases"]["edges"].size)
153
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"], "hasNextPage is false when first is not specified")
154
+ end
155
+
156
+ it "applies to queries by `last`" do
157
+ last_cursor = "Ng=="
158
+ second_to_last_two_names = ["Death Star", "Shield Generator"]
159
+ result = query(query_string, "last" => 100, "before" => last_cursor)
160
+ assert_equal(second_to_last_two_names, get_names(result))
161
+ assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
162
+
163
+ result = query(query_string, "before" => last_cursor)
164
+ assert_equal(second_to_last_two_names, get_names(result))
165
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
166
+
167
+ third_cursor = "Mw=="
168
+ first_and_second_names = ["Yavin", "Echo Base"]
169
+ result = query(query_string, "last" => 100, "before" => third_cursor)
170
+ assert_equal(first_and_second_names, get_names(result))
171
+
172
+ result = query(query_string, "before" => third_cursor)
173
+ assert_equal(first_and_second_names, get_names(result))
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "without a block" do
179
+ let(:query_string) {%|
180
+ {
181
+ empire {
182
+ basesClone(first: 10) {
183
+ edges {
184
+ node {
185
+ name
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }|}
191
+ it "uses default resolve" do
192
+ result = query(query_string)
193
+ bases = result["data"]["empire"]["basesClone"]["edges"]
194
+ assert_equal(3, bases.length)
195
+ end
196
+ end
197
+
198
+ describe "custom ordering" do
199
+ let(:query_string) {%|
200
+ query getBases {
201
+ empire {
202
+ basesByName(first: 30) { ... basesFields }
203
+ bases(first: 30) { ... basesFields2 }
204
+ }
205
+ }
206
+ fragment basesFields on BaseConnection {
207
+ edges {
208
+ node {
209
+ name
210
+ }
211
+ }
212
+ }
213
+ fragment basesFields2 on BasesConnectionWithTotalCount {
214
+ edges {
215
+ node {
216
+ name
217
+ }
218
+ }
219
+ }
220
+ |}
221
+
222
+ def get_names(result, field_name)
223
+ bases = result["data"]["empire"][field_name]["edges"]
224
+ base_names = bases.map { |b| b["node"]["name"] }
225
+ end
226
+
227
+ it "applies the default value" do
228
+ result = query(query_string)
229
+ bases_by_id = ["Death Star", "Shield Generator", "Headquarters"]
230
+ bases_by_name = ["Death Star", "Headquarters", "Shield Generator"]
231
+
232
+ assert_equal(bases_by_id, get_names(result, "bases"))
233
+ assert_equal(bases_by_name, get_names(result, "basesByName"))
234
+ end
235
+ end
236
+
237
+ describe "with a Sequel::Dataset" do
238
+ def get_names(result)
239
+ ships = result["data"]["empire"]["basesAsSequelDataset"]["edges"]
240
+ names = ships.map { |e| e["node"]["name"] }
241
+ end
242
+
243
+ def get_page_info(result)
244
+ result["data"]["empire"]["basesAsSequelDataset"]["pageInfo"]
245
+ end
246
+
247
+ def get_first_cursor(result)
248
+ result["data"]["empire"]["basesAsSequelDataset"]["edges"].first["cursor"]
249
+ end
250
+
251
+ def get_last_cursor(result)
252
+ result["data"]["empire"]["basesAsSequelDataset"]["edges"].last["cursor"]
253
+ end
254
+
255
+ describe "results" do
256
+ let(:query_string) {%|
257
+ query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
258
+ empire {
259
+ basesAsSequelDataset(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) {
260
+ ... basesConnection
261
+ }
262
+ }
263
+ }
264
+
265
+ fragment basesConnection on BasesConnectionWithTotalCount {
266
+ totalCount,
267
+ edges {
268
+ cursor
269
+ node {
270
+ name
271
+ }
272
+ },
273
+ pageInfo {
274
+ hasNextPage
275
+ hasPreviousPage
276
+ startCursor
277
+ endCursor
278
+ }
279
+ }
280
+ |}
281
+
282
+ it 'limits the result' do
283
+ result = query(query_string, "first" => 2)
284
+ assert_equal(2, get_names(result).length)
285
+ assert_equal(true, get_page_info(result)["hasNextPage"])
286
+ assert_equal(false, get_page_info(result)["hasPreviousPage"])
287
+ assert_equal("MQ==", get_page_info(result)["startCursor"])
288
+ assert_equal("Mg==", get_page_info(result)["endCursor"])
289
+ assert_equal("MQ==", get_first_cursor(result))
290
+ assert_equal("Mg==", get_last_cursor(result))
291
+
292
+ result = query(query_string, "first" => 3)
293
+ assert_equal(3, get_names(result).length)
294
+ assert_equal(false, get_page_info(result)["hasNextPage"])
295
+ assert_equal(false, get_page_info(result)["hasPreviousPage"])
296
+ assert_equal("MQ==", get_page_info(result)["startCursor"])
297
+ assert_equal("Mw==", get_page_info(result)["endCursor"])
298
+ assert_equal("MQ==", get_first_cursor(result))
299
+ assert_equal("Mw==", get_last_cursor(result))
300
+ end
301
+
302
+ it 'provides custom fields on the connection type' do
303
+ result = query(query_string, "first" => 2)
304
+ assert_equal(
305
+ Base.where(faction_id: 2).count,
306
+ result["data"]["empire"]["basesAsSequelDataset"]["totalCount"]
307
+ )
308
+ end
309
+
310
+ it 'slices the result' do
311
+ result = query(query_string, "first" => 2)
312
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
313
+
314
+ # After the last result, find the next 2:
315
+ last_cursor = get_last_cursor(result)
316
+
317
+ result = query(query_string, "after" => last_cursor, "first" => 2)
318
+ assert_equal(["Headquarters"], get_names(result))
319
+
320
+ last_cursor = get_last_cursor(result)
321
+
322
+ result = query(query_string, "before" => last_cursor, "last" => 1)
323
+ assert_equal(["Shield Generator"], get_names(result))
324
+
325
+ result = query(query_string, "before" => last_cursor, "last" => 2)
326
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
327
+
328
+ result = query(query_string, "before" => last_cursor, "last" => 10)
329
+ assert_equal(["Death Star", "Shield Generator"], get_names(result))
330
+
331
+ end
332
+
333
+ it "applies custom arguments" do
334
+ result = query(query_string, "first" => 1, "nameIncludes" => "ea")
335
+ assert_equal(["Death Star"], get_names(result))
336
+
337
+ after = get_last_cursor(result)
338
+
339
+ result = query(query_string, "first" => 2, "nameIncludes" => "ea", "after" => after )
340
+ assert_equal(["Headquarters"], get_names(result))
341
+ before = get_last_cursor(result)
342
+
343
+ result = query(query_string, "last" => 1, "nameIncludes" => "ea", "before" => before)
344
+ assert_equal(["Death Star"], get_names(result))
345
+ end
346
+ end
347
+ end
348
+ end
@@ -66,6 +66,10 @@ describe GraphQL::Schema::Printer do
66
66
  describe ".print_introspection_schema" do
67
67
  it "returns the schema as a string for the introspection types" do
68
68
  expected = <<SCHEMA
69
+ schema {
70
+ query: Query
71
+ }
72
+
69
73
  type __Directive {
70
74
  name: String!
71
75
  description: String
@@ -147,6 +151,10 @@ SCHEMA
147
151
  describe ".print_schema" do
148
152
  it "returns the schema as a string for the defined types" do
149
153
  expected = <<SCHEMA
154
+ schema {
155
+ query: Query
156
+ }
157
+
150
158
  enum Choice {
151
159
  FOO
152
160
  BAR
@@ -21,7 +21,7 @@ describe GraphQL::Schema::ReduceTypes do
21
21
  end
22
22
 
23
23
  it "finds type from arguments" do
24
- result = reduce_types([QueryType])
24
+ result = reduce_types([DairyAppQueryType])
25
25
  assert_equal(DairyProductInputType, result["DairyProductInput"])
26
26
  end
27
27
 
@@ -26,37 +26,43 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
26
26
 
27
27
  query_root_error = {
28
28
  "message"=>"Argument 'id' on Field 'cheese' has an invalid value",
29
- "locations"=>[{"line"=>3, "column"=>7}]
29
+ "locations"=>[{"line"=>3, "column"=>7}],
30
+ "path"=>["query getCheese", "cheese", "id"],
30
31
  }
31
32
  assert_includes(errors, query_root_error)
32
33
 
33
34
  directive_error = {
34
35
  "message"=>"Argument 'if' on Directive 'skip' has an invalid value",
35
- "locations"=>[{"line"=>4, "column"=>30}]
36
+ "locations"=>[{"line"=>4, "column"=>30}],
37
+ "path"=>["query getCheese", "cheese", "source", "if"],
36
38
  }
37
39
  assert_includes(errors, directive_error)
38
40
 
39
41
  input_object_error = {
40
42
  "message"=>"Argument 'product' on Field 'badSource' has an invalid value",
41
- "locations"=>[{"line"=>6, "column"=>7}]
43
+ "locations"=>[{"line"=>6, "column"=>7}],
44
+ "path"=>["query getCheese", "badSource", "product"],
42
45
  }
43
46
  assert_includes(errors, input_object_error)
44
47
 
45
48
  input_object_field_error = {
46
49
  "message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value",
47
- "locations"=>[{"line"=>6, "column"=>40}]
50
+ "locations"=>[{"line"=>6, "column"=>40}],
51
+ "path"=>["query getCheese", "badSource", "product", "source"],
48
52
  }
49
53
  assert_includes(errors, input_object_field_error)
50
54
 
51
55
  missing_required_field_error = {
52
56
  "message"=>"Argument 'product' on Field 'missingSource' has an invalid value",
53
- "locations"=>[{"line"=>7, "column"=>7}]
57
+ "locations"=>[{"line"=>7, "column"=>7}],
58
+ "path"=>["query getCheese", "missingSource", "product"],
54
59
  }
55
60
  assert_includes(errors, missing_required_field_error)
56
61
 
57
62
  fragment_error = {
58
63
  "message"=>"Argument 'source' on Field 'similarCheese' has an invalid value",
59
- "locations"=>[{"line"=>13, "column"=>7}]
64
+ "locations"=>[{"line"=>13, "column"=>7}],
65
+ "path"=>["fragment cheeseFields", "similarCheese", "source"],
60
66
  }
61
67
  assert_includes(errors, fragment_error)
62
68
  end
@@ -23,25 +23,29 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
23
23
 
24
24
  query_root_error = {
25
25
  "message"=>"Field 'cheese' doesn't accept argument 'silly'",
26
- "locations"=>[{"line"=>4, "column"=>7}]
26
+ "locations"=>[{"line"=>4, "column"=>7}],
27
+ "path"=>["query getCheese", "cheese", "silly"],
27
28
  }
28
29
  assert_includes(errors, query_root_error)
29
30
 
30
31
  input_obj_record = {
31
32
  "message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
32
- "locations"=>[{"line"=>5, "column"=>29}]
33
+ "locations"=>[{"line"=>5, "column"=>29}],
34
+ "path"=>["query getCheese", "searchDairy", "product", "wacky"],
33
35
  }
34
36
  assert_includes(errors, input_obj_record)
35
37
 
36
38
  fragment_error = {
37
39
  "message"=>"Field 'similarCheese' doesn't accept argument 'nonsense'",
38
- "locations"=>[{"line"=>9, "column"=>7}]
40
+ "locations"=>[{"line"=>9, "column"=>7}],
41
+ "path"=>["fragment cheeseFields", "similarCheese", "nonsense"],
39
42
  }
40
43
  assert_includes(errors, fragment_error)
41
44
 
42
45
  directive_error = {
43
46
  "message"=>"Directive 'skip' doesn't accept argument 'something'",
44
- "locations"=>[{"line"=>10, "column"=>10}]
47
+ "locations"=>[{"line"=>10, "column"=>10}],
48
+ "path"=>["fragment cheeseFields", "id", "something"],
45
49
  }
46
50
  assert_includes(errors, directive_error)
47
51
  end